容器進階:運行環境的一致性

Blog:博客園 個人

參考:深入剖析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。

1.png

除了只讀的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,這就是暴露的lowerdirupperdir的統一視圖。任何對容器的改變也影響這個目錄。

總結

  • Namespace+Cgroups 構成的隔離環境,這一部分我們稱為容器運行時(Container Runtime),是容器的動態視圖。
  • 掛載在 /var/lib/docker/overlay2/<directory_of_running_container>上的 rootfs,這一部分我們稱為容器鏡像(Container Image),是容器的靜態視圖,用於保持容器的一致性;

在 rootfs 的基礎上,Docker 公司創新性地提出了使用多個增量 rootfs 聯合掛載一個完整 rootfs 的方案,這就是容器鏡像中「層」的概念。通過「分層鏡像」的設計,以 Docker 鏡像為核心,來自不同公司、不同團隊的技術人員被緊密地聯繫在了一起。而且,由於容器鏡像的操作是增量式的,這樣每次鏡像拉取、推送的內容,比原本多個完整的操作系統的大小要小得多;而共享層的存在,可以使得所有這些容器鏡像需要的總空間,也比每個鏡像的總和要小。這樣就使得基於容器鏡像的團隊協作,要比基於動則幾個 GB 的虛擬機磁盤鏡像的協作要敏捷得多。

一旦這個鏡像被發佈,那麼你在全世界的任何一個地方下載這個鏡像,得到的內容都完全一致,可以完全復現這個鏡像製作者當初的完整環境。這,就是容器技術「強一致性」的重要體現。