Docker五種存儲驅動原理及應用場景和性能測試對比
一、原理說明
寫時復制(CoW)
所有驅動都用到的技術——寫時復制(CoW)。CoW就是copy-on-write,表示只在需要寫時才去復制,這個是針對已有文件的修改場景。比如基於一個image啟動多個Container,如果為每個Container都去分配一個image一樣的文件系統,那麽將會占用大量的磁盤空間。而CoW技術可以讓所有的容器共享image的文件系統,所有數據都從image中讀取,只有當要對文件進行寫操作時,才從image裏把要寫的文件復制到自己的文件系統進行修改。所以無論有多少個容器共享同一個image,所做的寫操作都是對從image中復制到自己的文件系統中的復本上進行,並不會修改image的源文件,且多個容器操作同一個文件,會在每個容器的文件系統裏生成一個復本,每個容器修改的都是自己的復本,相互隔離,相互不影響。使用CoW可以有效的提高磁盤的利用率。
用時分配(allocate-on-demand)
而寫時分配是用在原本沒有這個文件的場景,只有在要新寫入一個文件時才分配空間,這樣可以提高存儲資源的利用率。比如啟動一個容器,並不會為這個容器預分配一些磁盤空間,而是當有新文件寫入時,才按需分配新空間。
AUFS
AUFS(AnotherUnionFS)是一種Union FS,是文件級的存儲驅動。AUFS能透明覆蓋一或多個現有文件系統的層狀文件系統,把多層合並成文件系統的單層表示。簡單來說就是支持將不同目錄掛載到同一個虛擬文件系統下的文件系統。這種文件系統可以一層一層地疊加修改文件。無論底下有多少層都是只讀的,只有最上層的文件系統是可寫的。當需要修改一個文件時,AUFS創建該文件的一個副本,使用CoW將文件從只讀層復制到可寫層進行修改,結果也保存在可寫層。在Docker中,底下的只讀層就是image,可寫層就是Container。結構如下圖所示:
Overlay
Overlay是Linux內核3.18後支持的,也是一種Union FS,和AUFS的多層不同的是Overlay只有兩層:一個upper文件系統和一個lower文件系統,分別代表Docker的鏡像層和容器層。當需要修改一個文件時,使用CoW將文件從只讀的lower復制到可寫的upper進行修改,結果也保存在upper層。在Docker中,底下的只讀層就是image,可寫層就是Container。結構如下圖所示:
Device mapper
Device mapper是Linux內核2.6.9後支持的,提供的一種從邏輯設備到物理設備的映射框架機制,在該機制下,用戶可以很方便的根據自己的需要制定實現存儲資源的管理策略。前面講的AUFS和OverlayFS都是文件級存儲,而Device mapper是塊級存儲,所有的操作都是直接對塊進行操作,而不是文件。Device mapper驅動會先在塊設備上創建一個資源池,然後在資源池上創建一個帶有文件系統的基本設備,所有鏡像都是這個基本設備的快照,而容器則是鏡像的快照。所以在容器裏看到文件系統是資源池上基本設備的文件系統的快照,並不有為容器分配空間。當要寫入一個新文件時,在容器的鏡像內為其分配新的塊並寫入數據,這個叫用時分配。當要修改已有文件時,再使用CoW為容器快照分配塊空間,將要修改的數據復制到在容器快照中新的塊裏再進行修改。Device mapper 驅動默認會創建一個100G的文件包含鏡像和容器。每一個容器被限制在10G大小的卷內,可以自己配置調整。結構如下圖所示:
devicemapper是Red Hat Enterprise Linux下Docker Engine的默認存儲驅動,它有兩種配置模式:loop-lvm和direct-lvm,loop-lvm是默認的模式,但如果是在生產環境的部署Docker,官方不推薦使用該模式。direct-lvm是Docker推薦的生產環境的推薦模式,他使用塊設備來構建精簡池來存放鏡像和容器的數據。
devicemapper默認會在/var/lib/docker/devicemapper/devicemapper目錄下生成data和metadata兩個稀疏文件,並將兩個文件掛為loop設備作為塊設備來使用。
loop-lvm和direct-lvm的最大不同是創建DM thin pool的不再是通過losetup掛載的兩個稀疏文件,而是兩個裸的真正的塊設備。direct lvm的讀寫性能表現更加穩定。
Btrfs
Btrfs被稱為下一代寫時復制文件系統,並入Linux內核,也是文件級級存儲,但可以像Device mapper一直接操作底層設備。Btrfs把文件系統的一部分配置為一個完整的子文件系統,稱之為subvolume 。那麽采用 subvolume,一個大的文件系統可以被劃分為多個子文件系統,這些子文件系統共享底層的設備空間,在需要磁盤空間時便從底層設備中分配,類似應用程序調用 malloc()分配內存一樣。為了靈活利用設備空間,Btrfs 將磁盤空間劃分為多個chunk 。每個chunk可以使用不同的磁盤空間分配策略。比如某些chunk只存放metadata,某些chunk只存放數據。這種模型有很多優點,比如Btrfs支持動態添加設備。用戶在系統中增加新的磁盤之後,可以使用Btrfs的命令將該設備添加到文件系統中。Btrfs把一個大的文件系統當成一個資源池,配置成多個完整的子文件系統,還可以往資源池裏加新的子文件系統,而基礎鏡像則是子文件系統的快照,每個子鏡像和容器都有自己的快照,這些快照則都是subvolume的快照。
當寫入一個新文件時,為在容器的快照裏為其分配一個新的數據塊,文件寫在這個空間裏,這個叫用時分配。而當要修改已有文件時,使用CoW復制分配一個新的原始數據和快照,在這個新分配的空間變更數據,變結束再更新相關的數據結構指向新子文件系統和快照,原來的原始數據和快照沒有指針指向,被覆蓋。
ZFS
ZFS 文件系統是一個革命性的全新的文件系統,它從根本上改變了文件系統的管理方式,ZFS 完全拋棄了“卷管理”,不再創建虛擬的卷,而是把所有設備集中到一個存儲池中來進行管理,用“存儲池”的概念來管理物理存儲空間。過去,文件系統都是構建在物理設備之上的。為了管理這些物理設備,並為數據提供冗余,“卷管理”的概念提供了一個單設備的映像。而ZFS創建在虛擬的,被稱為“zpools”的存儲池之上。每個存儲池由若幹虛擬設備(virtual devices,vdevs)組成。這些虛擬設備可以是原始磁盤,也可能是一個RAID1鏡像設備,或是非標準RAID等級的多磁盤組。於是zpool上的文件系統可以使用這些虛擬設備的總存儲容量。
下面看一下在Docker裏ZFS的使用。首先從zpool裏分配一個ZFS文件系統給鏡像的基礎層,而其他鏡像層則是這個ZFS文件系統快照的克隆,快照是只讀的,而克隆是可寫的,當容器啟動時則在鏡像的最頂層生成一個可寫層。如下圖所示:
當要寫一個新文件時,使用按需分配,一個新的數據快從zpool裏生成,新的數據寫入這個塊,而這個新空間存於容器(ZFS的克隆)裏。
當要修改一個已存在的文件時,使用寫時復制,分配一個新空間並把原始數據復制到新空間完成修改。
可以通過docker info命令來查看當前daemon使用著哪種存儲驅動。
想要設置存儲驅動,可以在dockerd啟動的時候加入--storage-driver=,如dockerd --storage-driver=devicemapper &
二、存儲驅動的對比及適應場景
AUFS VS Overlay
AUFS和Overlay都是聯合文件系統,但AUFS有多層,而Overlay只有兩層,所以在做寫時復制操作時,如果文件比較大且存在比較低的層,則AUSF可能會慢一些。而且Overlay並入了linux kernel mainline,AUFS沒有,所以可能會比AUFS快。但Overlay還太年輕,要謹慎在生產使用。而AUFS做為docker的第一個存儲驅動,已經有很長的歷史,比較的穩定,且在大量的生產中實踐過,有較強的社區支持。目前開源的DC/OS指定使用Overlay。
Overlay VS Device mapper
Overlay是文件級存儲,Device mapper是塊級存儲,當文件特別大而修改的內容很小,Overlay不管修改的內容大小都會復制整個文件,對大文件進行修改顯示要比小文件要消耗更多的時間,而塊級無論是大文件還是小文件都只復制需要修改的塊,並不是整個文件,在這種場景下,顯然device mapper要快一些。因為塊級的是直接訪問邏輯盤,適合IO密集的場景。而對於程序內部復雜,大並發但少IO的場景,Overlay的性能相對要強一些。
Device mapper VS Btrfs Driver VS ZFS
Device mapper和Btrfs都是直接對塊操作,都不支持共享存儲,表示當有多個容器讀同一個文件時,需要生活多個復本,所以這種存儲驅動不適合在高密度容器的PaaS平臺上使用。而且在很多容器啟停的情況下可能會導致磁盤溢出,造成主機不能工作。Device mapper不建議在生產使用。Btrfs在docker build可以很高效。
ZFS最初是為擁有大量內存的Salaris服務器設計的,所在在使用時對內存會有影響,適合內存大的環境。ZFS的COW使碎片化問題更加嚴重,對於順序寫生成的大文件,如果以後隨機的對其中的一部分進行了更改,那麽這個文件在硬盤上的物理地址就變得不再連續,未來的順序讀會變得性能比較差。ZFS支持多個容器共享一個緩存塊,適合PaaS和高密度的用戶場景。
Overlay vs Overlay2
OverlayFS有兩種存儲驅動,它們使用了相同的OverlayFS技術,但卻有著不同的實現,在磁盤使用上也並不互相兼容。因為不兼容,兩者之間的切換必須重新創建所有的鏡像。overlay驅動是最原始的OverlayFS實現,並且,在Docker1.11之前是僅有的OverlayFS驅動選擇。overlay驅動在inode消耗方面有著較明顯的限制,並且會損耗一定的性能。overlay2驅動解決了這種限制,不過只能在Linux kernel 4.0以上使用它。
三、IO性能對比
測試工具:IOzone(是一個文件系統的benchmark工具,可以測試不同的操作系統中文件系統的讀寫性能)
測試場景:從4K到1G文件的順序和隨機IO性能
測試方法:基於不同的存儲驅動啟動容器,在容器內安裝IOzone,執行命令:
./iozone -a -n 4k -g 1g -i 0 -i 1 -i 2 -f /root/test.rar -Rb ./iozone.xls
測試項的定義和解釋
Write:測試向一個新文件寫入的性能。
Re-write:測試向一個已存在的文件寫入的性能。
Read:測試讀一個已存在的文件的性能。
Re-Read:測試讀一個最近讀過的文件的性能。
Random Read:測試讀一個文件中的隨機偏移量的性能。
Random Write:測試寫一個文件中的隨機偏移量的性能。
測試數據對比
Write:
Re-write:
Read:
Re-Read:
Random Read:
Random Write:
通過以上的性能數據可以看到:
AUFS在讀的方面性能相比Overlay要差一些,但在寫的方面性能比Overlay要好。
device mapper在512M以上文件的讀寫性能都非常的差,但在512M以下的文件讀寫性能都比較好。
btrfs在512M以上的文件讀寫性能都非常好,但在512M以下的文件讀寫性能相比其他的存儲驅動都比較差。
ZFS整體的讀寫性能相比其他的存儲驅動都要差一些。 簡單的測試了一些數據,對測試出來的數據原理還需要進一步的解析。
Docker五種存儲驅動原理及應用場景和性能測試對比