大家好,我是張晉濤。
上週在我的交流群裡有個小夥伴問到了 Overlay2 相關的問題,這篇就來介紹一下。
本節,我將為你介紹 Docker 現在推薦使用的儲存驅動 Overlay2,在開始之前,你可以執行以下命令來檢視 Docker 正在使用的儲存驅動:
(MoeLove) ➜ ~ docker info --format '{{.Driver}}'
overlay2
如果你看到的結果也是 overlay2
說明你的 Docker 已經在使用 overlay2 儲存驅動了。我在個人工作站上用的是 btrfs,這是因為自從 Fedora 33 開始,btrfs 就成為了 Fedora 預設的檔案系統。不過伺服器上就都是 overlay2 了。
你也可能會看到其他不同的結果,可以在啟動 docker daemon 的時候,透過 --storage-driver
引數進行指定,也可以在 /etc/docker/daemon.json
檔案中透過 storage-driver
欄位進行配置。
目前對於 Docker 最新版本而言,你有以下幾種儲存驅動可供選擇:
overlay2
fuse-overlayfs
btrfs
zfs
aufs
overlay
devicemapper
vfs
但它們對於你使用的檔案系統之類的都有不同的要求,且實現方式也不盡相同。我以本節的重點 overlay2
儲存驅動為例,它需要你使用 Linux 4.x 以上版本的核心,或者是對於 RHEL/CentOS 等需要使用 3.10.0-514 以上的核心(舊版本中存在一些相容性問題,我在之前的文章中有提到過)。
同時,它支援你使用 ext4 的檔案系統,或者增加了 ftype=1
的 xfs 檔案系統。可以透過 docker info
進行得到檔案系統相關的資訊。
# 省略了部分輸出
(MoeLove) ➜ ~ docker info
Storage Driver: overlay2
Backing Filesystem: extfs
Supports d_type: true
Native Overlay Diff: true
儲存驅動的作用
前面雖然已經聊瞭如何設定和檢查當前在用的儲存驅動,但尚未介紹為何一定要使用儲存驅動,以及它的作用。
還記得我在之前的文章《萬字長文:徹底搞懂容器映象構建》中為你介紹的 Docker 如何儲存映象相關的內容嗎,如果忘了可以回頭複習一下。
Docker 將容器映象做了分層儲存,每個層相當於包含著一條 Dockerfile 的指令。而這些層在磁碟上的儲存方式,以及在啟動容器時,如何組織這些層,並提供可寫層,便是儲存驅動的主要作用了。
另外需要注意的是:不同的儲存驅動實現不同,效能也有差異,同時使用不同的儲存驅動也會導致佔用的磁碟空間有所不同。
同時: 由於它們的實現不同,當你修改儲存驅動後,可能會導致看不到原有的映象,容器等,這是正常的,不必擔心,切換回原先的驅動即可見。
OverlayFS
瞭解完前面的背景知識後,你也看到了我剛才列出的可用儲存驅動中有兩個 overlay
和 overlay2
,其實 overlay2
算是 overlay
的升級版,這兩個儲存驅動所用的都是 OverlayFS
。
overlay
驅動是在 2014 年 8 月份首次進入 Docker 的,而 overlay2
則是在 2016 年 6 月份被合併,並首次出現在 Docker 1.12 中的。它的出現是為了解決 overlay
儲存驅動可能早層 inode 耗盡的問題。
簡單介紹完 overlay
和 overlay2
,我們將重點回歸到 OverlayFS
上。
我們啟動一個容器,以此為切入點來認識下 OverlayFS,注意: 以下內容使用 Linux 5.4 核心以及 Docker 20.10.21,不同環境下可能結果略有差異。
# 檢查無在執行的容器和 overlay 掛載
(MoeLove) ➜ ~ mount |grep overlay
(MoeLove) ➜ ~ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
# 啟動一個容器
(MoeLove) ➜ ~ docker run --rm -d alpine sleep 99999
caa9517ce0d799602735a30aaaaf123c07e07ff6e44c5a4b07e776af85780abe
(MoeLove) ➜ ~ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
caa9517ce0d7 alpine "sleep 99999" 23 seconds ago Up 22 seconds hopeful_dubinsky
# 檢查 overlay 掛載
(MoeLove) ➜ ~ mount |grep overlay
overlay on /var/lib/docker/overlay2/f4356a8f14342008fc298bf3d313b863d10f30ef447a3b2f51ea9ece0dec09db/merged type overlay (rw,relatime,seclabel,lowerdir=/var/lib/docker/overlay2/l/5OO3RLRXHJPEH3IFEXNCTO4PY5:/var/lib/docker/overlay2/l/UVA7IR67ZZTN2BNTKCZ7T6HUWU,upperdir=/var/lib/docker/overlay2/f4356a8f14342008fc298bf3d313b863d10f30ef447a3b2f51ea9ece0dec09db/diff,workdir=/var/lib/docker/overlay2/f4356a8f14342008fc298bf3d313b863d10f30ef447a3b2f51ea9ece0dec09db/work)
可以看到,在啟動容器後,系統上多了一個 OverlayFS (overlay) 的掛載。注意看其中的幾個內容:
掛載點在:
/var/lib/docker/overlay2/f4356a8f14342008fc298bf3d313b863d10f30ef447a3b2f51ea9ece0dec09db/merged
(MoeLove) ➜ ~ sudo ls /var/lib/docker/overlay2/f4356a8f14342008fc298bf3d313b863d10f30ef447a3b2f51ea9ece0dec09db/merged bin dev etc home lib media mnt opt proc root run sbin srv sys tmp usr var
其中的內容,看著很熟悉,是我們所啟動容器根目錄中的內容。為了驗證這一說法,我在容器中新寫一個檔案:
(MoeLove) ➜ ~ docker exec -it $(docker ps -ql) sh / # echo 'Hello Docker' > moelove-info
再次檢視此掛載點中的內容:
(MoeLove) ➜ ~ sudo ls /var/lib/docker/overlay2/22be5e4dc4541a60aa4f6de628c3938e7fdc9c4b117277274cd911c46166986b/merged bin dev moelove-info etc home lib media mnt opt proc root run sbin srv sys tmp usr var (MoeLove) ➜ ~ sudo cat /var/lib/docker/overlay2/f4356a8f14342008fc298bf3d313b863d10f30ef447a3b2f51ea9ece0dec09db/merged/moelove-info Hello Docker
可以看到剛才寫的內容已經在這個掛載點的目錄中了。
lowerdir
: 這是是我們 mount 中指定的目錄。這個
lowerdir
中包含兩個目錄,這是使用了核心對 OverlayFS multi layer 特性的支援,我們分別檢視下其中內容:(MoeLove) ➜ ~ sudo ls -a /var/lib/docker/overlay2/l/5OO3RLRXHJPEH3IFEXNCTO4PY5 . .. dev .dockerenv etc (MoeLove) ➜ ~ sudo ls -a /var/lib/docker/overlay2/l/UVA7IR67ZZTN2BNTKCZ7T6HUWU . .. bin dev etc home lib media mnt opt proc root run sbin srv sys tmp usr var
這兩個目錄,是不是看著很熟悉?
是的,它們就是我們所啟動容器根目錄中的大部分內容。為什麼說是大部分內容呢?當我們檢視其中的內容時,你也會發現它們的內容也並不完整。比如我們剛才新寫入的
moelove-info
檔案,或者當我們檢視etc
目錄下的檔案,你也會發現其中都只是常規系統/etc
目錄下的部分內容。(MoeLove) ➜ ~ sudo ls /var/lib/docker/overlay2/l/5OO3RLRXHJPEH3IFEXNCTO4PY5/etc hostname hosts mtab resolv.conf (MoeLove) ➜ ~ sudo ls /var/lib/docker/overlay2/l/UVA7IR67ZZTN2BNTKCZ7T6HUWU/etc alpine-release fstab init.d modprobe.d mtab passwd protocols shells udhcpd.conf apk group inittab modules network periodic securetty ssl conf.d hostname issue modules-load.d opt profile services sysctl.conf crontabs hosts logrotate.d motd os-release profile.d shadow sysctl.d
upperdir
是另一個重要的目錄,我們來看看其中的內容(MoeLove) ➜ ~ sudo ls -a /var/lib/docker/overlay2/f4356a8f14342008fc298bf3d313b863d10f30ef447a3b2f51ea9ece0dec09db/diff . .. moelove-info root
我們發現這個目錄中包含著剛才建立的
moelove-info
檔案。同時,其中也包含一個root
目錄,這個目錄便是我們預設使用的root
使用者的家目錄。如果去檢視其中的內容,也會發現剛才我們執行命令的歷史記錄。
workdir
這個目錄和upperdir
在同一個父目錄下,檢視其內容發現裡面只有一個work
目錄(MoeLove) ➜ ~ sudo ls -a /var/lib/docker/overlay2/f4356a8f14342008fc298bf3d313b863d10f30ef447a3b2f51ea9ece0dec09db/work . .. work
看完以上的介紹,想必你已經發現了它們之間的部分聯絡,在此之前,我們在額外看一個目錄,那就是 upperdir
和 workdir
以及掛載點共同的父目錄:
(MoeLove) ➜ ~ sudo ls /var/lib/docker/overlay2/f4356a8f14342008fc298bf3d313b863d10f30ef447a3b2f51ea9ece0dec09db
diff link lower merged work
你會發現這個目錄下的內容就比較直觀了。我們剛才已經看了其中 diff
,merged
和 work
目錄的內容了,現在看看 lower
中的內容吧:
(MoeLove) ➜ ~ sudo cat /var/lib/docker/overlay2/f4356a8f14342008fc298bf3d313b863d10f30ef447a3b2f51ea9ece0dec09db/lower
l/5OO3RLRXHJPEH3IFEXNCTO4PY5:l/UVA7IR67ZZTN2BNTKCZ7T6HUWU
我們發現,lower
檔案中的內容是以 :
分隔的兩個 lowerdir
的目錄名稱。
至此,我們可以得到以下結論:
lower
是基礎層,可以包含多個lowerdir
;diff
是可寫層,即掛載時的upperdir
,在容器內變更的檔案都在這一層儲存;merged
是最終的合併結果,即容器給我們呈現出來的結果;
Overlay2
經過前面對 Docker 啟動容器後掛載的 OverlayFS 的介紹後,Overlay2 的工作流程想必你也就比較清楚了。
將映象各層作為 lower
基礎層,同時增加 diff
這個可寫層,透過 OverlayFS 的工作機制,最終將 merged
作為容器內的檔案目錄展示給使用者。
你可能會有疑問,如果只是這樣簡單的組織,會不會有什麼限制呢?答案是肯定的,當然有限制,我們可以透過 Overlay2 的程式碼來看
// daemon/graphdriver/overlay2/overlay.go#L442
func (d *Driver) getLower(parent string) (string, error) {
// 省略部分內容
if len(lowers) > maxDepth {
return "", errors.New("max depth exceeded")
}
}
可以看到其對 lower 的深度有硬編碼的限制,當前硬編碼的限制是 128 。如果你在使用的過程中遇到這個錯誤,那表示你超過了最大深度限制,你就需要找些辦法來減少層級了。
總結
本節,我為你介紹了 OverlayFS 及 Overlay2 儲存驅動相關的內容。透過實際啟動容器生成的相關目錄來介紹 overlay2 的工作流程,想必透過這種方式能更易理解。
歡迎訂閱我的文章公眾號【MoeLove】