一、UnionFS
Linux 的命名空間和控制組分別解決了不同資源隔離的問題,前者解決了進程、網絡以及文件系統的隔離,后者實現了 CPU、內存等資源的隔離,但是在 Docker 中還有另一個非常重要的問題需要解決 - 也就是鏡像。
鏡像到底是什么,它又是如何組成和組織的是作者使用 Docker 以來的一段時間內一直比較讓作者感到困惑的問題,我們可以使用 docker run 非常輕松地從遠程下載 Docker 的鏡像并在本地運行。
Docker 鏡像其實本質就是一個壓縮包,我們可以使用下面的命令將一個 Docker 鏡像中的文件導出:
$ docker export $(docker create busybox) | tar -C rootfs -xvf -
$ ls
bin dev etc home proc root sys tmp usr var
你可以看到這個 busybox 鏡像中的目錄結構與 Linux 操作系統的根目錄中的內容并沒有太多的區別,可以說 Docker 鏡像就是一個文件。
二、存儲驅動
Docker 使用了一系列不同的存儲驅動管理鏡像內的文件系統并運行容器,這些存儲驅動與 Docker 卷(volume)有些不同,存儲引擎管理著能夠在多個容器之間共享的存儲。
想要理解 Docker 使用的存儲驅動,我們首先需要理解 Docker 是如何構建并且存儲鏡像的,也需要明白 Docker 的鏡像是如何被每一個容器所使用的;Docker 中的每一個鏡像都是由一系列只讀的層組成的,Dockerfile 中的每一個命令都會在已有的只讀層上創建一個新的層:
FROM ubuntu:15.04
COPY . /app
RUN make /app
CMD python /app/app.py
容器中的每一層都只對當前容器進行了非常小的修改,上述的 Dockerfile 文件會構建一個擁有四層 layer 的鏡像:
當鏡像被 docker run 命令創建時就會在鏡像的最上層添加g zhi一個可寫的層,也就是容器層,所有對于運行時容器的修改其實都是對這個容器讀寫層的修改。
容器和鏡像的區別就在于,所有的鏡像都是只讀的,而每一個容器其實等于鏡像加上一個可讀寫的層,也就是同一個鏡像可以對應多個容器。
三、AUFS
UnionFS 其實是一種為 Linux 操作系統設計的用于把多個文件系統『聯合』到同一個掛載點的文件系統服務。而 AUFS 即 Advanced UnionFS 其實就是 UnionFS 的升級版,它能夠提供更優秀的性能和效率。
AUFS 作為聯合文件系統,它能夠將不同文件夾中的層聯合(Union)到了同一個文件夾中,這些文件夾在 AUFS 中稱作分支,整個『聯合』的過程被稱為聯合掛載(Union Mount):
每一個鏡像層或者容器層都是 /var/lib/docker/ 目錄下的一個子文件夾;在 Docker 中,所有鏡像層和容器層的內容都存儲在 /var/lib/docker/aufs/diff/ 目錄中:
$ ls /var/lib/docker/aufs/diff/00adcccc1a55a36a610a6ebb3e07cc35577f2f5a3b671be3dbc0e74db9ca691c 93604f232a831b22aeb372d5b11af8c8779feb96590a6dc36a80140e38e764d8
00adcccc1a55a36a610a6ebb3e07cc35577f2f5a3b671be3dbc0e74db9ca691c-init 93604f232a831b22aeb372d5b11af8c8779feb96590a6dc36a80140e38e764d8-init
019a8283e2ff6fca8d0a07884c78b41662979f848190f0658813bb6a9a464a90 93b06191602b7934fafc984fbacae02911b579769d0debd89cf2a032e7f35cfa
...
而 /var/lib/docker/aufs/layers/ 中存儲著鏡像層的元數據,每一個文件都保存著鏡像層的元數據,最后的 /var/lib/docker/aufs/mnt/ 包含鏡像或者容器層的掛載點,最終會被 Docker 通過聯合的方式進行組裝。
上面的這張圖片非常好的展示了組裝的過程,每一個鏡像層都是建立在另一個鏡像層之上的,同時所有的鏡像層都是只讀的,只有每個容器最頂層的容器層才可以被用戶直接讀寫,所有的容器都建立在一些底層服務(Kernel)上,包括命名空間、控制組、rootfs 等等,這種容器的組裝方式提供了非常大的靈活性,只讀的鏡像層通過共享也能夠減少磁盤的占用。
四、其他存儲驅動
AUFS 只是 Docker 使用的存儲驅動的一種,除了 AUFS 之外,Docker 還支持了不同的存儲驅動,包括 aufs、devicemapper、overlay2、zfs 和 vfs 等等,在最新的 Docker 中,overlay2 取代了 aufs 成為了推薦的存儲驅動,但是在沒有 overlay2 驅動的機器上仍然會使用 aufs 作為 Docker 的默認驅動。
不同的存儲驅動在存儲鏡像和容器文件時也有著完全不同的實現,有興趣的讀者可以在 Docker 的官方文檔 Select a storage driver 中找到相應的內容。
想要查看當前系統的 Docker 上使用了哪種存儲驅動只需要使用以下的命令就能得到相對應的信息:
$ docker info | grep Storage
Storage Driver: aufs
作者的這臺 Ubuntu 上由于沒有 overlay2 存儲驅動,所以使用 aufs 作為 Docker 的默認存儲驅動。