docker鏡像與容器存儲結構分析

view

 

Docker是一個開源的應用容器引擎,主要利用linux內核namespace實現沙盒隔離,用cgroup實現資源限制。

Docker 支持三種不同的鏡像層次存儲的drivers:   aufs、devicemapper、btrfs ;

Aufs:

AUFS (AnotherUnionFS) 是一種 Union FS, 簡單來說就是支持將不同目錄掛載到同一個虛擬文件系統下(unite several directories into a single virtual filesystem)的文件系統。 Aufs driver是docker 最早支持的driver,但是aufs只是linux內核的一個補丁集而且不太可以會被合併加入到linux內核中。但是由於aufs是唯一一個storage driver可以實現容器間共享可執行及可共享的運行庫, 所以當你跑成千上百個擁有相同程序代碼或者運行庫時時候,aufs是個相當不錯的選擇。

 

Device Mapper:

Device mapper 是 Linux 2.6 內核中提供的一種從邏輯設備到物理設備的映射框架機制,在該機制下,用戶可以很方便的根據自己的需要制定實現存儲資源的管理策略(詳見:http://www.ibm.com/developerworks/cn/linux/l-devmapper/index.html) 。

Device mapper driver 會創建一個100G的簡單文件包含你的鏡像和容器。每一個容器被限制在10G大小的卷內。(如果想要調整,參考:http://jpetazzo.github.io/2014/01/29/docker-device-mapper-resize/ 。中文譯文: http://zhumeng8337797.blog.163.com/blog/static/100768914201452405120107/

你可以在啓動docker daemon時用參數-s 指定driver:  docker -d -s devicemapper ;

 

Btrfs:

Btufs driver 在docker build 可以很高效。但是跟 devicemapper 一樣不支持設備間共享存儲(文檔裏是does not share executable memory between devices)。

 

下面筆者就已有的條件去分析下docker的鏡像與容器的存儲結構。

環境:

opensuse 13.10  + Docker version 1.2.0, build fa7b24f

Ubuntu 14.10    + Docker version 1.0.1, build 990021a

在沒有aufs支持的linux發行版本上(CentOS,opensuse等)安裝docker可能就使用了devicemapper driver。

查看你的linux發行版有沒有aufs支持:lsmod | grep aufs

筆者opensuse 13.10裏是沒有加載這個模塊的:

圖片1

而虛擬機裏的ubuntu 14.10 是加載了這個模塊的:

圖片2

 

而我們列出/var/lib/docker 這個目錄的內容也可以看出你那個docker是使用了哪個storage driver:

opensuse 13.10 上的/var/lib/docker

圖片3

這裏應該看出是使用了device mapper這個driver ;

然後再來看看虛擬機ubuntu 14.10上/var/lib/docker 目錄:

圖片4

這裏也可以看出筆者ubuntu裏docker 是使用了aufs 這個driver : 下文就這兩個不同的driver作對比。

請注意分析的是哪一個。

 

那麼鏡像文件是本地存放在哪裏呢?

筆者在opensuse和ubuntu裏把docker徹底重新安裝了一遍刪除了所有鏡像,並只Pull下來一個ubuntu:14.10的鏡像,這樣分析起來會比較簡單明瞭: 現在兩個系統都只有一個ubuntu:14.10的鏡像:

opensuse:

image5

Ubuntu :

image6

 

好了。首先現在我們來看看/var/lib/docker裏都是什麼文件。

1、首先用Python 的json.tool工具查看下repositories-* 裏的內容。

opensuse:

image7

裏面的json數據記錄的正是本地上存放的鏡像的名稱及其64位長度的ID.這個ID可以有其12位的簡短模式。 Ubuntu上也是一樣的:

image8

而且我們可以發現這兩個ID是一樣。這時我們其實可以猜想到:這個ID是全局性的,就是說你這個鏡像在鏡像倉庫上的ID也是這個。被其它機器上ID也是這個。這樣的好處無疑是方便管理鏡像。

 

 2、/var/lib/docker/graph 目錄裏的內容:

opensuse:

image9

Ubuntu:

image10

 

Graph目錄裏有7個長ID命名的目錄,其中第二個長ID是我們所pull下來的ubuntu14.10鏡像的對應的長ID..那麼其它6個是怎麼來的呢?

這裏我們用docker images -tree列出鏡像樹形結構:

image11

可以看到最下層的鏡像是我們的ubuntu14.10。那麼上面對應的是6個layer。就是說在這個樹中第n+1個層是基於第n個層上改動的。而第個層在graph目錄裏都對應着一個長ID目錄。

我們來看看虛擬機裏ubuntu14.10 裏的docker images -tree:

image13

大小數量一致。但是到了最後一個層的大小不一樣(這裏原因可能會是系統問題,也可能是docker版本問題。具體原因需要另外考察)

再分析一下各個層的大小,第一個爲0B, 第二個層就應該爲198.9MB,第三個層大小爲0.2MB(199.1-198.9)…如此類推下去。

上層的image依賴下層的image(注:這裏的邏輯上層是上圖樹形結構的下層),因此docker中把下層的image稱作父image,沒有父image的image稱作base image ;

例如我要用這裏的ubuntu:14.10爲模板啓動一個容器時,docker會加載樹形結構中的最下層( 2185fd5…),然後加載其父層(f180ea…),這樣一直加載到第一層(511136…)纔算加載這個rootfs。那麼一個層在哪裏保存它的父層信息呢?在下面長ID目錄裏的json文件其實也可以看到這個信息。

 

graph長ID目錄內容:(對於ubuntu裏是一樣的,這裏以opensuse爲例)

我們進入長ID目錄裏看看裏面的內容:

opensuse :

image14

我們進入最後一個層長ID目錄裏。裏面有一個json文件及一個名爲layersize的文件。 用cat查看layersize裏的內容,裏面記錄的數字是指這個層的大小。這裏(綠色前頭)是0。而我們從上面的目錄樹可以算出最後一個層確實是0。如果還不相信。我們再算算倒數第二個層的大小(opensuse裏的樹形圖裏短id爲f180ea115597的層)應該爲37.8M。現在進入對應長ID目錄:

image15

可以看到是是37816084(B),約37.8M,與我們計算的剛剛吻合。

而另一個文件json又是什麼呢?用python工具看看:(內容有點多,沒有截完)

image16

 

可以看到json這個文件保存的是這個鏡像的元數據。

拉到底部可以看到有個parent:的值:

image17

這個就是保存了其父層長ID的值。對照樹形結構看f180ea115597 的父層是不是0f154c52e965 。

但是注意在graph這個目錄裏並沒有找到我們想找到的鏡像內容存放地。只是一些鏡像相關的信息數據。

 

鏡像裏的內容存放在哪裏

opensuse : 

在opensuse下的/var/lib/docker/devicemapper/devicemapper/這個目錄下找到兩個文件,並列出其大小。

image18

其中一個data的文件大小爲100G(非真實佔用)。真實佔用的情況如下:

22_5

100G的只佔用了590M。

上面我們講到:Device mapper driver 會創建一個100G的簡單文件包含你的鏡像和容器。每一個容器被限制在10G大小的卷內。那麼看來這個100G的簡單文件正是這個名爲data 的文件,那麼鏡像和容器下是存放在這裏的。

好了。這時我在opensuse上再pull下一個ubuntu:12.10 鏡像看看這個文件大小有什麼變化: 這次一下子截了三個命令的信息:

image20

Pull下來的ubuntu是172.1M。樹形結構可以看到各個層的關係。而data的大小變成了787M. 沒pull ubuntu:12.10之前是590M.增加了197M,跟pull下來的172.1M有點差距。這裏可認爲是存儲了額外的某些信息。

那麼容器是不是也存放在這裏呢?

我們用ubuntu14.10啓動一個模板看看情況如何:

image21

這次我也是一下子截了幾個命令:

可以看到了一個基於ubuntu:14.10鏡像的容器在運行中,簡短ID是a9b35d72fcd4,

第二個命令du列出了data的大小爲789M,增加了2M。

第三個命令列出了container目錄內出現一個長ID的目錄,ID就是運行的容器的ID。但是裏面的文件應該都是些配置文件。並沒有我們想要的內容目錄。

 

這樣的話我們進一步做測試:在運行的容器內使用dd if=/dev/zero of=test.txt bs=1M count=8000 創建一個8G大小的文件後:

image22

這裏data變成了8.6G,增長了接近8G,這樣也證實了容器裏的內容是保存在data這個簡單文件內的。

這樣的話證實了devicemapper driver是把鏡像和容器的文件都存儲在data這個文件內。

 

Ubuntu 的aufs driver 又如何呢:

Ubuntu上由於是aufs driver 所以/var/lib/docker 目錄下有aufs目錄而不是devicemapper 目錄:

image23

這裏的aufs 目錄有三個目錄,diff 、layers 、mnt 三個目錄。

這裏layers目錄是保存了layers層次信息,並不是layers裏面的內容。

而diff 目錄時有數個長ID目錄:

image24

列出這幾個目錄的大小可以看出基本與上面樹形結構的所能計算的大小相對應(相關部分可能是由於壓縮或者其它原因造成,這裏純屬猜測)。

那我們進入f180ea115597這個ID對應的目錄看看裏面是什麼:

image25

裏面是一些文件夾,但是隻有幾個,並不像我們平時常規linux發行版裏的那麼齊全。

這裏的話其實我們可以想到了因爲一個層是基於另一個層之上的。Aufs文件系統可以做到增量修改,所以這裏的幾個文件夾是基於上一個層做的修改內容增量地保存在這裏,因爲上一個層對於這個層來說不可寫:

在這裏我需要先引用一張網上的圖片:

image100

 

這裏我們可以看到一個我們想象中的運行中的container是包含了若干個readonly的image層,然後最上面的writable層纔是我們可寫的層。第一個readonly的層會加載其父層。直到最下面的base image層。

我們所做的改動會被保存在最上面的那個writable層裏。當我們用commit 把容器固化成鏡像時那個層就會變成我們上面看到的“目錄不齊全的”長ID目錄。

 

爲了證實這一點,我們在運行一個基於ubuntu:14.10鏡像的容器:

image27

可以看到運行的容器簡短ID爲7b3c13323d8c 。

這時再列出diff目錄的內容:

image28

多了兩個長ID目錄,正是我們運行的容器的ID,列出內容:

image29

 

然後我們在運行的容器中創建一個/test 目錄,並在裏面用dd命令創建一個8G的test.txt文件:完成這些後再列出這兩個目錄內容:

image30

可以看到其中一個目錄(沒有init後綴)變成了7.5G,而另一個目錄還是24K。

在長ID目錄裏還多了一個test文件夾,正是我們在容器裏創建的,這樣的話裏面無疑問就是test.txt文件了。容器通過這種方法在writable層裏記錄了修改過的內容(增量記錄) (這裏有個小問題筆者也還不清楚:怎麼記錄刪除了東西呢?這個問題以後再考察)

從上面我們可以知道容器的writable 層是保存在以容器ID爲名的長ID目錄裏的,而ID+init後綴目錄是保存容器的初始信息的。

 

好了,現在我們進行最後一個實驗:把容器固化成鏡像。

(這裏要做個小小調整。把上面8G的文件刪除了再建一個3G大小的文件test_3G.txt代替)

image30

image31

 

Commit 後把容器固化成了test_image的鏡像。得到那個鏡像的長ID。

現在看看變化:

image32

那個窗口目錄還在,原因是我們還沒用rm 命令刪除那個容器。而多出來的鏡像目錄正是我們固化所得到的,其大小與上面容器writable層大小一致爲3GB。現在看看裏面是什麼內容:

image33

裏面有一個test目錄,目錄下對應我們創建的3GB大小的test_3G.txt文件。

這就是我們改動過的內容保存了在這個目錄內。

 

現在我們用rm命令刪除容器看看結果:

image34

容器被刪除了,其對應的長目錄ID也被刪除了。而那個固化的得到的鏡像( c7560af30 )被保存了下來。

 

通過上面的小實驗基本可以看清docker 在devicemapping 和 aufs這兩個driver 的存儲結構,但是這些目錄是怎樣靈活地在運行容器時被加載到一起就需要讀者去了解更深層的關於aufs及devicemapping相關的知識。

 

參考文獻:

docker官方文檔:https://docs.docker.com/reference/commandline/cli/

Docker存儲結構:http://blog.thoward37.me/articles/where-are-docker-images-stored/


 歡迎訪問本人網站:http://www.programfish.com 

LinuxCoder社區: http://linuxcoder.org

注意:轉載請註明 “作者:廣州Linux愛好者+雲計算 刁金明”



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章