12.4 聯合檔案系統
聯合檔案系統 (UnionFS) 是 Docker 映象分層儲存的基礎,它允許將多個目錄掛載為同一個虛擬檔案系統。
12.4.1 什麼是聯合檔案系統
聯合檔案系統 (UnionFS) 是一種 分層、輕量級 的檔案系統,它將多個目錄 『聯合』 掛載到同一個虛擬目錄,形成一個統一的檔案系統檢視。
核心思想:將多個只讀層疊加,最上層可寫,形成完整的檔案系統。
/bin /etc /lib /usr /var /app /data"] UnionFS["UnionFS 聯合掛載"] ContainerLayer["容器層 (讀寫) : /app/data/log.txt (新寫入)"] ImageLayer3["映象層3 (只讀) : /app/app.py"] ImageLayer2["映象層2 (只讀) : /usr/local/bin/python"] ImageLayer1["映象層1 (只讀) : /bin /etc /lib (基礎系統)"] ContainerFS --> UnionFS UnionFS --> ContainerLayer --> ImageLayer3 --> ImageLayer2 --> ImageLayer1
12.4.2 為什麼 Docker 使用聯合檔案系統
Docker 選擇聯合檔案系統作為其儲存驅動,主要基於以下幾個核心優勢。
1. 映象分層複用
多個映象共享相同的底層,節省磁碟空間。
2. 快速建立
每個 Dockerfile 指令建立一層,只有變化的層需要重建:
FROM node:22 # 層1:基礎映象
COPY package.json ./ # 層2:依賴定義
RUN npm install # 層3:安裝依賴
COPY . . # 層4:應用程式碼
程式碼變化時,只需重建層 4,層 1-3 使用快取。
3. 容器啟動快
容器啟動時不需要複製映象,只需:
- 在映象層上建立一個薄的可寫層
- 聯合掛載所有層
12.4.3 Copy-on-Write:寫時複製
當容器修改只讀層中的檔案時:
/etc/nginx.conf"] end subgraph After ["修改後"] direction TB A_C["容器層
/etc/nginx.conf ← 複製到容器層後修改"] A_I["映象層
/etc/nginx.conf (原文件仍在,但被遮蔽)"] end B_C --- B_I A_C --- A_I
流程:
- 從只讀層讀取檔案
- 複製到容器的可寫層
- 在可寫層中修改
- 後續讀取使用可寫層的版本
12.4.4 Docker 支援的儲存驅動
Docker 的儲存驅動經歷了從早期各式各樣的機制(如 aufs, devicemapper),到被廣泛使用的現代經典 graph driver (overlay2),再到當下(Engine v29.x 及以後)在新安裝場景中預設啟用的 containerd 映象儲存引擎(containerd image store) 的演進。
| 儲存後端 / 驅動 | 核心屬性說明 | 推薦程度 |
|---|---|---|
| containerd image store | (v29.x 新一代預設後端,新裝預設) 基於 containerd 的 snapshotters,原生支援 OCI image index、多架構映象與 Attestations 建立溯源元資料儲存;啟用 userns-remap 時不可用。 |
✅強烈推薦 (現代預設) |
| overlay2 | (經典 Graph Driver) 傳統架構下的現代 Linux 預設驅動,效能優秀,但在處理複雜溯源元資料(索引)時受限。 | ✅推薦 (主要備用) |
| aufs | 早期預設,已從現代 Docker Engine 移除 | 歷史資料 |
| btrfs/zfs | 使用原生穩定檔案系統快照能力 | 特定場景 |
| devicemapper | 區塊裝置級儲存,已從現代 Docker Engine 移除 | 歷史資料 |
| vfs | 不使用 CoW,每層完整複製 | 僅測試 |
Classic Graph Drivers 與 Snapshotters 的核心差異
傳統模型(如 overlay2)將映象拉取解開的過程由 Docker 的 graph drivers 處理。而新的 containerd image store 則將這一職責徹底下放給了 containerd 自身的 snapshotters(底層在 Linux 發行版通常依然利用作業系統的 overlayfs)。這種架構改變帶來了:
- 本地免拉取檢視多平臺映象 index manifest 與 attestations (SBOM、Provenance)。
- 允許 Docker 使用 containerd snapshotters 管理映象層,便於與現代 OCI 生態能力對齊。它不表示 kubelet/containerd 會自動複用 Docker 本地映象;Kubernetes 映象分發仍應以 registry、映象拉取策略和執行時設定為準。
檢視當前儲存驅動與後端
## 檢視預設儲存驅動
$ docker info | grep "Storage Driver"
Storage Driver: overlay2
## 在 Engine v29.x 中,可以透過如下輸出驗證是否開啟了 containerd 映象後端:
$ docker info -f '{{ .DriverStatus }}'
[[driver-type io.containerd.snapshotter.v1]]
12.4.5 overlay2 工作原理
在經典 graph driver 體系下,overlay2 仍是 Linux 上的主要推薦驅動;Docker Engine 29 新安裝場景預設走 containerd image store/snapshotter。
容器看到的完整檔案系統"] OverlayFS["OverlayFS"] Upper["upper
(容器層)
讀寫"] Lower2["lower2
(映象層)
只讀"] Lower1["lower1
(基礎層)
只讀"] Merged --> OverlayFS OverlayFS --> Upper OverlayFS --> Lower2 OverlayFS --> Lower1
- lowerdir:只讀的映象層 (可以有多個)
- upperdir:可寫的容器層
- workdir:OverlayFS 的工作目錄
- merged:聯合掛載後的檢視
檔案操作行為
| 操作 | 行為 |
|---|---|
| 讀取 | 從上到下查詢第一個對應的檔案 |
| 建立 | 在 upper 層建立 |
| 修改 | 如果在 lower 層,先複製到 upper 層再修改 |
| 刪除 | 在 upper 層建立 whiteout 檔案標記刪除 |
12.4.6 檢視映象層
## 檢視映象的層訊息
$ docker history nginx:alpine
IMAGE CREATED CREATED BY SIZE
a6eb2a334a9f 2 weeks ago CMD ["nginx" "-g" "daemon off;"] 0B
<missing> 2 weeks ago STOPSIGNAL SIGQUIT 0B
<missing> 2 weeks ago EXPOSE map[80/tcp:{}] 0B
<missing> 2 weeks ago ENTRYPOINT ["/docker-entrypoint.sh"] 0B
<missing> 2 weeks ago COPY 30-tune-worker-processes.sh /docker-ent… 4.62kB
...
## 檢視層的儲存位置
$ docker inspect nginx:alpine --format '{{json .GraphDriver.Data}}' | jq
{
"LowerDir": "/var/lib/docker/overlay2/.../diff:/var/lib/docker/overlay2/.../diff",
"MergedDir": "/var/lib/docker/overlay2/.../merged",
"UpperDir": "/var/lib/docker/overlay2/.../diff",
"WorkDir": "/var/lib/docker/overlay2/.../work"
}
12.4.7 最佳實踐
為了建立高效、輕量的映象,我們在使用聯合檔案系統時應注意以下幾點。
1. 減少映象層數
## ❌ 每條指令建立一層
RUN apt-get update
RUN apt-get install -y nginx
RUN rm -rf /var/lib/apt/lists/*
## ✅ 合併為一層
RUN apt-get update && \
apt-get install -y nginx && \
rm -rf /var/lib/apt/lists/*
2. 避免在容器中寫入大量資料
容器層的寫入效能低於直接寫入。大量資料應使用:
- 資料卷 (Volume)
- 繫結掛載 (Bind Mount)
3. 使用 .dockerignore
排除不需要的檔案可以:
- 減小建立上下文
- 避免建立不必要的層