Docker之Linux UnionFS
UnionFS
UnionFS是一種為Linux,FreeBSD和NetBSD作業系統設計的把其他檔案系統聯合到一個聯合掛載點的檔案系統服務。它使用branch把不同檔案系統的檔案和目錄“透明地”覆蓋,形成一個單一一致的檔案系統。這些branches或者是read-only或者是read-write的,所以當對這個虛擬後的聯合檔案系統進行寫操作的時候,系統是真正寫到了一個新的檔案中。看起來這個虛擬後的聯合檔案系統是可以對任何檔案進行操作的,但是其實它並沒有改變原來的檔案,這是因為unionfs用到了一個重要的資管管理技術叫寫時複製。
寫時複製(copy-on-write,下文簡稱CoW),也叫隱式共享,是一種對可修改資源實現高效複製的資源管理技術。它的思想是,如果一個資源是重複的,但沒有任何修改,這時候並不需要立即建立一個新的資源;這個資源可以被新舊例項共享。建立新資源發生在第一次寫操作,也就是對資源進行修改的時候。通過這種資源共享的方式,可以顯著地減少未修改資源複製帶來的消耗,但是也會在進行資源修改的時候增減小部分的開銷。
用一個經典的例子來解釋一下,Knoppix,一個用於Linux演示、光碟教學和商業產品演示的Linux發行版,就是把一個CD-ROM或者DVD和一個存在在可讀寫裝置(eg,U盤)上的叫knoppix.img的檔案系統聯合起來。這樣任何對CD/DVD上檔案的改動都會在被應用在U盤上,不改變原來的CD/DVD上的內容。
AUFS
AUFS,英文全稱是Advanced multi-layered unification filesystem, 曾經也叫 Acronym multi-layered unification filesystem,Another multi-layered unification filesystem。AUFS完全重寫了早期的UnionFS 1.x,其主要目的是為了可靠性和效能,並且引入了一些新的功能,比如可寫分支的負載均衡。AUFS的一些實現已經被納入UnionFS 2.x版本。
Docker是如何使用AUFS的
AUFS是Docker選用的第一種儲存驅動。AUFS具有快速啟動容器,高效利用儲存和記憶體的優點,直到現在AUFS仍然是Docker支援的一種儲存驅動型別。接下來我們要介紹Docker是如何利用AUFS儲存images和containers的。
image layer和AUFS
每一個Docker image都是由一系列的read-only layers組成。image layers的內容都儲存在Docker hosts filesystem的/var/lib/docker/aufs/diff目錄下。而/var/lib/docker/aufs/layers目錄則儲存著image layer如何堆疊這些layer的metadata。
準備一臺安裝了Docker 1.11.2的ECS。在沒有拉取任何映象,啟動任何容器的情況下,執行ls /var/lib/docker/aufs/diff命令,發現目錄沒有儲存任何內容。拉取Ubuntu:15.04映象,然後再次執行ls /var/lib/docker/aufs/diff命令。我們可以看到在docker pull的結果顯示ubuntu:15.04映象一共有4個layers,在執行ls /var/lib/docker/aufs/diff命令的結果中也有四個對應的儲存檔案目錄。這裡有一點需要說明的是,自從Docker 1.10之後,diff目錄下的儲存映象layer資料夾不再與映象ID相同。最後cat /var/lib/docker/aufs/layers/6bb19cb345da470e015ba3f1ca049a1c27d2c57ebc205ec165d2ad8a44e148ea命令列出來的是堆疊裡位於6bb19cb345da470e015ba3f1ca049a1c27d2c57ebc205ec165d2ad8a44e148ea layer下方的layers。
-
$ docker pull ubuntu:15.04
-
15.04: Pulling from library/ubuntu
-
9502adfba7f1: Pull complete
-
4332ffb06e4b: Pull complete
-
2f937cc07b5f: Pull complete
-
a3ed95caeb02: Pull complete
-
Digest: sha256:2fb27e433b3ecccea2a14e794875b086711f5d49953ef173d8a03e8707f1510f
-
Status: Downloaded newer image for ubuntu:15.04
-
$ ls /var/lib/docker/aufs/diff
-
208319b22189a2c3841bc4a4ef0df9f9238a3e832dc403133fb8ad4a6c22b01b 9c444e426a4a0aa3ad8ff162dd7bcd4dcbb2e55bdec268b24666171904c17573
-
6bb19cb345da470e015ba3f1ca049a1c27d2c57ebc205ec165d2ad8a44e148ea f193107618deb441376a54901bc9115f30473c1ec792b7fb3e73a98119e2cf77
-
$ ls /var/lib/docker/aufs/mnt
-
208319b22189a2c3841bc4a4ef0df9f9238a3e832dc403133fb8ad4a6c22b01b 9c444e426a4a0aa3ad8ff162dd7bcd4dcbb2e55bdec268b24666171904c17573
-
6bb19cb345da470e015ba3f1ca049a1c27d2c57ebc205ec165d2ad8a44e148ea f193107618deb441376a54901bc9115f30473c1ec792b7fb3e73a98119e2cf77
-
$ cat /var/lib/docker/aufs/layers/6bb19cb345da470e015ba3f1ca049a1c27d2c57ebc205ec165d2ad8a44e148ea
-
9c444e426a4a0aa3ad8ff162dd7bcd4dcbb2e55bdec268b24666171904c17573
-
f193107618deb441376a54901bc9115f30473c1ec792b7fb3e73a98119e2cf77
-
208319b22189a2c3841bc4a4ef0df9f9238a3e832dc403133fb8ad4a6c22b01b
接下來我們要以ubuntu:15.04映象為基礎映象,建立一個名為changed-ubuntu的映象。這個映象只是在映象的/tmp資料夾中新增一個寫了“Hello world”的檔案。你可以使用下面的Dockerfile來實現:
-
FROM ubuntu:15.04
-
RUN echo "Hello world" > /tmp/newfile
在terminal中cd到上面Dockerfile所在位置,執行docker build -t changed-ubuntu .命令來build映象。
-
$docker build -t changed-ubuntu .
-
Sending build context to Docker daemon 10.75 kB
-
Step 1 : FROM ubuntu:15.04
-
---> d1b55fd07600
-
Step 2 : RUN echo "Hello world" > /tmp/newfile
-
---> Running in c72100f81dd1
-
---> 9d8602c9aee1
-
Removing intermediate container c72100f81dd1
-
Successfully built 9d8602c9aee1
然後執行docker images檢視現在的映象,可以看到新生成的changed-ubuntu。
-
$docker images
-
REPOSITORY TAG IMAGE ID CREATED SIZE
-
changed-ubuntu latest 9d8602c9aee1 About a minute ago 131.3 MB
-
ubuntu 15.04 d1b55fd07600 10 months ago 131.3 MB
使用docker history changed-ubuntu命令可以清楚地檢視到changed-ubuntu映象使用了哪些image layers。從輸出中可以看到9d8602c9aee1 image layer位於最上層,只有12B的大小,就是由/bin/sh -c echo "Hello world" > /tmp/newfile命令建立的。也就是說changed-ubuntu映象只佔用了12Bytes的磁碟空間,這也證明了AUFS是如何高效使用磁碟空間的。而下面的四層image layers則是共享的構成ubuntu:15.04映象的4個image layers。“missing”標記的layers是自Docker 1.10之後,一個映象的image layers的image history資料都儲存在一個檔案中導致,這是一個Docker官方認為的正常行為。
-
$docker history changed-ubuntu
-
IMAGE CREATED CREATED BY SIZE COMMENT
-
9d8602c9aee1 4 minutes ago /bin/sh -c echo "Hello world" > /tmp/newfile 12 B
-
d1b55fd07600 10 months ago /bin/sh -c #(nop) CMD ["/bin/bash"] 0 B
-
<missing> 10 months ago /bin/sh -c sed -i 's/^#\s*\(deb.*universe\)$/ 1.879 kB
-
<missing> 10 months ago /bin/sh -c echo '#!/bin/sh' > /usr/sbin/polic 701 B
-
<missing> 10 months ago /bin/sh -c #(nop) ADD file:3f4708cf445dc1b537 131.3 MB
接下來我們繼續檢視layers的儲存資訊,從輸出中我們可以看到/var/lib/docker/aufs/diff目錄和/var/lib/docker/aufs/mnt目錄均多了一個資料夾9f122dbaa103338f27bac146326af38a2bcb52f98ebb3530cac828573faa3c4e。當使用cat /var/lib/docker/aufs/layers/9f122dbaa103338f27bac146326af38a2bcb52f98ebb3530cac828573faa3c4e命令來檢視它的metadata時可以看到,它前面的layers就是ubuntu:15.04映象所使用的4個image layers。進一步的探查/var/lib/docker/aufs/diff/9f122dbaa103338f27bac146326af38a2bcb52f98ebb3530cac828573faa3c4e資料夾,發現其中儲存了一個/tmp/newfile檔案,檔案中只有一行“Hello world”。至此,我們完整地分析出了image layer和AUFS是如何通過共享檔案和資料夾來實現映象儲存的。
-
$ ls /var/lib/docker/aufs/diff
-
208319b22189a2c3841bc4a4ef0df9f9238a3e832dc403133fb8ad4a6c22b01b 9c444e426a4a0aa3ad8ff162dd7bcd4dcbb2e55bdec268b24666171904c17573 f193107618deb441376a54901bc9115f30473c1ec792b7fb3e73a98119e2cf77
-
6bb19cb345da470e015ba3f1ca049a1c27d2c57ebc205ec165d2ad8a44e148ea 9f122dbaa103338f27bac146326af38a2bcb52f98ebb3530cac828573faa3c4e
-
$ ls /var/lib/docker/aufs/mnt
-
208319b22189a2c3841bc4a4ef0df9f9238a3e832dc403133fb8ad4a6c22b01b 9c444e426a4a0aa3ad8ff162dd7bcd4dcbb2e55bdec268b24666171904c17573 f193107618deb441376a54901bc9115f30473c1ec792b7fb3e73a98119e2cf77
-
6bb19cb345da470e015ba3f1ca049a1c27d2c57ebc205ec165d2ad8a44e148ea 9f122dbaa103338f27bac146326af38a2bcb52f98ebb3530cac828573faa3c4e
-
$ cat /var/lib/docker/aufs/layers/9f122dbaa103338f27bac146326af38a2bcb52f98ebb3530cac828573faa3c4e
-
6bb19cb345da470e015ba3f1ca049a1c27d2c57ebc205ec165d2ad8a44e148ea
-
9c444e426a4a0aa3ad8ff162dd7bcd4dcbb2e55bdec268b24666171904c17573
-
f193107618deb441376a54901bc9115f30473c1ec792b7fb3e73a98119e2cf77
-
208319b22189a2c3841bc4a4ef0df9f9238a3e832dc403133fb8ad4a6c22b01b
-
$ cat /var/lib/docker/aufs/diff/9f122dbaa103338f27bac146326af38a2bcb52f98ebb3530cac828573faa3c4e/tmp/newfile
-
Hello world
container layer和AUFS
Docker使用AUFS的CoW技術來實現image layer共享和減少磁碟空間佔用。CoW意味著一旦某個檔案只有很小的部分有改動,AUFS也需要複製整個檔案。這種設計會對容器效能產生一定的影響,尤其是在待拷貝的檔案很大,或者位於很多image layers的下方,或AUFS需要深度搜索目錄結構樹的時候。不過也不用過度擔心,對於一個容器,每一個image layer最多隻需要拷貝一次。後續的改動都會在第一次拷貝的container layer上進行。
啟動一個Container的時候,Docker會為其建立一個read-only的init layer,用來儲存與這個容器內環境相關的內容;Docker還會為其建立一個read-write的layer用來執行所有寫操作。
Container layer的mount目錄也是/var/lib/docker/aufs/mnt。Container的metadata和配置檔案都存放在/var/lib/docker/containers/目錄中。Container的read-write layer儲存在/var/lib/docker/aufs/diff/目錄下。即使容器停止,這個可讀寫層仍然存在,因而重啟容器不會丟失資料,只有當一個容器被刪除的時候,這個可讀寫層才會一起刪除。
接下來我們仍然用實驗來證明上面的結論。首先查詢現有的容器數目為0,而且在/var/lib/docker/containers目錄下也沒有查到任何資料。最後,檢視下系統的aufs mount情況,只有一個config檔案。
-
$ docker ps -a
-
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
-
$ ls /var/lib/docker/containers
-
$ ls /sys/fs/aufs/
啟動一個changed-ubuntu的容器。
-
$docker run -dit changed-ubuntu bash
-
fb5939d878bb0521008d63eb06adea75e6af275855f11879dfa3992dfdaa5e3f
-
$docker ps -a
-
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
-
fb5939d878bb changed-ubuntu "bash" 28 seconds ago Up 27 seconds amazing_babbage
檢視/var/lib/docker/aufs/diff目錄發現,下面多了兩個資料夾,f9ccf5caa9b7324f0ef112750caa14203b557d276ca08c78c23a42a949e2bfc8就是Docker為容器建立的read-write layer,而f9ccf5caa9b7324f0ef112750caa14203b557d276ca08c78c23a42a949e2bfc8-init則是Docker為容器建立的read-only的init layer。
-
$ ls /var/lib/docker/aufs/diff
-
208319b22189a2c3841bc4a4ef0df9f9238a3e832dc403133fb8ad4a6c22b01b 9f122dbaa103338f27bac146326af38a2bcb52f98ebb3530cac828573faa3c4e f9ccf5caa9b7324f0ef112750caa14203b557d276ca08c78c23a42a949e2bfc8-init
-
6bb19cb345da470e015ba3f1ca049a1c27d2c57ebc205ec165d2ad8a44e148ea f193107618deb441376a54901bc9115f30473c1ec792b7fb3e73a98119e2cf77
-
9c444e426a4a0aa3ad8ff162dd7bcd4dcbb2e55bdec268b24666171904c17573 f9ccf5caa9b7324f0ef112750caa14203b557d276ca08c78c23a42a949e2bfc8
/var/lib/docker/aufs/mnt目錄的變化和/var/lib/docker/aufs/diff一致。
-
$ ls /var/lib/docker/aufs/mnt
-
208319b22189a2c3841bc4a4ef0df9f9238a3e832dc403133fb8ad4a6c22b01b 9f122dbaa103338f27bac146326af38a2bcb52f98ebb3530cac828573faa3c4e f9ccf5caa9b7324f0ef112750caa14203b557d276ca08c78c23a42a949e2bfc8-init
-
6bb19cb345da470e015ba3f1ca049a1c27d2c57ebc205ec165d2ad8a44e148ea f193107618deb441376a54901bc9115f30473c1ec792b7fb3e73a98119e2cf77
-
9c444e426a4a0aa3ad8ff162dd7bcd4dcbb2e55bdec268b24666171904c17573 f9ccf5caa9b7324f0ef112750caa14203b557d276ca08c78c23a42a949e2bfc8
/var/lib/docker/aufs/layers/目錄下多了與上文兩個檔案目錄同名的檔案,用cat命令可以清楚地看到其依賴layer的記錄。
-
$ls /var/lib/docker/aufs/layers
-
208319b22189a2c3841bc4a4ef0df9f9238a3e832dc403133fb8ad4a6c22b01b 9f122dbaa103338f27bac146326af38a2bcb52f98ebb3530cac828573faa3c4e f9ccf5caa9b7324f0ef112750caa14203b557d276ca08c78c23a42a949e2bfc8-init
-
6bb19cb345da470e015ba3f1ca049a1c27d2c57ebc205ec165d2ad8a44e148ea f193107618deb441376a54901bc9115f30473c1ec792b7fb3e73a98119e2cf77
-
9c444e426a4a0aa3ad8ff162dd7bcd4dcbb2e55bdec268b24666171904c17573 f9ccf5caa9b7324f0ef112750caa14203b557d276ca08c78c23a42a949e2bfc8
-
$ cat /var/lib/docker/aufs/layers/f9ccf5caa9b7324f0ef112750caa14203b557d276ca08c78c23a42a949e2bfc8
-
f9ccf5caa9b7324f0ef112750caa14203b557d276ca08c78c23a42a949e2bfc8-init
-
9f122dbaa103338f27bac146326af38a2bcb52f98ebb3530cac828573faa3c4e
-
6bb19cb345da470e015ba3f1ca049a1c27d2c57ebc205ec165d2ad8a44e148ea
-
9c444e426a4a0aa3ad8ff162dd7bcd4dcbb2e55bdec268b24666171904c17573
-
f193107618deb441376a54901bc9115f30473c1ec792b7fb3e73a98119e2cf77
-
208319b22189a2c3841bc4a4ef0df9f9238a3e832dc403133fb8ad4a6c22b01b
-
$ cat /var/lib/docker/aufs/layers/f9ccf5caa9b7324f0ef112750caa14203b557d276ca08c78c23a42a949e2bfc8-init
-
9f122dbaa103338f27bac146326af38a2bcb52f98ebb3530cac828573faa3c4e
-
6bb19cb345da470e015ba3f1ca049a1c27d2c57ebc205ec165d2ad8a44e148ea
-
9c444e426a4a0aa3ad8ff162dd7bcd4dcbb2e55bdec268b24666171904c17573
-
f193107618deb441376a54901bc9115f30473c1ec792b7fb3e73a98119e2cf77
-
208319b22189a2c3841bc4a4ef0df9f9238a3e832dc403133fb8ad4a6c22b01b
在/var/lib/docker/containers/目錄下新建一個與containerid相同的資料夾,存放著容器的metadata和config檔案。
-
$ ls /var/lib/docker/containers/
-
fb5939d878bb0521008d63eb06adea75e6af275855f11879dfa3992dfdaa5e3f
-
$ ls /var/lib/docker/containers/fb5939d878bb0521008d63eb06adea75e6af275855f11879dfa3992dfdaa5e3f/
-
config.v2.json fb5939d878bb0521008d63eb06adea75e6af275855f11879dfa3992dfdaa5e3f-json.log hostconfig.json hostname hosts resolv.conf resolv.conf.hash shm
接下來我們從系統aufs來看mount的情況,在/sys/fs/aufs/目錄下多了一個si_fe6d5733e85e4904資料夾。通過cat /sys/fs/aufs/si_fe6d5733e85e4904/*命令我們可以清楚地看到這就是我們剛剛起的容器的layer許可權,只有最上面的f9ccf5caa9b7324f0ef112750caa14203b557d276ca08c78c23a42a949e2bfc8 layer是read-write許可權。
-
$s /sys/fs/aufs/
-
config si_fe6d5733e85e4904
-
$ cat /sys/fs/aufs/si_fe6d5733e85e4904/*
-
/var/lib/docker/aufs/diff/f9ccf5caa9b7324f0ef112750caa14203b557d276ca08c78c23a42a949e2bfc8=rw
-
/var/lib/docker/aufs/diff/f9ccf5caa9b7324f0ef112750caa14203b557d276ca08c78c23a42a949e2bfc8-init=ro+wh
-
/var/lib/docker/aufs/diff/9f122dbaa103338f27bac146326af38a2bcb52f98ebb3530cac828573faa3c4e=ro+wh
-
/var/lib/docker/aufs/diff/6bb19cb345da470e015ba3f1ca049a1c27d2c57ebc205ec165d2ad8a44e148ea=ro+wh
-
/var/lib/docker/aufs/diff/9c444e426a4a0aa3ad8ff162dd7bcd4dcbb2e55bdec268b24666171904c17573=ro+wh
-
/var/lib/docker/aufs/diff/f193107618deb441376a54901bc9115f30473c1ec792b7fb3e73a98119e2cf77=ro+wh
-
/var/lib/docker/aufs/diff/208319b22189a2c3841bc4a4ef0df9f9238a3e832dc403133fb8ad4a6c22b01b=ro+wh
-
64
-
/run/shm/aufs.xino
最後提下AUFS如何為Container刪除一個檔案。如果要刪除file1,AUFS會在container的read-write層生成一個.wh.file1的檔案來隱藏所有read-only層的file1檔案。至此,我們已清楚地描述和驗證了Docker是如何使用AUFS來管理container layers的。
自己動手寫AUFS
下面我們自己動手用簡單的命令來建立一個AUFS檔案系統,感受下如何使用AUFS和CoW實現檔案管理。
首先在你的實驗目錄下建立一個aufs的資料夾,然後在aufs目錄下建立一個mnt的資料夾做過掛載點。接著在aufs目錄下建立一個叫container-layer的資料夾,裡面有一個名為container-layer.txt的檔案,檔案內容為I am container layer。同樣地,繼續在aufs目錄下建立4個名為image-layer${n}的資料夾(n取值分別為1~4),裡面有一個名為image-layer${n}.txt的檔案,檔案內容為I am image layer${n}。使用如下命令檢查檔案內容:
-
$ cd /home/qinyujia/aufs
-
$ ls
-
container-layer image-layer1 image-layer2 image-layer3 image-layer4 mnt
-
$ cat container-layer.txt
-
I am container layer
-
$ cat image-layer1/image-layer1.txt
-
I am image layer 1
-
$cat image-layer2/image-layer2.txt
-
I am image layer 2
-
$ cat image-layer3/image-layer3.txt
-
I am image layer 3
-
$ cat image-layer4/image-layer4.txt
-
I am image layer 4
要聯合的檔案目錄都已經準備好了,接下來我們把container-layer和4個名為image-layer${n}的資料夾使用aufs的方式來掛載到剛剛建立的mnt目錄下。在mount aufs的命令中,我們沒有指定待掛載的5個資料夾的許可權,預設行為是,dirs指定的左邊起第一個目錄是read-write許可權,後續的都是read-only許可權。
-
$ sudo mount -t aufs -o dirs=./container-layer:./image-layer4:./image-layer3:./image-layer2:./image-layer1 none ./mnt
-
$ tree mnt
-
mnt
-
├── container-layer.txt
-
├── image-layer1.txt
-
├── image-layer2.txt
-
├── image-layer3.txt
-
└── image-layer4.txt
大家還記得上文中我們曾經在系統aufs目錄下去檢視檔案的讀寫許可權嗎?這裡我們依然使用cat /sys/fs/aufs/si_fe6d5733e85e5904/* 命令來確認新mount的檔案系統中每個目錄的許可權。(注意si_fe6d5733e85e5904應該是系統為這個mnt掛載點新建立的,而非在介紹Docker和AUFS裡面提到的那個資料夾)根據輸出我們可以清楚地看到,只有container-layer資料夾是read-write的,其餘都是read-only許可權。
-
$ cat /sys/fs/aufs/si_fe6d5733e85e5904/*
-
/home/qinyujia/aufs/container-layer=rw
-
/home/qinyujia/aufs/image-layer4=ro
-
/home/qinyujia/aufs/image-layer3=ro
-
/home/qinyujia/aufs/image-layer2=ro
-
/home/qinyujia/aufs/image-layer1=ro
-
/home/qinyujia/aufs/container-layer/.aufs.xino
下面我們要執行一個有意思的操作,往mnt/image-layer1.txt檔案末尾新增一行文字“write to mnt's image-layer1.txt”。根據上面我們介紹的CoW技術,大家猜想下會產生什麼樣的行為。
-
$ echo -e "\nwrite to mnt's image-layer1.txt" >> ./mnt/image-layer4.txt
我們用cat命令去檢視mnt/image-layer4.txt檔案內容,發現內容確實從“I am image layer 4”變成了
“I am image layer 4
write to mnt's image-layer1.txt”,因為mnt只是一個虛擬掛載點,因為我們繼續去尋找檔案到底修改在了什麼位置。
-
$ cat ./mnt/image-layer4.txt
-
I am image layer 4
-
write to mnt's image-layer1.txt
我們檢視image-layer4/image-layer4.txt檔案內容,發現其並未改變。
-
$ cat image-layer4/image-layer4.txt
-
I am image layer 4
接下來,當我們檢查container-layer資料夾的時候,發現多了一個名為image-layer4.txt的檔案,檔案的內容是
"I am image layer 4
write to mnt's image-layer1.txt"。也就是當我們嘗試向mnt/image-layer4.txt檔案進行寫操作的時候,系統首先在mnt目錄下查詢名為image-layer4.txt的檔案,將其拷貝到read-write層的container-layer目錄中,接著對container-layer目錄中的image-layer4.txt檔案進行寫操作。到此,我們成功地完成了一個小小的demo,實現了自己的AUFS檔案系統。
-
$ ls container-layer/
-
container-layer.txt image-layer4.txt
-
$cat container-layer/image-layer4.txt
-
I am image layer 4
-
write to mnt's image-layer1.txt
轉自:
https://blog.csdn.net/xiangxizhishi/article/details/79441396