Docker最開始采用AUFS作為檔案系統,也得益于AUFS分層的概念,實作了多個Container可以共享同一個image。但由于AUFS未并入Linux核心,且隻支援Ubuntu,考慮到相容性問題,在Docker 0.7版本中引入了存儲驅動, 目前,Docker支援AUFS、Btrfs、Device mapper、OverlayFS、ZFS五種存儲驅動。就如Docker官網上說的,沒有單一的驅動适合所有的應用場景,要根據不同的場景選擇合适的存儲驅動,才能有效的提高Docker的性能。如何選擇适合的存儲驅動,要先了解存儲驅動原理才能更好的判斷,本文介紹一下Docker五種存儲驅動原理詳解及應用場景及IO性能測試的對比。在講原理前,先講一下寫時複制和寫時配置設定兩個技術。
一、原理說明
寫時複制(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整體的讀寫性能相比其他的存儲驅動都要差一些。 簡單的測試了一些資料,對測試出來的資料原理還需要進一步的解析。
本文轉自 dengaosky 51CTO部落格,原文連結:http://blog.51cto.com/dengaosky/2073824,如需轉載請自行聯系原作者