OverlayFS 儲存驅動程式
OverlayFS 是一個聯合檔案系統。
本頁將 Linux 核心驅動程式稱為 `OverlayFS`,將 Docker 儲存驅動程式稱為 `overlay2`。
注意對於 `fuse-overlayfs` 驅動程式,請查閱無根模式文件。
先決條件
OverlayFS 是推薦的儲存驅動程式,如果您滿足以下先決條件,則受支援
Linux 核心版本 4.0 或更高,或者使用核心版本 3.10.0-514 或更高的 RHEL 或 CentOS。
`overlay2` 驅動程式支援 `xfs` 後備檔案系統,但僅在啟用了 `d_type=true` 的情況下。
使用 `xfs_info` 驗證 `ftype` 選項是否設定為 `1`。要正確格式化 `xfs` 檔案系統,請使用標誌 `-n ftype=1`。
更改儲存驅動程式會使本地系統上現有的容器和映象無法訪問。在更改儲存驅動程式之前,使用 `docker save` 儲存您已構建的任何映象,或將它們推送到 Docker Hub 或私有登錄檔,這樣您以後就不需要重新建立它們了。
使用 `overlay2` 儲存驅動程式配置 Docker
在執行此過程之前,您必須首先滿足所有先決條件。
以下步驟概述瞭如何配置 `overlay2` 儲存驅動程式。
停止 Docker。
$ sudo systemctl stop docker
將 `/var/lib/docker` 的內容複製到一個臨時位置。
$ cp -au /var/lib/docker /var/lib/docker.bk
如果您想使用與 `/var/lib/` 不同的後備檔案系統,請格式化該檔案系統並將其掛載到 `/var/lib/docker`。請務必將此掛載新增到 `/etc/fstab` 以使其永久生效。
編輯 `/etc/docker/daemon.json`。如果它還不存在,請建立它。假設該檔案是空的,新增以下內容。
{ "storage-driver": "overlay2" }
如果 `daemon.json` 檔案包含無效的 JSON,Docker 將不會啟動。
啟動 Docker。
$ sudo systemctl start docker
驗證守護程序是否正在使用 `overlay2` 儲存驅動程式。使用 `docker info` 命令並查詢 `Storage Driver` 和 `Backing filesystem`。
$ docker info Containers: 0 Images: 0 Storage Driver: overlay2 Backing Filesystem: xfs Supports d_type: true Native Overlay Diff: true <...>
Docker 現在正在使用 `overlay2` 儲存驅動程式,並已自動建立了具有所需 `lowerdir`、`upperdir`、`merged` 和 `workdir` 構造的 overlay 掛載。
繼續閱讀以瞭解 OverlayFS 如何在 Docker 容器內工作,以及有關效能建議和與不同後備檔案系統相容性限制的資訊。
`overlay2` 驅動程式的工作原理
OverlayFS 在單個 Linux 主機上將兩個目錄分層,並將它們呈現為單個目錄。這些目錄稱為層,統一過程稱為聯合掛載。OverlayFS 將較低的目錄稱為 `lowerdir`,將較高的目錄稱為 `upperdir`。統一檢視透過其自己的目錄 `merged` 公開。
`overlay2` 驅動程式原生支援多達 128 個較低的 OverlayFS 層。此功能為與層相關的 Docker 命令(如 `docker build` 和 `docker commit`)提供了更好的效能,並消耗了後備檔案系統上更少的 inode。
磁碟上的映象和容器層
使用 `docker pull ubuntu` 下載一個五層映象後,您可以在 `/var/lib/docker/overlay2` 下看到六個目錄。
警告不要直接操作 `/var/lib/docker/` 內的任何檔案或目錄。這些檔案和目錄由 Docker 管理。
$ ls -l /var/lib/docker/overlay2
total 24
drwx------ 5 root root 4096 Jun 20 07:36 223c2864175491657d238e2664251df13b63adb8d050924fd1bfcdb278b866f7
drwx------ 3 root root 4096 Jun 20 07:36 3a36935c9df35472229c57f4a27105a136f5e4dbef0f87905b2e506e494e348b
drwx------ 5 root root 4096 Jun 20 07:36 4e9fa83caff3e8f4cc83693fa407a4a9fac9573deaf481506c102d484dd1e6a1
drwx------ 5 root root 4096 Jun 20 07:36 e8876a226237217ec61c4baf238a32992291d059fdac95ed6303bdff3f59cff5
drwx------ 5 root root 4096 Jun 20 07:36 eca1e4e1694283e001f200a667bb3cb40853cf2d1b12c29feda7422fed78afed
drwx------ 2 root root 4096 Jun 20 07:36 l
新的 `l` (小寫 `L`) 目錄包含作為符號連結的縮短層識別符號。這些識別符號用於避免觸及 `mount` 命令引數的頁面大小限制。
$ ls -l /var/lib/docker/overlay2/l
total 20
lrwxrwxrwx 1 root root 72 Jun 20 07:36 6Y5IM2XC7TSNIJZZFLJCS6I4I4 -> ../3a36935c9df35472229c57f4a27105a136f5e4dbef0f87905b2e506e494e348b/diff
lrwxrwxrwx 1 root root 72 Jun 20 07:36 B3WWEFKBG3PLLV737KZFIASSW7 -> ../4e9fa83caff3e8f4cc83693fa407a4a9fac9573deaf481506c102d484dd1e6a1/diff
lrwxrwxrwx 1 root root 72 Jun 20 07:36 JEYMODZYFCZFYSDABYXD5MF6YO -> ../eca1e4e1694283e001f200a667bb3cb40853cf2d1b12c29feda7422fed78afed/diff
lrwxrwxrwx 1 root root 72 Jun 20 07:36 NFYKDW6APBCCUCTOUSYDH4DXAT -> ../223c2864175491657d238e2664251df13b63adb8d050924fd1bfcdb278b866f7/diff
lrwxrwxrwx 1 root root 72 Jun 20 07:36 UL2MW33MSE3Q5VYIKBRN4ZAGQP -> ../e8876a226237217ec61c4baf238a32992291d059fdac95ed6303bdff3f59cff5/diff
最底層包含一個名為 `link` 的檔案,其中包含縮短識別符號的名稱,以及一個名為 `diff` 的目錄,其中包含該層的內容。
$ ls /var/lib/docker/overlay2/3a36935c9df35472229c57f4a27105a136f5e4dbef0f87905b2e506e494e348b/
diff link
$ cat /var/lib/docker/overlay2/3a36935c9df35472229c57f4a27105a136f5e4dbef0f87905b2e506e494e348b/link
6Y5IM2XC7TSNIJZZFLJCS6I4I4
$ ls /var/lib/docker/overlay2/3a36935c9df35472229c57f4a27105a136f5e4dbef0f87905b2e506e494e348b/diff
bin boot dev etc home lib lib64 media mnt opt proc root run sbin srv sys tmp usr var
第二底層以及每個更高層,都包含一個名為 `lower` 的檔案,它表示其父層,以及一個名為 `diff` 的目錄,其中包含其內容。它還包含一個 `merged` 目錄,其中包含其父層和自身的統一內容,以及一個由 OverlayFS 內部使用的 `work` 目錄。
$ ls /var/lib/docker/overlay2/223c2864175491657d238e2664251df13b63adb8d050924fd1bfcdb278b866f7
diff link lower merged work
$ cat /var/lib/docker/overlay2/223c2864175491657d238e2664251df13b63adb8d050924fd1bfcdb278b866f7/lower
l/6Y5IM2XC7TSNIJZZFLJCS6I4I4
$ ls /var/lib/docker/overlay2/223c2864175491657d238e2664251df13b63adb8d050924fd1bfcdb278b866f7/diff/
etc sbin usr var
要檢視在使用 `overlay` 儲存驅動程式與 Docker 時存在的掛載,請使用 `mount` 命令。為了便於閱讀,下面的輸出已被截斷。
$ mount | grep overlay
overlay on /var/lib/docker/overlay2/9186877cdf386d0a3b016149cf30c208f326dca307529e646afce5b3f83f5304/merged
type overlay (rw,relatime,
lowerdir=l/DJA75GUWHWG7EWICFYX54FIOVT:l/B3WWEFKBG3PLLV737KZFIASSW7:l/JEYMODZYFCZFYSDABYXD5MF6YO:l/UL2MW33MSE3Q5VYIKBRN4ZAGQP:l/NFYKDW6APBCCUCTOUSYDH4DXAT:l/6Y5IM2XC7TSNIJZZFLJCS6I4I4,
upperdir=9186877cdf386d0a3b016149cf30c208f326dca307529e646afce5b3f83f5304/diff,
workdir=9186877cdf386d0a3b016149cf30c208f326dca307529e646afce5b3f83f5304/work)
第二行上的 `rw` 表示 `overlay` 掛載是讀寫的。
下圖顯示了 Docker 映象和 Docker 容器如何分層。映象層是 `lowerdir`,容器層是 `upperdir`。如果映象有多個層,則使用多個 `lowerdir` 目錄。統一檢視透過一個名為 `merged` 的目錄公開,該目錄實際上是容器的掛載點。


當映象層和容器層包含相同的檔案時,容器層 (`upperdir`) 優先,並隱藏了映象層中相同檔案的存在。
為了建立一個容器,`overlay2` 驅動程式將代表映象頂層的目錄與一個新的容器目錄組合起來。映象的層是 overlay 中的 `lowerdirs`,並且是隻讀的。新的容器目錄是 `upperdir`,並且是可寫的。
磁碟上的映象和容器層
以下 `docker pull` 命令顯示了一個 Docker 主機下載一個包含五層的 Docker 映象。
$ docker pull ubuntu
Using default tag: latest
latest: Pulling from library/ubuntu
5ba4f30e5bea: Pull complete
9d7d19c9dc56: Pull complete
ac6ad7efd0f9: Pull complete
e7491a747824: Pull complete
a3ed95caeb02: Pull complete
Digest: sha256:46fb5d001b88ad904c5c732b086b596b92cfb4a4840a3abd0e35dbb6870585e4
Status: Downloaded newer image for ubuntu:latest
映象層
每個映象層在 `/var/lib/docker/overlay/` 中都有自己的目錄,其中包含其內容,如下例所示。映象層 ID 與目錄 ID 不對應。
警告不要直接操作 `/var/lib/docker/` 內的任何檔案或目錄。這些檔案和目錄由 Docker 管理。
$ ls -l /var/lib/docker/overlay/
total 20
drwx------ 3 root root 4096 Jun 20 16:11 38f3ed2eac129654acef11c32670b534670c3a06e483fce313d72e3e0a15baa8
drwx------ 3 root root 4096 Jun 20 16:11 55f1e14c361b90570df46371b20ce6d480c434981cbda5fd68c6ff61aa0a5358
drwx------ 3 root root 4096 Jun 20 16:11 824c8a961a4f5e8fe4f4243dab57c5be798e7fd195f6d88ab06aea92ba931654
drwx------ 3 root root 4096 Jun 20 16:11 ad0fe55125ebf599da124da175174a4b8c1878afe6907bf7c78570341f308461
drwx------ 3 root root 4096 Jun 20 16:11 edab9b5e5bf73f2997524eebeac1de4cf9c8b904fa8ad3ec43b3504196aa3801
映象層目錄包含該層獨有的檔案以及與下層共享資料的硬連結。這可以有效地利用磁碟空間。
$ ls -i /var/lib/docker/overlay2/38f3ed2eac129654acef11c32670b534670c3a06e483fce313d72e3e0a15baa8/root/bin/ls
19793696 /var/lib/docker/overlay2/38f3ed2eac129654acef11c32670b534670c3a06e483fce313d72e3e0a15baa8/root/bin/ls
$ ls -i /var/lib/docker/overlay2/55f1e14c361b90570df46371b20ce6d480c434981cbda5fd68c6ff61aa0a5358/root/bin/ls
19793696 /var/lib/docker/overlay2/55f1e14c361b90570df46371b20ce6d480c434981cbda5fd68c6ff61aa0a5358/root/bin/ls
容器層
容器也以磁碟形式存在於 Docker 主機的 `/var/lib/docker/overlay/` 檔案系統中。如果您使用 `ls -l` 命令列出正在執行的容器的子目錄,會存在三個目錄和一個檔案
$ ls -l /var/lib/docker/overlay2/<directory-of-running-container>
total 16
-rw-r--r-- 1 root root 64 Jun 20 16:39 lower-id
drwxr-xr-x 1 root root 4096 Jun 20 16:39 merged
drwxr-xr-x 4 root root 4096 Jun 20 16:39 upper
drwx------ 3 root root 4096 Jun 20 16:39 work
`lower-id` 檔案包含容器所基於的映象頂層的 ID,即 OverlayFS 的 `lowerdir`。
$ cat /var/lib/docker/overlay2/ec444863a55a9f1ca2df72223d459c5d940a721b2288ff86a3f27be28b53be6c/lower-id
55f1e14c361b90570df46371b20ce6d480c434981cbda5fd68c6ff61aa0a5358
`upper` 目錄包含容器讀寫層的內容,它對應於 OverlayFS `upperdir`。
`merged` 目錄是 `lowerdir` 和 `upperdirs` 的聯合掛載,它構成了從正在執行的容器內部看到的檔案系統檢視。
`work` 目錄是 OverlayFS 內部使用的。
要檢視使用 `overlay2` 儲存驅動程式與 Docker 時存在的掛載,請使用 `mount` 命令。為了便於閱讀,以下輸出已被截斷。
$ mount | grep overlay
overlay on /var/lib/docker/overlay2/l/ec444863a55a.../merged
type overlay (rw,relatime,lowerdir=/var/lib/docker/overlay2/l/55f1e14c361b.../root,
upperdir=/var/lib/docker/overlay2/l/ec444863a55a.../upper,
workdir=/var/lib/docker/overlay2/l/ec444863a55a.../work)
第二行上的 `rw` 表示 `overlay` 掛載是讀寫的。
容器讀寫如何與 `overlay2` 協同工作
讀取檔案
考慮三種情況,其中容器使用 overlay 開啟檔案進行讀取訪問。
檔案在容器層中不存在
如果容器開啟一個檔案進行讀取訪問,並且該檔案尚未存在於容器(`upperdir`)中,它將從映象(`lowerdir`)中讀取。這幾乎不會產生效能開銷。
檔案只存在於容器層
如果容器開啟一個檔案進行讀取訪問,並且該檔案存在於容器(`upperdir`)中而不在映象(`lowerdir`)中,它將直接從容器中讀取。
檔案同時存在於容器層和映象層
如果容器開啟一個檔案進行讀取訪問,並且該檔案在映象層和容器層中都存在,則讀取容器層中的檔案版本。容器層 (`upperdir`) 中的檔案會遮蔽映象層 (`lowerdir`) 中同名的檔案。
修改檔案或目錄
考慮一些容器中檔案被修改的場景。
首次寫入檔案
當容器首次寫入一個現有檔案時,該檔案在容器(`upperdir`)中不存在。`overlay2` 驅動程式會執行一個 `copy_up` 操作,將檔案從映象(`lowerdir`)複製到容器(`upperdir`)。然後容器將更改寫入到容器層中檔案的新副本。
然而,OverlayFS 在檔案級別而不是塊級別工作。這意味著所有 OverlayFS `copy_up` 操作都會複製整個檔案,即使檔案很大且只有一小部分被修改。這可能對容器寫入效能產生顯著影響。但有兩點值得注意
`copy_up` 操作僅在第一次寫入給定檔案時發生。後續對同一檔案的寫入操作將針對已經複製到容器的檔案副本進行。
OverlayFS 支援多層結構。這意味著當在具有多層的映象中搜索檔案時,效能可能會受到影響。
刪除檔案和目錄
當在容器內刪除一個*檔案*時,會在容器(`upperdir`)中建立一個*白化*檔案。映象層(`lowerdir`)中的檔案版本不會被刪除(因為`lowerdir`是隻讀的)。然而,這個白化檔案會阻止它對容器可用。
當在容器內刪除一個*目錄*時,會在容器(`upperdir`)中建立一個*不透明目錄*。這與白化檔案的工作方式相同,有效地阻止了對該目錄的訪問,即使它仍然存在於映象(`lowerdir`)中。
重新命名目錄
只有當源路徑和目標路徑都在頂層時,才允許對目錄呼叫 `rename(2)`。否則,它會返回 `EXDEV` 錯誤(“不允許跨裝置連結”)。您的應用程式需要被設計為能夠處理 `EXDEV` 並回退到“複製和取消連結”策略。
OverlayFS 和 Docker 效能
`overlay2` 的效能可能優於 `btrfs`。但是,請注意以下細節
頁面快取
OverlayFS 支援頁面快取共享。多個訪問同一檔案的容器會共享該檔案的單個頁面快取條目。這使得 `overlay2` 驅動程式在記憶體使用上非常高效,是高密度用例(如 PaaS)的良好選擇。
寫時複製
與其他寫時複製檔案系統一樣,每當容器首次寫入檔案時,OverlayFS 都會執行復制操作。這會給寫操作增加延遲,特別是對於大檔案。然而,一旦檔案被複制上去,所有後續對該檔案的寫入都發生在頂層,無需進一步的複製操作。
效能最佳實踐
以下通用效能最佳實踐適用於 OverlayFS。
使用快速儲存
固態硬碟(SSD)比旋轉磁碟提供更快的讀寫速度。
為寫入密集型工作負載使用卷
卷為寫入密集型工作負載提供了最佳且最可預測的效能。這是因為它們繞過了儲存驅動程式,並且不會產生由瘦配置和寫時複製引入的任何潛在開銷。卷還有其他好處,例如允許您在容器之間共享資料,並在沒有執行中的容器使用它們時也能持久化您的資料。
OverlayFS 相容性的限制
總結一下 OverlayFS 與其他檔案系統不相容的方面:
open(2)
- OverlayFS 只實現了 POSIX 標準的一個子集。這可能導致某些 OverlayFS 操作違反 POSIX 標準。其中一個操作就是寫時複製(copy-up)操作。假設您的應用程式呼叫 `fd1=open("foo", O_RDONLY)`,然後呼叫 `fd2=open("foo", O_RDWR)`。在這種情況下,您的應用程式期望 `fd1` 和 `fd2` 指向同一個檔案。然而,由於在第二次呼叫 `open(2)` 後發生了寫時複製操作,這兩個描述符指向了不同的檔案。`fd1` 繼續引用映象中的檔案(`lowerdir`),而 `fd2` 引用容器中的檔案(`upperdir`)。一個解決方法是 `touch` 這些檔案,這會觸發寫時複製操作。所有後續的 `open(2)` 操作,無論是隻讀還是讀寫訪問模式,都將引用容器中的檔案(`upperdir`)。
已知 `yum` 會受到影響,除非安裝了 `yum-plugin-ovl` 包。如果 `yum-plugin-ovl` 包在您的發行版中不可用,例如 RHEL/CentOS 6.8 或 7.2 之前的版本,您可能需要在執行 `yum install` 之前執行 `touch /var/lib/rpm/*`。該包為 `yum` 實現了上述的 `touch` 解決方法。
rename(2)
- OverlayFS 不完全支援 `rename(2)` 系統呼叫。您的應用程式需要能夠檢測到它的失敗,並回退到“複製和取消連結”的策略。