隨著Docker 容器技術(shù)的不斷發(fā)展和業(yè)內(nèi)對Docker 的使用不斷深入,大家已經(jīng)不再滿足于對Docker 的基本使用。 把玩Docker 多年的老司機(jī)應(yīng)該或多或少都會遇到過一些Docker 存儲方面的問題,比如因為宿主機(jī)文件系統(tǒng)和Docker存儲類型不兼容引起的問題,或者使用AUFS 出現(xiàn)文件復(fù)制慢等問題,存儲問題每次遇到都要心里一緊。
Docker 存儲類的問題在排查上往往比較復(fù)雜,有過Docker 存儲類問題排查經(jīng)驗的同學(xué)應(yīng)該都有體會。每當(dāng)遇到此類的問題筆都有一股想找Docker 公司理論的沖動,不是說好的 ” build once run anywhere” 嗎?
筆者自己所在的團(tuán)隊也曾數(shù)次遇到關(guān)于Docker 存儲的問題,每次團(tuán)隊內(nèi)的成員基本都是避之不及,生怕會由自己處理(怕掉坑里)。為此筆者專門理了一下Docker 存儲類的問題的復(fù)雜點,發(fā)現(xiàn)這里面很大一部分其實是因為問題處理人員對Docker 的存儲類型、存儲原理不了解造成的,為此我們專門整理了一份Docker 選型方面的避坑指南,供大家參考,希望可以幫助到大家。
Docker 存儲概述
我們知道,每個Docker 容器里面都有一個自己的文件系統(tǒng),Docker 在運(yùn)行容器時需要進(jìn)行文件系統(tǒng)的創(chuàng)建,這樣rootfs 才能進(jìn)行掛載。
Bootfs 也稱引導(dǎo)文件系統(tǒng),位于Docker 鏡像和Docker 容器的最底層。bootfs 中主要包含兩個部分:bootloader和kernel,bootloader 用于引導(dǎo)加載內(nèi)核,當(dāng)內(nèi)核加載到內(nèi)存中后bootfs 的使命就完成了,接下來bootfs會被卸載掉。rootfs 中包含了一些linux中標(biāo)準(zhǔn)的目錄和文件,包括:/dev/設(shè)備文件目錄、/proc/進(jìn)程信息目錄、/bin/二進(jìn)制文件目錄、/etc/配置文件目錄等。
眾所周知,Docker 存儲的核心是鏡像的分層機(jī)制,鏡像分層的一大好處是鏡像之間可以進(jìn)行繼承,也正是因為這個特性我們可以借助基鏡像定制化的構(gòu)建我們需要的應(yīng)用鏡像。
Docker官方在版本1.10 時引入一個新的可尋址的存儲模型,了解過Docker 存儲這方面的同學(xué)應(yīng)該知道在Docker1.10之前Docker 鏡像的管理是使用的隨機(jī)的UUID管理,Docker1.10之后已經(jīng)使用安全內(nèi)容hash取代了隨機(jī)UUID進(jìn)行鏡像的管理。同時,考慮到已經(jīng)有很多用戶在使用UUID的鏡像管理方式,Docker 官方專門針對這個改動提供了遷移工具,幫助大家將已經(jīng)存在的鏡像遷移到新的模型之上。
我們知道,一個有眾多版本的鏡像的所有鏡像占據(jù)的空間可能比單個版本的鏡像占用的空間多不了多少(比如一個nginx鏡像有500MB,根據(jù)配置的不同我們保存了10個版本,10個版本占用的空間一般和一個nginx鏡像占用的空間接近,遠(yuǎn)低于10個鏡像空間的總和)。多個版本占用的空間之所以沒有明顯增加是由于Docker 容器、鏡像可以共享底層的基礎(chǔ)文件系統(tǒng)層,雖然鏡像的個數(shù)很多,但是他們占據(jù)的共享鏡像層在磁盤中只存在一份,每個容器只需要在自己的最上層加上一個自己獨有的可讀可寫層就可以正常使用了,因為這種存儲機(jī)制的存在大大提高了Docker 鏡像存儲的效率。這種模式的主要機(jī)制就是分層模型將不同的目錄掛載到了同一個虛擬文件系統(tǒng)之上,通過下面這個圖可以很好的理解這種模式。
上文中我們提到docker 的鏡像是分層的,但我們沒有說這部分的功能具體是由哪一部分實現(xiàn)的。其實不僅分層鏡像,包括每個Docker 容器的可讀寫層都是由Docker 的存儲方式負(fù)責(zé)管理的。
接觸容器比較早的同學(xué)可能還記得,Docker 在最初的時候只能在支持AUFS的文件系統(tǒng)的操作系統(tǒng)上運(yùn)行,而當(dāng)時在眾多操作系統(tǒng)中Ubuntu 已經(jīng)率先提供了對AUFS的支持,因此最開始玩Docker 的同學(xué)使用的linux發(fā)行版都是Ubuntu系列的發(fā)行版。
但是由于AUFS 未能加入Linux 內(nèi)核,成為linux發(fā)行版的標(biāo)配,因此為了尋求兼容性和擴(kuò)展性,Docker 在內(nèi)部通過graphdriver 這樣一種機(jī)制以一種可以擴(kuò)展的方式實現(xiàn)了對不同文件的系統(tǒng)的兼容(實際使用中可能發(fā)現(xiàn)有些版本還是會存在一些兼容性的問題)。
Docker 目前支持的驅(qū)動類型包括:AUFS、Device mapper、Btrfs、OverlayFS和ZFS(較新),這些驅(qū)動存在一個共同點,那就是都使用了寫時復(fù)制技術(shù)。下面我們逐個看下這種存儲驅(qū)動的特性以及在使用中可能會遇到的問題。
AUFS 存儲驅(qū)動
1. Union File System
UnionFS(Union File System簡稱)是一種為Linux、FreeBSD 等操作系統(tǒng)而設(shè)計的,可以把其他文件系統(tǒng)聯(lián)合到一個掛載點的文件系統(tǒng)服務(wù)。
簡單說來聯(lián)合文件系統(tǒng)就是將不同的目錄掛載到同一個虛擬機(jī)文件系統(tǒng)下的文件系統(tǒng)。這種文件系統(tǒng)的一大特點就是可以一層層的疊加修改文件,除了最上面的一層之外無論底下有多少層都是只讀的,只有最上層的文件系統(tǒng)是可以寫的。
UnionFS 使用分支(branch)對不同文件系統(tǒng)的文件(包含目錄)進(jìn)行覆蓋,從而最終形成一個單一的文件系統(tǒng),也就是我們直接操縱的文件系統(tǒng)。UnionFS 使用的這些分支要么是只讀的,要么是讀寫的,分支的這種特性決定了當(dāng)我們對虛擬后的聯(lián)合文件系統(tǒng)中寫入數(shù)據(jù)的時候,數(shù)據(jù)被寫到了一個新的文件中。這樣看來我們似乎可以操作這個虛擬文件系統(tǒng)中的任何的文件,但其實我們并未改變原來的文件,導(dǎo)致這種情況出現(xiàn)的原因是UnionFS使用了寫時復(fù)制(copy-on-write)這樣一種資源管理技術(shù)。
寫時復(fù)制也稱隱式共享(此名稱可能較少見),寫時復(fù)制是一種可以對可修改資源實現(xiàn)高效復(fù)制的資源管理技術(shù)。它的核心的設(shè)計思想是,如果一個數(shù)據(jù)資源是重復(fù)的,沒有做過任何的修改,這個時候不需要創(chuàng)建新的資源,而是讓新舊實例都去共享這僅有一份的資源。當(dāng)需要對資源進(jìn)行數(shù)據(jù)的增加或者修改的時候才會進(jìn)行新資源的創(chuàng)建。從上面的寫時復(fù)制的原理中我們即可看出,通過這種資源共享的方式我們可以省掉對未修改資源的復(fù)制,從而為我們帶來效率的提升。
2. AUFS
AUFS 全稱為Advanced Multi-Layered Unification Filesystem,是一種聯(lián)合文件系統(tǒng),是一種文件級的存儲驅(qū)動,出于對可靠性和性能的考慮,AUFS 對UnionFS1.x 進(jìn)行了整個的重寫,并且在重寫過程中還引入了一些新的功能特性,比如對于可寫的分支添加了負(fù)載均衡的功能。不僅僅是AUFS借鑒UnionFS,UnionFS也會去借鑒AUFS,比如UnionFS 2.x中借鑒了AUFS的一些實現(xiàn)。
有些同學(xué)可能會問,存儲驅(qū)動類型這么多,為什么Docker 最開始選擇AUFS作為自己的存儲驅(qū)動? 筆者個人認(rèn)為是因為AUFS具備的以下幾個優(yōu)勢:快速啟動容器、高效利用存儲、高效利用內(nèi)存。
當(dāng)我們需要對一個文件進(jìn)行修改時,AUFS 會為目的文件創(chuàng)建一個文件副本,具體是從包含該文件的只讀層使用寫時復(fù)制技術(shù)將文件復(fù)制到最上面的可寫層。需要注意的是,當(dāng)我們修改完文件之后修改后的文件是保存在最上層的可寫層的,下面的只讀層的文件并未被修改。
3. Docker 對AUFS的使用
在Docker 體系中,我們熟知的最上層的讀寫層就是容器,除最上層之外的下面的那些層就是鏡像。
4. AUFS 避坑
了解AUFS 存儲驅(qū)動的優(yōu)勢和劣勢可以幫助我們更好的對存儲驅(qū)動進(jìn)行選型,同時也可在很大程度上幫助我們排查AUFS 存儲驅(qū)動方面相關(guān)的問題。
好,我們先看下AUFS 作為Docker 存儲驅(qū)動的優(yōu)勢,以便選擇更合適的使用場景:
(1) 得益于AUFS 高效的目錄掛載機(jī)制,Docker 可以很快的創(chuàng)建容器。AUFS 的讀寫效率很高,和直接在Docker 容器所在宿主機(jī)上的讀寫接近。和虛擬服務(wù)器相比,AUFS讀寫性能優(yōu)于KVM,包括順序讀寫和隨機(jī)讀寫兩個方面。除此磁盤的高效使用之外,Docker的AUFS存儲驅(qū)動也可以對內(nèi)存進(jìn)行非常高效的使用。
(2) 相比一些出現(xiàn)較晚的Docker 存儲驅(qū)動,Docker AUFS存儲驅(qū)動性能相對比較穩(wěn)定,并且AUFS目前已經(jīng)有很多的生產(chǎn)部署實踐,另外AUFS 在社區(qū)生態(tài)方面也得到大量的支持。
下面我們再看下AUFS 作為Docker 存儲驅(qū)動存在的坑:
(1) AUFS 是Docker 第一個支持的存儲驅(qū)動,但AUFS 目前仍未加入linux系統(tǒng)的內(nèi)核主線,比如RedHat系列(包括CentOS)目前還無法直接使用AUFS。
(2) AUFS 目前不支持重命名的系統(tǒng)調(diào)用,這個會導(dǎo)致我們在執(zhí)行拷貝和解除連接時失敗。
(3) 我們知道AUFS 文件系統(tǒng)中每個源目錄都會對應(yīng)一個分支文件的掛載過程其實就是對不同分支的一個聯(lián)合操作,當(dāng)出現(xiàn)相同內(nèi)容的不同分支時會進(jìn)行上下層的覆蓋。但是當(dāng)我們寫入大文件的時候,比如大的存儲日志信息的文件或者存儲數(shù)據(jù)庫數(shù)據(jù)庫的文件,動態(tài)掛載多個路徑的存在會使分支越來越多,進(jìn)而會導(dǎo)致文件查找的性能越來越差,尤其是當(dāng)文件存在于較低的層時,問題會更加嚴(yán)重。
小結(jié):AUFS 存儲驅(qū)動建議在大并發(fā)但少I/O的場景下進(jìn)行使用。
Device mapper
1. Device Mapper 原理
部署Docker 環(huán)境的時候一般都會要求Linux 內(nèi)核版本最低為3.10,似乎Docker 運(yùn)行需要的存儲驅(qū)動是從linux 內(nèi)核3.10 這個版本才進(jìn)行引入的,但實際早在linux 內(nèi)核版本2.6的時候DeviceMapper就已經(jīng)被引入。
DeviceMapper 作為內(nèi)核的一部分主要提供了邏輯卷管理和通用設(shè)備映射的功能,為了實現(xiàn)系統(tǒng)中的塊設(shè)備管理提供了一個高度模塊化的內(nèi)核的架構(gòu)。
在深入了解DeviceMapper 之前我們需要先了解下DeviceMapper 中三個比較重要的概念:Mapped Device、Mapping Table和Target Device。
Mapped Device 并不是一個實際的設(shè)備,并沒有實際的物理硬件和它一一對應(yīng),相反Mapped Device 是一個邏輯上的抽象設(shè)備,簡單說來,可以將其理解成為內(nèi)核向外提供的邏輯設(shè)備。Mapped Device 通過另一個對象Mapping Table 和Target Device 建立映射關(guān)系。
Mapping Table 中包含Mapped Device 邏輯的起始地址、地址范圍以及Target Device 代表的物理設(shè)備的地址偏移量和Target 的類型信息,需要注意的是地址和偏移量單位為扇區(qū),即512字節(jié)。
Target Device 簡單說來就是Mapped Device 這個邏輯設(shè)備所映射到的一個物理設(shè)備。
Device Mapper 是塊級別的存儲驅(qū)動,所有的操作都是對存儲塊進(jìn)行直接的操作,這方面和上文中講的AUFS不同(AUFS 是文件級的存儲,所有的操作都是對文件進(jìn)行直接的操作)。
在使用Device Mapper 時,Device Mapper 會先在塊設(shè)備上創(chuàng)建一個資源池,然后接下來會在新建的資源池上添加一個沒有文件系統(tǒng)的基本設(shè)備,我們所有的容器鏡像都是這個設(shè)備的快照,我們的Docker容器是鏡像的快照。因此我們在Docker 容器中看到的文件系統(tǒng)系統(tǒng)其實是Device Mapper 資源池上基本設(shè)備上的文件系統(tǒng)的快照,實際上并沒有為我們的Docker 容器進(jìn)行空間的分配。
理解Device Mapper 的工作機(jī)制,還需要明白用時分配。用時分配是指,當(dāng)我們需要寫入一個文件時,我們需要為新文件分配新的存儲塊并,分配好存儲塊后才能進(jìn)行數(shù)據(jù)的寫入。
當(dāng)我們需要去修改容器中已經(jīng)存在的文件時,這個時候Device Mapper 借助寫時復(fù)制技術(shù)為容器分配存儲塊空間,然后Device Mapper 將需要修改的數(shù)據(jù)拷貝到容器快照中前面新建的塊里面,之后才會對文件中的數(shù)據(jù)進(jìn)行修改。
值得一提的是,Device Mapper 存儲驅(qū)動默認(rèn)會創(chuàng)建一個100GB 文件,包含Docker 容器和Docker 鏡像,需要注意的默認(rèn)情況下每個容器的大小都被限制在10GB的卷以內(nèi),但可以進(jìn)行自定義的設(shè)置,具體結(jié)構(gòu)如下圖:
另外Docker 存儲驅(qū)動的信息可以通過命令docker info 或者dmsetup ls 進(jìn)行查看:
[root@localhost ~]# docker info
Containers: 11
Running: 0
Paused: 0
Stopped: 11
Images: 6
Server Version: 1.10.3
Storage Driver: devicemapper
Pool Name: docker-8:3-404820673-pool
Pool Blocksize: 65.54 kB
Base Device Size: 10.74 GB
Backing Filesystem: xfs
Data file: /dev/loop0
Metadata file: /dev/loop1
Data Space Used: 12.51 GB
Data Space Total: 107.4 GB
Data Space Available: 82.06 GB
Metadata Space Used: 10.18 MB
Metadata Space Total: 2.147 GB
Metadata Space Available: 2.137 GB
Udev Sync Supported: true
Deferred Removal Enabled: false
Deferred Deletion Enabled: false
Deferred Deleted Device Count: 0
Data loop file: /var/lib/docker/devicemapper/devicemapper/data
WARNING: Usage of loopback devices is strongly discouraged for production use. Either use `--storage-opt dm.thinpooldev` or use `--storage-opt dm.no_warn_on_loop_devices=true` to suppress this warning.
Metadata loop file: /var/lib/docker/devicemapper/devicemapper/metadata
Library Version: 1.02.107-RHEL7 (2016-06-09)
Execution Driver: native-0.2
Logging Driver: journald
Plugins:
Volume: local
Network: null host bridge
Kernel Version: 3.10.0-327.36.1.el7.x86_64
Operating System: CentOS Linux 7 (Core)
OSType: linux
Architecture: x86_64
Number of Docker Hooks: 2
CPUs: 4
Total Memory: 1.781 GiB
Name: localhost.localdomain
ID: V7HM:XRBY:P6ZU:SGWK:J52L:VYOY:UK6L:TR45:YJRC:SZBS:DQRF:CFP5
WARNING: bridge-nf-call-iptables is disabled
WARNING: bridge-nf-call-ip6tables is disabled
Registries: docker.io (secure)
[root@localhost ~]# dmsetup ls
docker-8:3-404820673-pool(253:0)
2. Device Mapper 避坑
(1) Device Mapper 優(yōu)勢
Device Mapper 的寫時復(fù)制機(jī)制的最小操作單元為64KB,塊級無論是大文件還是小文件都只需要復(fù)制需要修改的塊,并不復(fù)制整個文件,相比aufs存儲驅(qū)動和overlay存儲驅(qū)動的整個文件復(fù)制Device Mapper 的效率更高。
相比上文中我們說過的AUFS存儲驅(qū)動,Device Mapper 在文件系統(tǒng)兼容性方面較好,另外DeviceMapper存儲驅(qū)動只需要一個文件進(jìn)行存儲,基本不會占用文件系統(tǒng)的inode。
(2) Device Mapper 避坑
在內(nèi)存使用方面,以Device Mapper 作為存儲驅(qū)動時,啟動多少個容器就需要復(fù)制多少份文件到內(nèi)存中,因為Device Mapper 不支持共享存儲,當(dāng)有多個容器需要讀同一個文件的內(nèi)容時,需要生成多個該文件的副本,在較多的容器進(jìn)行啟動、停止的時候可能會出現(xiàn)磁盤溢出的情況。因此如果是容器密度高的業(yè)務(wù)場景下盡量不要部署到單臺主機(jī)上。
每個容器每次在寫數(shù)據(jù)時都需要占用一個新的塊,由于每個存儲塊都需要借助存儲池進(jìn)行分配,且寫的文件比較稀疏,盡管Device Mapper 的I/O利用率很高,但由于額外增加了VFS 開銷導(dǎo)致性能不好。在選擇存儲類型時需要著重注意下這一點,強(qiáng)烈建議根據(jù)自己的業(yè)務(wù)場景進(jìn)行壓測后再進(jìn)行選擇。
在實際使用中,如果我們?yōu)槊總€容器都進(jìn)行塊設(shè)備的分配,當(dāng)啟動多個容器時數(shù)據(jù)會從磁盤被多次加載到內(nèi)存中,因此在這種場景下時內(nèi)存消耗較大,在選型時需要注意。
當(dāng)選用的宿主機(jī)為RHEL系列(包含CentOS)時,需要注意Device Mapper 是RHEL系列下的默認(rèn)的存儲驅(qū)動,配置方式分為兩種模式:loop-lvm和direct-lvm。其中l(wèi)oop-lvm是默認(rèn)的模式,loop-lvm 使用操作系統(tǒng)的層面的離散文件來構(gòu)建精簡池。一般情況下生產(chǎn)環(huán)境推薦使用direct-lvm 模式,不建議使用默認(rèn)的loop-lvm模式,經(jīng)實際測試loop-lvm 的性能很難達(dá)到生產(chǎn)環(huán)境的要求
小結(jié):Device Mapper 存儲驅(qū)動更適合在I/O密集的場景下進(jìn)行使用。
OverlayFS 文件系統(tǒng)
1. Overlay 原理
Overlay 存儲驅(qū)動的設(shè)計和AUFS 非常相似,也是一種聯(lián)合文件系統(tǒng),同時也是一種文件級別的存儲驅(qū)動,但和AUFS不同的是Overlay 只有兩層(一個是upperdir,另一個是lowerdir,從二者的名字也可看出,upperdir 代表的是我們的容器層,lowerdir層代表的是我們的鏡像層),而AUFS 有多層。相比AUFS Overlay 存儲驅(qū)動進(jìn)入linux較晚(Linux 內(nèi)核3.18之后才進(jìn)行支持)。
當(dāng)需要對文件進(jìn)行修改時,需要借助寫時復(fù)制從只讀的lowerdir 層復(fù)制到可讀寫的upper dir層,然后再在upperdir 層中對文件進(jìn)行修改,修改完成后的文件最終也是保存在組上面的upper層中。
2. Ovrelay 避坑
在說Overlay 使用之前我們先看下它的優(yōu)點,看下它適合的業(yè)務(wù)場景都有哪些。
(1) 從性能上來看,Overlay 存儲驅(qū)動要比Device Mapper 更好,速度更快。在某些情況下Overlay 的速度也比Btrfs存儲驅(qū)動快。由于Overlay 只有兩層,AUFS有多層,因此Overlay在速度上也比AUFS要快,減少操作延時。
(2) Overlay 支持頁緩存的共享,這樣在多個容器在訪問同一個文件時可以共享一個頁緩存,從而提高內(nèi)存使用效率。
下面我們再看下Overlay 在使用時需要注意的問題,注意避坑。
(1) Overlay 存儲驅(qū)動出現(xiàn)較晚,目前生產(chǎn)環(huán)境部署的經(jīng)驗相對較少,實際使用中需要解決的問題可能較多。
(2) 由于Overlay存儲驅(qū)動是文件級的,因此在對文件進(jìn)行第一次的操作時需要將目標(biāo)文件拷貝至最上面的讀寫層。
(3) 和其他的存儲驅(qū)動相比,Overlay 消耗inode 較多(我們有個容器云曾經(jīng)出現(xiàn)過此種情況),尤其是當(dāng)我們環(huán)境中鏡像和容器較多時可能會觸發(fā)這個問題。為了解決這個問題,一般的建議方式是將目錄/var/lib/docker直接掛到某個單獨的文件系統(tǒng)中,這樣在一定程度上可以減少其他的文件對inode的占用。
上面提到的解決方式只是最常用的一種,除了這種方式,我們還有其他的解決方式,如Overlay 版本更換為Overlay2,Overlay2中已經(jīng)解決了這個問題,在我們不再詳細(xì)描述。
(4) 除了上面說到的這些問題,Overlay 還存在POSIX的標(biāo)準(zhǔn)問題。Overlay的部分操作目前還不符合POSIX的標(biāo)準(zhǔn)。
(5) 最后需要注意的是Overlay 不支持rename系統(tǒng)調(diào)用,因此如果涉及到”copy”、”unlink ”的操作會出現(xiàn)失敗的情況。
小結(jié):Overlay 存儲適合大并發(fā)但少I/O的場景。
Btrfs 文件系統(tǒng)
1. Btrfs 原理
Btrfs 文件系統(tǒng)也稱B-tree 文件系統(tǒng)或者B-tree FS,Btrfs 是一種支持寫入復(fù)制的文件系統(tǒng),由著名公司Oracle 進(jìn)行研發(fā),第一個穩(wěn)定版本于2014年進(jìn)行發(fā)布。Btrfs 文件系統(tǒng)的目標(biāo)是取代linux中的ext3文件系統(tǒng)。
Btrfs 被很多人稱為下一代的寫時復(fù)制文件系統(tǒng),和Overlay 相似也是基于文件級別的存儲,目前Btrfs已經(jīng)并入linux內(nèi)核。
和前面我們講到的Device Mapper 類似,Btrfs 可以對底層的設(shè)備進(jìn)行直接的操作。
Btrfs 中有兩個重要的概念:subvolumes和snapshots,Btrfs 利用subvolumes和snapshots 管理Docker鏡像和Docker 容器的分層。
Subvolumes 是指Btrfs文件系統(tǒng)的一部分配置,或者也可稱為Btrfs文件系統(tǒng)的子文件系統(tǒng),借助subvlolumes我們可以將一個大的文件系統(tǒng)劃分成若干個子文件系統(tǒng),這些子文件系統(tǒng)共享底層的設(shè)備空間,在需要磁盤空間時便從底層設(shè)備中進(jìn)行獲取,整個過程類似malloc()分配內(nèi)存空間的過程。我們在使用Btrfs文件系統(tǒng)時,通常為了比較方便的使用設(shè)備的空間,Btrfs會將磁盤空間劃分成多個塊,需要注意的是劃分的這些塊的空間分配策略可以是不同的,比如,實際使用中我們可能會將一部分的塊用來存儲元數(shù)據(jù),將另一部分的塊用來存儲我們實際的業(yè)務(wù)數(shù)據(jù)。
Snapshots 是subvolumes 的一個實時讀寫拷貝,其分配單位稱為塊,一般每個塊的大小為1GB。
Btrfs 文件系統(tǒng)將一個文件系統(tǒng)當(dāng)成一個資源池來進(jìn)行看待,這個資源池中會有多個子文件系統(tǒng),我們可以動態(tài)的往資源池中進(jìn)行子文件系統(tǒng)的添加。
2. Btrfs避坑
先看下Btrfs 的優(yōu)勢和適合的業(yè)務(wù)場景。
相比我們經(jīng)常使用的ext4文件系統(tǒng),Btrfs 文件系統(tǒng)在很多方面都做了改進(jìn),比如大家熟知的支持子卷、支持快照,除了這些功能,Btrfs文件系統(tǒng)還內(nèi)置了壓縮和RAID的支持等功能。
相比傳統(tǒng)的文件系統(tǒng),雖然Btrfs 引入了很多非常棒的特性,但它存在的問題也同樣的突出。
(1) Btrfs 文件系統(tǒng)未引入頁緩存共享的機(jī)制,這就導(dǎo)致Btrfs 文件系統(tǒng)不適合在容器密度比較高的場景下進(jìn)行使用。因為沒有頁緩存共享,多個容器在訪問相同的文件時需要對文件緩存多次。
(2) Btrfs 文件系統(tǒng)中使用的small write 功能導(dǎo)致整個文件系統(tǒng)存在性能瓶頸。
(3) Btrfs 文件系統(tǒng)寫數(shù)據(jù)的方式是journaling,接觸過的同學(xué)應(yīng)該知道這種方式會在一定程度上影響順序?qū)懙男阅堋?/p>
(4) 除上面的問題外,Btrfs 還存在一個很嚴(yán)重的問題:碎片化。如果不對碎片進(jìn)行周期性的清理,碎片化會導(dǎo)致很嚴(yán)重的性能問題。幸運(yùn)的是,在新版本的Btrfs中已經(jīng)可以通過mount時指定autodefrag參數(shù)來解決這個問題了。
小結(jié):Btrfs 存儲不適合在容器密度高的平臺上進(jìn)行使用。
下一代文件系統(tǒng)ZFS
1. ZFS 原理
相比上文中我們說過的文件系統(tǒng),ZFS 在很多方面做了革命性的改變,它從根本上改變了文件系統(tǒng)的管理機(jī)制。
ZFS 文件系統(tǒng)中已經(jīng)不再使用卷管理,不再需要創(chuàng)建虛擬的卷,因此所有的設(shè)備都是直接放到統(tǒng)一的存儲池中進(jìn)行管理的,即用存儲池的概念對物理存儲空間進(jìn)行管理。
在容器環(huán)境中使用ZFS 文件系統(tǒng)時,會先從存儲池中為Docker鏡像的基礎(chǔ)層分配一個ZFS 文件系統(tǒng),Docker 鏡像的其他層來自ZFS文件系統(tǒng)快照的克隆,當(dāng)容器運(yùn)行起來的時候會在Docker 鏡像的最頂層生成一個可寫層。
2. ZFS 避坑
(1) 雖然ZFS 是下一代的文件系統(tǒng),但由于相對較新,目前一般不推薦在生產(chǎn)環(huán)境中的Docker 使用ZFS存儲,可現(xiàn)在測試環(huán)境采坑積累經(jīng)驗后再考慮上生產(chǎn)。
(2) ZFS 最初并不是為普通的服務(wù)器設(shè)計的,而是為Sun Solaris服務(wù)器量身打造的,從實際經(jīng)驗來看,如果需要部署在普通的服務(wù)器上,建議在正式使用前先壓測下內(nèi)存的使用。
(3) ZFS 有緩存的功能,但并不是適合每一種場景,一般只建議在高密度的場景下進(jìn)行使用。
小結(jié):ZFS 存儲適合PaaS服務(wù)密度較高的場景下使用。
存儲總結(jié)
每種Docker 存儲驅(qū)動都有自己的適用場景,沒有好壞之分,只有合適之分。不同的存儲驅(qū)動在不同的應(yīng)用場景下性能不同,例如,AUFS存儲驅(qū)動和Overlay存儲驅(qū)動工作在文件級別,因此在內(nèi)存使用上比較高效,但是在進(jìn)行大文件的讀寫時容器層所占據(jù)的空間會變的很大,這種場景下就更適合選用在高I/O負(fù)載下存在的優(yōu)勢的存儲驅(qū)動,如Device Mapper、Btrfs、ZFS,對于容器層數(shù)較多,且寫小文件頻繁的場景下選擇Device Mapper 比選擇Overlay 作為存儲驅(qū)動更加合適。
因此建議結(jié)合自己的業(yè)務(wù)場景去選擇需要的存儲驅(qū)動,另外在選擇存儲驅(qū)動時需要結(jié)合自己宿主機(jī)的環(huán)境,注意避坑。
最后,為方便大家查詢不同操作系統(tǒng)發(fā)行版對應(yīng)的推薦存儲驅(qū)動,筆者將Docker 官方給出的一個不同Linux 發(fā)行版的推薦表和不同存儲驅(qū)動對應(yīng)所支持的文件系統(tǒng)表貼一下:
不同Linux 發(fā)行版推薦表: