使用 ZFS 儲存驅動程式

ZFS 是一種下一代檔案系統,支援許多高階儲存技術,例如卷管理、快照、校驗和、壓縮和重複資料刪除、複製等等。

它由 Sun Microsystems(現為 Oracle Corporation)建立,並在 CDDL 許可下開源。由於 CDDL 和 GPL 之間存在許可證不相容性,因此 ZFS 無法作為 mainline Linux 核心的一部分提供。但是,ZFS On Linux (ZoL) 專案提供了一個樹外核心模組和使用者空間工具,可以單獨安裝。

ZFS on Linux (ZoL) 埠健康且正在成熟。但是,目前不建議將 zfs Docker 儲存驅動程式用於生產環境,除非您擁有豐富的 ZFS on Linux 使用經驗。

注意:Linux 平臺上還存在 ZFS 的 FUSE 實現。不推薦這樣做。原生 ZFS 驅動程式 (ZoL) 經過更多測試,效能更高,並且使用更廣泛。本文件的其餘部分將參考原生 ZoL 埠。

先決條件

  • ZFS 需要一個或多個專用塊裝置,最好是固態硬碟 (SSD)。
  • /var/lib/docker/ 目錄必須掛載在 ZFS 格式化的檔案系統上。
  • 更改儲存驅動程式會使您已建立的所有容器在本地系統上無法訪問。使用 docker save 儲存容器,並將現有映象推送到 Docker Hub 或私有倉庫,這樣您就不必以後重新建立它們。

注意

無需使用 MountFlags=slave,因為 dockerdcontainerd 位於不同的掛載名稱空間中。

使用 zfs 儲存驅動程式配置 Docker

  1. 停止 Docker。

  2. /var/lib/docker/ 的內容複製到 /var/lib/docker.bk,並刪除 /var/lib/docker/ 的內容。

    $ sudo cp -au /var/lib/docker /var/lib/docker.bk
    
    $ sudo rm -rf /var/lib/docker/*
    
  3. 在您的專用塊裝置或裝置上建立一個新的 zpool,並將其掛載到 /var/lib/docker/ 中。請確保您指定了正確的裝置,因為這是一個破壞性操作。此示例將兩個裝置新增到池中。

    $ sudo zpool create -f zpool-docker -m /var/lib/docker /dev/xvdf /dev/xvdg
    

    該命令建立 zpool 並將其命名為 zpool-docker。該名稱僅用於顯示目的,您可以使用其他名稱。使用 zfs list 檢查池是否已成功建立並掛載。

    $ sudo zfs list
    
    NAME           USED  AVAIL  REFER  MOUNTPOINT
    zpool-docker    55K  96.4G    19K  /var/lib/docker
    
  4. 配置 Docker 以使用 zfs。編輯 /etc/docker/daemon.json 並將 storage-driver 設定為 zfs。如果該檔案之前為空,現在應該看起來像這樣

    {
      "storage-driver": "zfs"
    }

    儲存並關閉檔案。

  5. 啟動 Docker。使用 docker info 驗證儲存驅動程式是否為 zfs

    $ sudo docker info
      Containers: 0
       Running: 0
       Paused: 0
       Stopped: 0
      Images: 0
      Server Version: 17.03.1-ce
      Storage Driver: zfs
       Zpool: zpool-docker
       Zpool Health: ONLINE
       Parent Dataset: zpool-docker
       Space Used By Parent: 249856
       Space Available: 103498395648
       Parent Quota: no
       Compression: off
    <...>
    

管理 zfs

增加正在執行裝置的容量

要增加 zpool 的大小,您需要向 Docker 主機新增一個專用塊裝置,然後使用 zpool add 命令將其新增到 zpool

$ sudo zpool add zpool-docker /dev/xvdh

限制容器的可寫儲存配額

如果要實施基於每個映象/資料集的配額,可以將 size 儲存選項設定為限制單個容器可用於其可寫層的空間量。

編輯 /etc/docker/daemon.json 並新增以下內容

{
  "storage-driver": "zfs",
  "storage-opts": ["size=256M"]
}

請參閱 守護程式參考文件 中每個儲存驅動程式的所有儲存選項

儲存並關閉檔案,然後重新啟動 Docker。

zfs 儲存驅動程式的工作原理

ZFS 使用以下物件

  • 檔案系統:精簡配置,根據需要從 zpool 分配空間。
  • 快照:檔案系統的空間高效的只讀時間點副本。
  • 克隆:快照的可寫副本。用於儲存與上一層的差異。

建立克隆的過程

ZFS snapshots and clones
  1. 從檔案系統建立一個只讀快照。
  2. 從快照建立一個可寫克隆。它包含與父層的任何差異。

檔案系統、快照和克隆都從底層 zpool 分配空間。

磁碟上的映象和容器層

每個正在執行的容器的統一檔案系統都掛載在 /var/lib/docker/zfs/graph/ 中的掛載點上。請繼續閱讀以瞭解統一檔案系統的組成方式。

映象分層和共享

映象的基礎層是一個 ZFS 檔案系統。每個子層都是基於其下方層的 ZFS 快照的 ZFS 克隆。容器是從它建立的映象的頂層建立的 ZFS 快照的 ZFS 克隆。

下圖顯示瞭如何將基於兩層映象的正在執行的容器組合在一起。

ZFS pool for Docker container

啟動容器時,以下步驟按順序發生

  1. 映象的基礎層在 Docker 主機上作為 ZFS 檔案系統存在。

  2. 其他映象層是託管直接下方映象層的資料集的克隆。

    在圖中,“層 1”是透過對基礎層進行 ZFS 快照然後從該快照建立克隆而新增的。該克隆是可寫的,並根據需要從 zpool 佔用空間。該快照是隻讀的,將基礎層保持為不可變物件。

  3. 啟動容器時,會在映象之上新增一個可寫層。

    在圖中,容器的可寫層是透過對映象的頂層(層 1)進行快照然後從該快照建立克隆而建立的。

  4. 容器修改其可寫層的內容時,會為更改的塊分配空間。預設情況下,這些塊為 128k。

zfs 如何處理容器讀寫操作

讀取檔案

每個容器的可寫層都是一個 ZFS 克隆,它與建立它的資料集(其父層快照)共享所有資料。即使要讀取的資料來自較深的層,讀取操作也是快速的。此圖說明了塊共享的工作原理

ZFS block sharing

寫入檔案

寫入新檔案:根據需要從底層 zpool 分配空間,並將塊直接寫入容器的可寫層。

修改現有檔案:僅為更改的塊分配空間,並使用寫時複製 (CoW) 策略將這些塊寫入容器的可寫層。這可以最大程度地減小層的尺寸並提高寫入效能。

刪除檔案或目錄:

  • 刪除位於較低層的檔案或目錄時,ZFS 驅動程式會遮蔽容器的可寫層中檔案或目錄的存在,即使檔案或目錄仍然存在於較低的只讀層中。
  • 如果在容器的可寫層中建立然後刪除檔案或目錄,則塊將被 zpool 回收。

ZFS 和 Docker 效能

有幾個因素會影響使用 zfs 儲存驅動程式的 Docker 的效能。

  • 記憶體:記憶體對 ZFS 效能有很大影響。ZFS 最初是為具有大量記憶體的大型企業級伺服器設計的。

  • ZFS 功能:ZFS 包含一個重複資料刪除功能。使用此功能可以節省磁碟空間,但會使用大量記憶體。建議您為與 Docker 一起使用的 zpool 停用此功能,除非您使用 SAN、NAS 或其他硬體 RAID 技術。

  • ZFS 快取: ZFS 將磁碟塊快取在一個名為自適應替換快取 (ARC) 的記憶體結構中。ZFS 的 *單複製 ARC* 功能允許將快取塊的單一副本共享給同一資料集的多個克隆。使用此功能,多個執行的容器可以共享單個快取塊的副本。此功能使 ZFS 成為 PaaS 和其他高密度用例的理想選擇。

  • 碎片化: 碎片化是像 ZFS 這樣的寫時複製檔案系統的一個自然副產品。ZFS 透過使用 128k 的小塊大小來緩解這個問題。ZFS 意圖日誌 (ZIL) 和寫操作合併(延遲寫)也有助於減少碎片化。您可以使用 `zpool status` 命令監控碎片化。但是,除了重新格式化和恢復檔案系統之外,沒有其他方法可以對 ZFS 進行碎片整理。

  • 使用 Linux 的原生 ZFS 驅動: 不建議使用 ZFS FUSE 實現,因為它效能很差。

效能最佳實踐

  • 使用快速儲存: 固態硬碟 (SSD) 比旋轉磁碟提供更快的讀寫速度。

  • 對寫密集型工作負載使用卷: 卷為寫密集型工作負載提供了最佳和最可預測的效能。這是因為它們繞過了儲存驅動程式,不會產生由精簡配置和寫時複製引入的任何潛在開銷。卷還具有其他優點,例如允許您在容器之間共享資料,並且即使沒有執行的容器使用它們,它們也能持久化。