Docker儲存驅動之AUFS簡介
簡介
AUFS曾是Docker預設的首選儲存驅動。它非常穩定、有很多真實場景的部署、很強的社群支援。它有以下主要優點:
極短的容器啟動時間。
有效的儲存利用率。
有效的記憶體利用率。
雖然如此,但由於它沒有包含在Linux核心主線中,所有很多Linux發行版並不支援AUFS。
以下章節介紹AUFS的特性,並且它們如何作用於Docker。
特性
映象分層和部署
AUFS是一種聯合檔案系統。它使用同一個Linux host上的多個目錄,逐個堆疊起來,對外呈現出一個統一的檔案系統。AUFS使用該特性,實現了Docker映象的分層。下圖展示出ubuntu:latest的映象的分層。
注意:在Docker1.10之前,layer的ID對應著其在/var/lib/docker下的目錄名稱,但在Docker1.10之後,不再有這種直接的對應關係。
對於一個容器來說,只有頂層的容器layer是可讀寫的,而下面的layer都是隻讀的。
讀寫檔案
Docker使用AUFS的CoW(Copy-on-Write)技術來實現映象共享和最小化磁碟空間的使用。AUFS作用於檔案層,也就是說AUFS CoW拷貝整個檔案——即使檔案只修改了一點點的內容。所以,它對容器的效能影響很明顯,尤其拷貝多層映象下的大檔案,或者是在一個深層次的目錄樹中進行搜尋。
不過,在給定的容器中,這種拷貝到頂層layer的操作,每個檔案只會做一次。隨後,對該檔案的讀寫操作,都只針對容器頂層可讀寫layer的拷貝檔案。
刪除檔案
通過上面的介紹,很容易想到。如果要在容器中刪除一個非頂層layer的檔案,肯定不會在下層layer中直接刪除,因為下層layer對於容器來說都是隻讀的。AUFS儲存驅動要刪除一個檔案,是通過在容器頂層layer增加一個whiteout檔案來實現的。這個whiteout檔案可以隱藏下層只讀layer中檔案的存在,容器感知不到只讀層layer中文的存在。事實上,無論該檔案在下層只讀layer中是否還存在,容器都認為這個檔案被刪除了。下圖展示了whiteout檔案如何工作的:
重新命名目錄
AUFS未能完美的支援rename(2)重新命名操作,會返回EXDEV[“cross-device link not permitted”],即使源路徑和目的路徑都在同一個AUFS層。因此,你的應用需要能處理EXDEV,可以用“拷貝再刪除”的策略來替代rename操作。
我在這裡做了一個測試,寫了一個簡單地C程式,該程式將目錄test重新命名為目錄gaga,並打印出rename的結果。該程式在普通伺服器上完美執行,那麼在容器中呢?開始做測試吧。
建立docker build目錄,進入該目錄。並在該目錄下建立子目錄test。
$ mkdir build-rename
$ cd build-rename
$ mkdir test
建立檔案test.c
$ vim test.c
#include<stdio.h>
#include <fcntl.h>
int main(void)
{
char oldname[100] = "test", newname[100]="gaga";
int ret = rename(oldname, newname);
if (ret == 0)
printf("rename ok.\n");
else
printf("ret = %d\n", ret);
return 0;
}
編譯該程式,生成可執行檔案a.out。
$ gcc test.c
建立Dockerfile
$ vim Dockerfile
FROM ubuntu
WORKDIR /usr/src/app
COPY ./* /usr/src/app/
CMD /usr/src/app/a.out
生成映象。
$ docker build -t rename:v1.0 ./
執行容器
$ docker run --rm rename:v1.0
ret = -1
該容器啟動後會執行可執行檔案a.out,重新命名一個目錄。可見結果,rename重新命名一個目錄的確是返回了失敗。
配置AUFS
準備
只有在OS安裝了AUFS的情況下才能使用AUFS儲存驅動,一般來說,Debian/Ubuntu都支援AUFS,而Redhat/CentOS都不支援AUFS。所以,你需要先檢視下你的系統是否安裝了AUFS。
$ grep aufs /proc/filesystems
nodev aufs
如果以上命令有輸出則表示支援AUFS,否則就說明還未安裝AUFS。可執行以下步驟:
a. 升級你係統的kernel版本到3.13或者更高,另外建議安裝kernel headers;
b. 對於Ubuntu/Debian:安裝linux-image-extra-*包:
$ apt-get install linux-image-extra-$(uname -r) \
linux-image-extra-virtual
配置
如果上述操作無誤,就可以使用AUFS來作為你Docker Daemon的儲存驅動了。
$ dockerd --storage-driver=aufs &
如果想持久化這個配置,可以編輯你的Docker配置檔案(如/etc/default/docker,雖然官方已經不建議使用該檔案了),並加入–storage-driver=aufs選項到DOCKER_OPTS中。
# Use DOCKER_OPTS to modify the daemon startup options.
DOCKER_OPTS="--storage-driver=aufs"
重啟docker daemon(systemctl restart docker.service)後,確認預設儲存驅動是否配置成功:
$ docker info | grep "Storage Driver"
Storage Driver: aufs
本地儲存和AUFS
當dockerd使用AUFS驅動時,驅動把映象和容器儲存在Docker host的本地儲存下:/var/lib/docker/aufs。
映象
映象層儲存在/var/lib/docker/aufs/diff裡。Docker 1.10之後,映象對應的目錄名稱不再和映象ID意義對應了。
/var/lib/docker/aufs/layers/目錄儲存了元資料資訊,這些元資料顯示了image層是如何疊加的。該目錄下的每個檔案,對應了一個層,而這個檔案的內容就是該層之下的層。如:
$ cat /var/lib/docker/aufs/layers/91e54dfb11794fad694460162bf0cb0a4fa710cfa3f60979c177d920813e267c
d74508fb6632491cea586a1fd7d748dfc5274cd6fdfedee309ecdcbc2bf5cb82
c22013c8472965aa5b62559f2b540cd440716ef149756e7b958a1b2aba421e87
d3a1f33e8a5a513092f01bb7eb1c2abf4d711e5105390a3fe1ae2248cfde1391
由於base layer之下不再有其它層,所有base layer對應的檔案內容是空的。
容器
執行中的容器對映在 /var/lib/docker/aufs/mnt/下,這就是AUFS給容器和它下層layer的一個mount point。如果容器沒有運行了,依然還有這個目錄,但卻是個空目錄,因為AUFS只在容器執行是才對映。Docker 1.10之上的版本,目錄名同樣不和容器ID對應。
容器元資料和多種配置檔案存放在該目錄下。
$ ls /var/lib/docker/aufs/mnt/670e0053b2ba02ab33dc24daca293e200aa98e871cefec016a5cbf9d41b7cfbf
bin boot dev etc home lib lib64 media mnt opt proc root run sbin srv sys tmp usr var
容器的可寫層儲存在目錄 /var/lib/docker/aufs/diff/,即使容器停止了,容器對應的目錄依然存在。只有刪除容器時,對應的目錄才會刪除。
AUFS在Docker中的效能
對於PaaS層來說,AUFS儲存驅動是一個很好的選擇。因為AUFS有效地在多個執行容器中共享映象,加速了容器啟動時間,減少了容器使用的磁碟空間。
AUFS在多個映象層和容器間分享檔案所使用的底層機制,高效地使用了系統的頁快取。
同時,AUFS儲存驅動也帶來了一些容器寫效能上的隱患。這是因為,容器第一次對任何檔案的修改,都需要先定位到檔案的所在的映象層次,並拷貝到容器最頂層的讀寫層。尤其當這些檔案存在於很底層,或者檔案本身非常大時,效能問題尤其嚴重。
小結
AUFS是Docker在Ubuntu/Debian中的預設儲存驅動,雖然後面可能會被替換掉。但暫時來說,它完美地契合Docker的特性。並且,如何合理使用,其效能非常優異。另外,需要注意的是,AUFS對目錄的重新命名支援得不好,在編寫程式時需要注意這點。