ZFS是下一代的文件系統,支持了很多存儲高級特性,如卷管理、快照、和校驗、壓縮和重複刪除技術、拷貝等。
ZFS由Sun公司創建,現屬於Oracle,ZFS是開源的,並基於CDDL license。因爲CDDL和GPL不兼容,ZFS不能加入Linux kernel主線。然而,ZFS On Linux(ZoL)項目提供kernel模塊和用戶空間程序,這些都可以單獨的安裝。
ZFS on Linux(ZoL)是一項成熟的技術,但是,現在卻不建議在產品中使用zfs存儲驅動,當然,除非你對ZoL有着豐富的經驗。
注意:在Linux平臺上,有ZFS的FUSE實現,它可以和Docker結合起來使用,但是卻不推薦這樣使用。原生的ZFS驅動(ZoL)被測試得更多,更有效率,也更廣泛地被使用。下面的內容也指的是原生的ZFS驅動。
鏡像分層和共享
Docker的zfs存儲驅動使用了ZFS驅動的三個組件:
●文件系統
●快照
●克隆
ZFS系統提供超配特性,通過按需分配操作從ZFS池(zpool)分配空間。快照和克隆是ZFS系統某個時間點的拷貝,較節省空間。快照是隻讀的,而克隆是讀寫的,克隆只能通過快照來創建。下圖顯示了它們之間這種簡單地關係。
圖中的實線顯示了創建了克隆的過程。第一步創建了一個文件系統的快照,第二步通過快照創建了一份克隆。虛線顯示了克隆、文件系統和快照之間的關係。這三個ZFS組件都從同一個下層的zpool申請空間。
在Docker host上使用zfs存儲驅動。鏡像的基礎層就是一個ZFS文件系統,每個子層都是底層ZFS快照的一個ZFS克隆,容器就是一個ZFS克隆,它基於其鏡像最頂層的ZFS快照。所有的ZFS組件都從同一個zpool分配空間。下圖顯示了一個基於兩層鏡像的容器是如何使用這些ZFS組件的。
結合上圖,下面的過程解釋了鏡像是如何分層的,容器是如何創建的。
▶在Docker host中,鏡像的基礎層是一個ZFS文件系統。
文件系統從zpool中消耗空間。
▶附加的鏡像層是其下層鏡像的克隆。
圖中,“Layer 1”是通過創建基礎鏡像的ZFS快照,然後再通過快照創建克隆來實現的。克隆是可讀寫的,並且從zpool按需分配空間;而快照是隻讀的。
▶當容器啓動時,就會在鏡像層上附加一個可讀寫層。
上圖中,製造了一個鏡像頂層的快照,然後再基於快照創建了一份克隆,而這份克隆就是容器的可讀寫層。
如果需要改變容器,那麼久會從zpool按需分配空間給容器,ZFS默認會分配128K大小的塊。
這種從只讀快照創建子層和容器的方法,使得鏡像作爲不可變對象來維護。
容器使用ZFS讀寫
容器從zfs存儲驅動讀數據的過程非常簡單。新啓動的容器基於ZFS克隆,這份克隆從初始化開始就共享它底層的所有數據。這意味着使用zfs的讀操作非常快,即使正在讀的數據並沒有拷貝到容器。下圖顯示了數據塊的共享情況。
寫新數據是通過按需分配操作來完成的。每次要向容器的一個新區域寫數據時,就會從zpool中分配一個新塊,也就是說寫新數據時容器就會消耗額外的空間。分配給容器的新空間從底層的zpool分配。
想更新容器的新數據,就得分配新空間給容器,並將這些改變的信息保存在新的塊中。原來的塊沒有改變,下面的鏡像依然是不可變的。
Docker中配置ZFS存儲驅動
只有在/var/lib/docker被映射在一個ZFS文件系統上時,才能在docker host上使用zfs存儲驅動。本節將介紹如何在Ubuntu上安裝原生的ZFS on Linux(ZoL)。
準備
如果你已經在Docker host上使用過Docker daemon,並且有一些想保存的鏡像,那麼在執行下面的步驟之前,你需要將這些鏡像保存到Docker Hub或者你的私有Docker鏡像倉庫中。
停止Docker daemon。另外,確保你在/dev/xvdb(這個設備名可能根據你的OS環境而不同,你需要換成適合你自己的設備名)上有一個空閒塊設備。
在Ubuntu16.04 LTS上安裝ZFS
1) 停止Docker daemon。
$ sudo systemctl stop docker.service
2) 安裝zfs包。
$ sudo apt-get install -y zfs
Reading package lists... Done
Building dependency tree
<output truncated>
3) 確認你的zfs模塊成功安裝並加載。
$ lsmod | grep zfs
zfs 2813952 3
zunicode 331776 1 zfs
zcommon 57344 1 zfs
znvpair 90112 2 zfs,zcommon
spl 102400 3 zfs,zcommon,znvpair
zavl 16384 1 zfs
在Ubuntu 14.04 LTS上安裝ZFS
1) 停止Docker daemon。
$ sudo systemctl stop docker.service
2) 安裝software-properties-common包。
$ sudo apt-get install -y software-properties-common
Reading package lists... Done
Building dependency tree
<output truncated>
3) 添加zfs-native包。
$ sudo add-apt-repository ppa:zfs-native/stable
The native ZFS filesystem for Linux. Install the ubuntu-zfs package.
<output truncated>
gpg: key F6B0FC61: public key "Launchpad PPA for Native ZFS for Linux" imported
gpg: Total number processed: 1
gpg: imported: 1 (RSA: 1)
OK
4) 獲取最新包的列表。
$ sudo apt-get update
Ign http://us-west-2.ec2.archive.ubuntu.com trusty InRelease
Get:1 http://us-west-2.ec2.archive.ubuntu.com trusty-updates InRelease [64.4 kB]
<output truncated>
Fetched 10.3 MB in 4s (2,370 kB/s)
Reading package lists... Done
5) 安裝ubuntu-zfs包。
$ sudo apt-get install -y ubuntu-zfs
Reading package lists... Done
Building dependency tree
<output truncated>
6) 加載zfs模塊。
$ sudo modprobe zfs
7) 確認模塊是否正確加載。
$ lsmod | grep zfs
zfs 2768247 0
zunicode 331170 1 zfs
zcommon 55411 1 zfs
znvpair 89086 2 zfs,zcommon
spl 96378 3 zfs,zcommon,znvpair
zavl 15236 1 zfs
在Docker中配置ZFS
一旦安裝和加載了ZFS,就可以在Docker中配置ZFS了。
1) 創建一個新的zpool。
$ sudo zpool create -f zpool-docker /dev/xvdb
上面命令創建了一個名爲“zpool-docker”的zpool。這個名稱是隨意的。
2) 檢查zpool是否存在。
$ sudo zfs list
NAME USED AVAIL REFER MOUNTPOINT
zpool-docker 55K 3.84G 19K /zpool-docker
3) 在/var/lib/docker上創建和映射一個新的ZFS文件系統。
$ sudo zfs create -o mountpoint=/var/lib/docker zpool-docker/docker
4) 檢查前面的步驟是否正常工作。
$ sudo zfs list -t all
NAME USED AVAIL REFER MOUNTPOINT
zpool-docker 93.5K 3.84G 19K /zpool-docker
zpool-docker/docker 19K 3.84G 19K /var/lib/docker
可以看到,在/var/lib/docker上映射了一個ZFS文件系統,daemon啓動後會自動加載zfs存儲驅動。
5) 啓動Docker daemon。
$ sudo service docker start
docker start/running, process 2315
6) 確認daemon正在使用zfs存儲驅動。
$ sudo docker info
Containers: 0
Images: 0
Storage Driver: zfs
Zpool: zpool-docker
Zpool Health: ONLINE
Parent Dataset: zpool-docker/docker
Space Used By Parent: 27648
Space Available: 4128139776
Parent Quota: no
Compression: off
Execution Driver: native-0.2
[...]
Docker中的ZFS性能
有以下一些因素會影響zfs存儲驅動在Docker中的性能。
●內存。內存對ZFS性能影響很大,因爲ZFS最初被設計成在擁有大量內存的Sun Solaris服務器上使用的。
●ZFS特性。使用ZFS特性,如重複刪除技術,會增加ZFS的內存使用。就內存使用和效率原因而言,建議關閉FS的重複刪除特性。
●ZFS緩存。ZFS將磁盤塊緩存在被稱作adaptive replacement cache(ARC)內存結構體中。ZFS的ARC單拷貝特性使得塊的單個緩存拷貝可以被多個文件系統的克隆共享,也就是說多個運行着的容器可以共享緩存塊的單個拷貝。這意味着對於PaaS或其他高密度用例來說,ZFS是一個不錯的選擇。
●存儲碎片。碎片是諸如ZFS這種copy-on-write文件系統的副產品。不過,ZFS寫128K的塊時,會分配slab(多個128K的塊)來進行CoW操作,從而嘗試減少存儲碎片。ZFS intent log(ZIL)和 coalescing of writes(delayed writes)也可以幫忙減少碎片。
●使用原生的Linux ZFS驅動。雖然Docker zfs存儲驅動支持ZFS FUSE實現,但在高性能場景下不建議使用ZFS FUSE。原生的ZFS Linux驅動有着更好的性能。
●使用SSD。爲了更好的性能,可以使用更快的存儲介質(如SSD)。不過,如果你的SSD存儲很有限,那麼建議你關閉SSD上的ZIL特性。
限制容器寫的存儲配額
如果你像實現對每個鏡像的存儲配額,那麼你可以使用–storage-opt選項。
--storage-opt size=256M
它會限制容器可以寫/修改的空間大小(需要ZFS使能特性available)。也就是說,如果你有一個256MB大小的鏡像,並且使用了前面的參數選項,那麼你對應的容器就有512MB的大小,其中有256MB是可用的。