參考:深入剖析Kubernetes 映象分層結構理論詳解1 深入理解Docker容器和映象 Union File System
上文簡單介紹了容器的隔離與限制,下文會更進一步去理解docker是如何保持一致性的。
Docker在映象的設計中引入了層(layer)的概念。也就是說,使用者製作映象的每一步操作,都會生成一個層,也就是一個增量rootfs(根檔案系統)。
這裡涉及到2個概念:rootfs、image、layer和Union File System 。
rootfs
什麼是rootfs?rootfs是核心啟動時所mount的第一個檔案系統,核心程式碼映像檔案儲存在根檔案系統中,而系統引導啟動程式會在根檔案系統掛載之後從中把一些基本的初始化指令碼和服務等載入到記憶體中去執行。
docker架構下沿用了linux中的rootfs的思想,當 docker daemon 為 docker 容器掛載 rootfs 時與傳統的linux核心極其類似,將其設定為只讀模式,在rootfs掛載完成之後,和linux核心不一樣的是,docker daemon沒有將它切換為讀寫模組。
可以這樣理解,對 Docker 專案來說,它最核心的原理實際上就是為待建立的使用者程式:
- 啟用 Linux Namespace 配置;
- 設定指定的 Cgroups 引數;
- 切換程式的根目錄(Change Root)
image和layer
容器映象的rootfs是容器只讀的檔案系統,rootfs又是由多個只讀的image構成。於是,rootfs中每個只讀的image都可以稱為一層layer。
除了只讀的image之外,Docker Daemon在建立容器時會在容器的rootfs之上,再mount一層read-write filesystem,而這一層檔案系統,也稱為容器的一層layer,常被稱為top layer。
Docker的設計理念中,top layer轉變為image的行為(Docker中稱為commit操作),大大釋放了容器rootfs的靈活性。Docker的開發者完全可以基於某個映象建立容器做開發工作,並且無論在開發週期的哪個時間點,都可以對容器進行commit,將所有top layer中的內容打包為一個image,構成一個新的映象。Commit完畢之後,使用者完全可以基於新的映象,進行開發、分發、測試、部署等。不僅docker commit的原理如此,基於Dockerfile的docker build,其追核心的思想,也是不斷將容器的top layer轉化為image。
Union File System
Docker的儲存驅動的實現是基於Union File System,簡稱UnionFS,他是一種為Linux 、FreeBSD 和NetBSD 作業系統設計的,把其他檔案系統聯合到一個聯合掛載點的檔案系統服務。它用到了一個重要的資源管理技術,叫寫時複製。寫時複製(copy-on-write),也叫隱式共享,是一種對可修改資源實現高效複製的資源管理技術。對於一個重複資源,若不修改,則無需立刻建立一個新的資源,該資源可以被共享使用。當發生修改的時候,才會建立新資源。這會大大減少對於未修改資源複製的消耗。
OverlayFS採用UFS模式,但相對於AUFS,其效能更高。在Docker中主要有overlay和overlay2兩種實現。Docker-CE預設採用overlay2。
OverlayFS中使用了兩個目錄,把一個目錄置放於另一個之上,並且對外提供單個統一的視角。下層的目錄叫做lowerdir,上層的叫做upperdir。對外展示的統一檢視稱作merged。建立一個容器,overlay驅動聯合映象層和一個新目錄給容器。映象頂層是overlay中的只讀 lowerdir,容器的新目錄是 可讀寫 的upperdir。它們預設儲存於/var/lib/docker/overlay2/目錄下。例如:
[root@test-04 overlay2]# tree -L 2
.
├── 19435179b7c97d5363928de2e44c675528dac3c58c855e33396d907f023621cd
│ ├── committed
│ ├── diff
│ └── link
├── 31dd5a960bc9d2b35771eaca22a598da3bfb089ac37120d14282eaaa20d83de2
│ ├── diff
│ ├── link
│ ├── lower
│ └── work
├── 91ce67fbd28ed8938a39248405656dbbd9a5d66025cae63ee58d044e61c8907f
│ ├── committed
│ ├── diff
│ └── link
├── 9c696d2632aa0894128ad9ab3141a8ed6cc8d6b012d5634b93527ef72b538106
│ ├── diff
│ ├── link
│ ├── lower
│ └── work
├── 9c696d2632aa0894128ad9ab3141a8ed6cc8d6b012d5634b93527ef72b538106-init
│ ├── committed
│ ├── diff
│ ├── link
│ ├── lower
│ └── work
├── 9f557c818388f90c28b782095bb37e8ad39c0d2d8888d0d4f96958d0ac941630
│ ├── committed
│ ├── diff
│ ├── link
│ ├── lower
│ └── work
├── backingFsBlockDev
└── l
├── 44ISUTVEWAUB3GXP5K5ZTY66S4 -> ../9c696d2632aa0894128ad9ab3141a8ed6cc8d6b012d5634b93527ef72b538106/diff
├── 6WQVWTVP4BK4LSS6FEMXYI7UJL -> ../91ce67fbd28ed8938a39248405656dbbd9a5d66025cae63ee58d044e61c8907f/diff
├── FUB2EZA444WCH3SP5VKRH3SPES -> ../19435179b7c97d5363928de2e44c675528dac3c58c855e33396d907f023621cd/diff
├── IXUFBMAQP26LTFBD7APAIALQ2B -> ../31dd5a960bc9d2b35771eaca22a598da3bfb089ac37120d14282eaaa20d83de2/diff
├── OPVHT4CAHBKEYLSAQ7BCJ7RCGL -> ../9c696d2632aa0894128ad9ab3141a8ed6cc8d6b012d5634b93527ef72b538106-init/diff
└── R4JCGPBIE4JRIOHLUFHILT5XJR -> ../9f557c818388f90c28b782095bb37e8ad39c0d2d8888d0d4f96958d0ac941630/diff
說明:
- l:目錄下儲存了多個軟連結,使用短名指向其他各層;
- diff:包含該層映象的具體內容,即upperdir和lowerdir,此處都為lowerdir;
- link:記錄該目錄對應的短名;
- lower:記錄該目錄的所有lowerdir,每一級間使用
:
分隔; - work:該目錄是OverlayFS功能需要的,會被如copy_up之類的操作使用;
-init
以結尾的是頂層的lowerdir
的父目錄,目的是為了初始化container配置資訊;merged
:該目錄就是container的mount point,這就是暴露的lowerdir
和upperdir
的統一檢視。任何對容器的改變也影響這個目錄。
總結
- Namespace+Cgroups 構成的隔離環境,這一部分我們稱為容器執行時(Container Runtime),是容器的動態檢視。
- 掛載在 /var/lib/docker/overlay2/<directory_of_running_container>上的 rootfs,這一部分我們稱為容器映象(Container Image),是容器的靜態檢視,用於保持容器的一致性;
在 rootfs 的基礎上,Docker 公司創新性地提出了使用多個增量 rootfs 聯合掛載一個完整 rootfs 的方案,這就是容器映象中“層”的概念。通過“分層映象”的設計,以 Docker 映象為核心,來自不同公司、不同團隊的技術人員被緊密地聯絡在了一起。而且,由於容器映象的操作是增量式的,這樣每次映象拉取、推送的內容,比原本多個完整的作業系統的大小要小得多;而共享層的存在,可以使得所有這些容器映象需要的總空間,也比每個映象的總和要小。這樣就使得基於容器映象的團隊協作,要比基於動則幾個 GB 的虛擬機器磁碟映象的協作要敏捷得多。
一旦這個映象被髮布,那麼你在全世界的任何一個地方下載這個映象,得到的內容都完全一致,可以完全復現這個映象製作者當初的完整環境。這,就是容器技術“強一致性”的重要體現。