前言
在之前的文章中有說過容器目錄的隔離機制. 今天來分析一下鏡像的文件系統.
Docker 已經用了很久了, 也知道鏡像存儲的時候是分層存儲的(從docker pull
時分層下載就能看出), 但是具體是如何將多層進行聚合並生成最終展示的文件, 這個過程從未深究過. 既然不知道, 又難掩好奇, 就抽時間康康它具體是怎麼做的吧
OverlayFS
OverlayFS
就是這樣一個聯合掛載的文件系統, 可以看看wiki上的介紹, 也可以看看內核文檔中的介紹.
它的作用簡單來說就是: 將多個目錄的文件內容融合爲一個目錄.
在OverlayFS
中, 文件目錄分爲四類:
lowerdir
: 只讀目錄, 可以有多個, 相同文件上層會覆蓋下層.upperdir
: 讀寫層. 只有一個, 對文件的增刪改記錄在這裏mergedir
: 在此目錄中展示合併後的文件內容workdir
: 文件系統的臨時目錄, 在創建後被清空. 這一層是文件系統使用的, 具體作用在後面說明. (todo https://blog.csdn.net/luckyapple1028/article/details/78075358)
實戰
在官方文檔中提到, 可以使用mount
命令來掛載OverlayFS
. 我們來測試一下
創建掛載相關的目錄及文件:
mkdir lower1 lower2 upper merged work
# lower文件被上層覆蓋
echo "lower1" > lower1/lower_change.txt
echo "lower2" > lower2/lower_change.txt
# lower文件在各層添加
echo "lower1" > lower1/lower1.txt
echo "lower2" > lower2/lower2.txt
# lower文件被upper覆蓋
echo "lower2" > lower2/upper.txt
echo "upper" > upper/upper.txt
# 文件掛載
mount -t overlay overlay -o lowerdir=lower1:lower2,upperdir=upper,workdir=work merged
# 卸載文件系統. 卸載後 merged 目錄下的所有文件均會消失
umount <dir>
此時進入merged
目錄查看文件, lower2
中的同名文件會被lower1
覆蓋. 現在, 讓我們來對系統進行簡單的測試, 以便於瞭解其原理(對merged
目錄操作):
- 創建文件: 創建的文件會同步出現在
upperdir
目錄下 - 修改文件: 修改的文件, 會將修改後的文件在
upperdir
下寫一份 - 刪除文件: 刪除文件就比較有意思了, 將
lower1.txt
文件刪除後, 會在upperdir
下創建一個whiteout
文件來標識下層文件被刪除. 如下圖:
當我們對掛載文件做了一系列操作之後, 可以嘗試下這樣的操作:
mkdir upper_new
umount merged
mount -t overlay overlay -o lowerdir=upper:lower1:lower2,upperdir=upper_new,workdir=work merged
# mount 命令可以看到當前所有的文件系統
mount
這樣我們就將對文件系統的改動記錄下來了. 熟悉不熟悉? 這不就是docker save
的操作麼.
workdir 作用
至此, 對OverlayFS
系統也有一定了解了, 但是workdir
我們從來就沒用到過, 它是做什麼的呢? 在官方文件中有說明, 我的理解是: 這個目錄是給文件系統使用的.
比如, 文件的刪除操作不是一步完成的, 涉及到文件刪除, upperdir
創建覆蓋文件等操作, 爲了防止斷電等異常情況, 會接住workdir
來實現原子刪除
不過具體的實現細節我沒有深究.
鏡像
好, 可以來看一下Docker
的鏡像存儲了.
鏡像信息保存在/var/lib/docker/image/overlay2/imagedb/content/sha256/<image sha256>
文件中. 其中rootfs
字段記錄了鏡像的各層信息, 下方是nginx
鏡像的信息. (或者用命令docker image inspect <image>
查看也行)
{
//...
"rootfs":
{
"type": "layers",
"diff_ids":
[
"sha256:8cbe4b54fa88d8fc0198ea0cc3a5432aea41573e6a0ee26eca8c79f9fbfa40e3",
"sha256:4b8862fe7056d8a3c2c0910eb38ebb8fc08785eaa1f9f53b2043bf7ca8adbafb",
"sha256:e60266289ce4a890aaf52b93228090998e28220aef04f128704141864992dd15",
"sha256:7daac92f43be84ad9675f94875c1a00357b975d6c58b11d17104e0a0e04da370",
"sha256:5e099cf3f3c83c449b8c062f944ac025c9bf2dd7ec255837c53430021f5a1517",
"sha256:4fd83434130318dede62defafcc5853d03dae8636eccfa1b9dcd385d92e3ff19"
]
}
}
layer
信息則保存在/var/lib/docker/image/overlay2/layerdb/sha256/<layer sha256>
目錄下, 此目錄下保存了以下幾個文件:
cache-id
: 文件的索引. 當前層的具體數據保存在/var/lib/docker/overlay2/<cache-id>
目錄下diff
: 就是上面的diff ids
size
: 當前層大小
好, 現在我們可以到/var/lib/docker/overlay2/<cache-id>/diff
目錄下看到這一層的具體數據了.
/var/lib/docker/overlay2/l
目錄下保存了diff
目錄的軟連接, 用於縮短路徑的吧, 給文件系統傳遞的就是這個路徑.
至此, 容器啓動後是如何做的心裏有數了吧. 將多個layer
層作爲lowerdir
, 新建一個upperdir
來進行容器內的文件修改. 最終整合成完成的文件目錄. 再通過目錄掛載替換進程的文件系統.
當然了, 文件系統也不是隻有Overlay
, 可以通過docker info
命令查看當前使用的文件系統. 其他的比如: unionfs-fuse
, mergerfs
等等.