1. 程式人生 > 實用技巧 >Docker的OverlayFS儲存驅動

Docker的OverlayFS儲存驅動

OverlayFS儲存驅動

OverlayFS是一個現代的Union Filesystem,類似於AUFS,但速度更快,實現更簡單。Docker為OverlayFS提供了兩個儲存驅動程式:overlay,以及更新和更穩定的overlay2。(本次主題在Linux核心中對應的驅動是OverlayFS,在Docker中對應的儲存驅動是overlayoverlay2

注意:如果你的Linux使用的是OverlayFS,請使用overlay2作為驅動而不是overlay,因為overlay2在inode利用率上更高效。使用overlay2要求Linux核心版本在4.0或者更高。

先決條件

OverlayFS是Docker推薦的儲存驅動,但在使用前,需要滿足如下先決條件:

● Linux核心版本需要為4.0或者更高。RHEL和CentOS的版本需要為3.10.0-514或者更高。如果我們使用比較老的核心,我們需要使用不推薦的overlay驅動。

● 當d_type=true時,overlayoverlay2才被xfs備份檔案系統支援。

使用xfs_info來驗證ftype選擇是否設定為1。為了正確格式化xfs檔案系統,需要使用標誌-n ftype=1

d_type 是 Linux 核心的一個術語,表示 “目錄條目型別”,而目錄條目,其實是檔案系統上目錄資訊的一個數據結構。d_type,就是這個資料結構的一個欄位,這個欄位用來表示檔案的型別,是檔案,還是管道,還是目錄還是套接字等。d_type 從 Linux 2.6 核心開始就已經支援了,只不過雖然 Linux 核心雖然支援,但有些檔案系統實現了 d_type,而有些,沒有實現,有些是選擇性的實現,也就是需要使用者自己用額外的引數來決定是否開啟d_type的支援。

注意其中的 ftype,1表示支援d_type,0表示不支援。

警告:在沒有d_type支援的XFS上執行Docker,會導致Docker跳過嘗試使用overlay或overlay2驅動程式的階段。現有的版本將繼續執行,但會產生錯誤。這個操作會允許使用者遷移他們的資料。在將來的版本中,這將是一個致命的錯誤,它將阻止Docker啟動。

● 更改儲存驅動程式會使本地系統上現有的容器和映象無法訪問。在更改儲存驅動程式之前,使用docker save儲存已構建的任何映象或將其推送到docker Hub或私有倉庫中,以後便不需要重新建立它們。

配置overlay或者overlay2儲存驅動

如果能使用overlay2

儲存驅動,請儘量使用overlay2儲存驅動,而不是overlay儲存驅動。Docker EE不在支援overlay儲存驅動。使用overlay儲存驅動要求Linux核心版本為3.18或者更新。使用overlay2儲存驅動要求Linux核心版本為4.0或者更新。

在配置overlay或者overlay2儲存驅動時,請先滿足上一節的先決條件。

下面的步驟將講述如何配置overlay2儲存驅動。如果您需要使用舊版的overlay驅動程式,請指定它。

  1. 暫停Docker
    $ sudo systemctl stop docker
  2. 複製/var/lib/docker的內容要一個臨時資料夾
    $ cp -au /var/lib/docker /var/lib/docker.bk
  3. 如果要使用與/var/lib/使用的檔案系統不同的備份檔案系統,請格式化該檔案系統並將其裝載到 /var/lib/docker中。確保將此掛載新增到/etc/fstab以使其具有永久性。
  4. 編輯/etc/sysconfig/docker-storage 檔案。


修改後紅線標註的儲存驅動名稱。目前,我只嘗試過將其從overlay2修改為overlay。

  1. 啟動Docker
    $ sudo systemctl start docker
  2. 驗證daemon是否在使用overlay。
    $ docker info
    這是修改之前的資訊:

Docker現在正使用overlay2儲存驅動程式,並自動建立了包含所需lowerdirupperdirmergedworkdir結構。

這是修改後的資訊:

Overlay2儲存驅動是如何工作的

OverlayFS在一個Linux主機上分層為兩個目錄,並將它們顯示為一個目錄。這些目錄稱為layers,統一為一個目錄的過程稱為union mountOverlayFS將下層目錄稱為lowerdir,將上層目錄稱為upperdir。統一檢視將它們統一為一個目錄,這個過程稱為mergedoverlay2驅動程式原生支援多達128個較低的OverlayFS層。此功能為與層相關的Docker命令(如docker builddocker commit)提供了更好的效能,並且減少了在後臺文件系統上inodes的消耗。

Overlay2如何在磁碟上儲存映象層和容器層

使用命令docker pull ubuntu:18.04拉取具有三層的ubuntu 18.04映象。

注意:不要直接操作/var/lib/docker/中的任何檔案或目錄。這些檔案和目錄由Docker管理。

/var/lib/docker/overlay2資料夾中,我們可以看到四個資料夾。

新的l(lowercaseL)目錄包含作為符號連結的縮短層識別符號。這些識別符號用於避免達到mount命令引數的頁面大小限制。

最底層映象5a54cd...729包含一個link和一個diff資料夾。link儲存的是映象層的id。資料夾diff包含了映象層的內容。

檢視link檔案內容:

檢視diff資料夾內容:

倒數第二層映象和其往上的每一層映象都包含:link檔案:映象層的id。lower檔案:表示其依賴的上層映象。以及一個名為diff的資料夾,包含其映象層的內容。還包含一個merged資料夾,其中包含上層映象和自身統一的內容,以及一個work資料夾,供OverlayFS在內部使用。

檢視倒數第二層映象509376...65c的link檔案和lower檔案

link檔案:映象層的ID

lower檔案:表示其依賴的上層映象

2J60...CONP是最底層映象5a54cd...729的ID,所以表明倒數第二層映象509376...65c是依賴於最底層映象5a54cd...729創立的。

倒數第三層映象d6f68c...49b也是Ubuntu 18.04映象的最頂層映象

檢視倒數第三層映象d6f68c...49b的link檔案和lower檔案

link檔案:映象層的ID

lower檔案:表示其依賴的上層映象

V27C...NNCB是倒數第二層映象509376...65c的ID,2J60...CONP是最底層映象5a54cd...729的ID。表明倒數第三層映象d6f68c...49b是依賴於倒數第二層映象509376...65c和最底層映象5a54cd...729建立的。映象id出現越早表示映象在越高層,映象id出現越晚表示映象在越底層。

Overlay儲存驅動是怎麼工作的?

此內容僅適用於overlay儲存驅動程式。Docker建議使用overlay2驅動程式,它的工作方式不一樣。

OverlayFS在一個Linux主機上分層兩個目錄,並將它們顯示為一個目錄。這些目錄稱為layers,統一過程稱為union mount。OverlayFS底層的目錄叫做lowerdir,上層目錄叫做upperdir。兩個目錄的統一檢視稱為merged

下圖展示了Docker映象和容器是如何分層的。映象層就是lowerdir,容器層就是upperdir。統一檢視通過名為merged的目錄公開,該目錄實際上是容器裝載點。該圖顯示了Docker儲存架構如何對映到OverlayFS儲存架構。

當影象層和容器層包含相同的檔案時,容器層會掩蓋影象層中存在相同的檔案,優先顯示容器層的檔案。Overlay儲存驅動只適用於兩層。這意味著多層映象不能實現為多個OverlayFS層。相反,每個映象層在/var/lib/docker/overlay下實現為自己的目錄。然後,硬連結被用作為一種節省空間的方式與底層映象共享的資料。使用硬連結會導致過度使用inode,這是overlay儲存驅動程式的已知限制,並且可能需要額外配置備份檔案系統來解決。

建立容器時,overlay驅動會將映象頂層目錄與容器的新目錄進行結合。在overlay中,映象頂層就是lowerdir,它是隻讀的。容器的新目錄是指upperdir,它是可讀寫的。

Overlay如何在磁碟上儲存映象層和容器層

使用命令docker pull ubuntu:18.04拉取具有三層的ubuntu 18.04映象。映象層的ID與目錄的ID不對應。

映象層

每個映象層在/var/lib/docker/overlay/中都有自己的目錄,如下所示。

overlay的每一個映象層都有一個完整的目錄結構,映象與映象之間通過硬連結共享檔案。映象層目錄包含該層特有的檔案以及與較低層共享的資料的硬連結。這樣可以有效地利用磁碟空間。

檢視1768...5cb映象層和4273..c5c6映象層通過硬連結共享的檔案:

可以看到1768...5cb映象層和4273..c5c6映象層的ls命令檔案都有相同的inode號,表明它們是通過硬連結共享的。

(一般情況下,檔名和inode號碼是"一一對應"關係,每個inode號碼對應一個檔名。但是,Unix/Linux系統允許,多個檔名指向同一個inode號碼。這意味著,可以用不同的檔名訪問同樣的內容;對檔案內容進行修改,會影響到所有檔名;但是,刪除一個檔名,不影響另一個檔名的訪問。這種情況就被稱為"硬連結"(hard link)。)

容器層

容器層也存在於磁碟上Docker主機的檔案系統的/var/lib/Docker/overlay下。

執行一個ubuntu18.04映象。

新增加的兩個資料夾是容器層,5c7a...b0a-init是容器初始層。

檢視5c7a...b0a層的內容:

5c7a...b0a容器層包含三個資料夾和一個檔案。lower-id檔案包含容器基於映象的頂層id,即OverlayFS的lowerdir

檢視5c7a...b0a容器層的lower-id

5c7a...b0a容器層的lower-id正好是映象層lowerdir的ID。

檢視5c7a...b0a容器層的ls命令檔案:

跟前面的1768...5cb映象層和4273..c5c6映象層的ls命令檔案都有相同的inode號,這表明每次新建立容器時都會建立大量的硬連結,消耗大量的inode。在Linux系統中,inode的數量即硬連結的數量是有限的,達到一定數量後將無法再建立新的容器。這是使用overlay儲存驅動的一個缺點。

upper目錄包含容器讀寫層的內容,它對應於OverlayFS的upperdir

merged目錄是lowerdirupperdir的聯合掛載,它是執行容器中的檔案系統檢視。

work目錄是OverlayFS的內部目錄。

容器是如何使用overlay或者overlay2進行讀寫的?

讀取檔案

考慮三種情況,容器使用overlay驅動開啟一個檔案進行讀取訪問。

●檔案不存在於容器層:如果容器開啟或者讀取一個檔案,這個檔案不存在於容器層(upper目錄),容器將從映象層(lowerdir目錄)讀取該檔案。這會產生很小的效能開銷。

●檔案只存在於容器層:如果容器開啟或者讀取一個檔案,這個檔案存只在於容器層(upper目錄),不存在於映象層(lowerdir目錄),容器會直接從容器層中讀取該檔案。

●檔案在容器層和映象層中都存在:如果容器開啟或者讀取一個檔案,這個檔案在容器層和映象層中都存在,容器將會讀取容器層中的檔案。容器層(upper目錄)中的檔案會模糊映象層(lowerdir目錄)中的同名檔案。

修改檔案或資料夾

考慮如下場景,當容器中的檔案被修改時。

●第一次寫檔案:容器第一次寫入存在於映象層(lowerdir目錄),不存在於容器層(upper目錄)的檔案。overlay或者overlay2驅動會執行copy_up操作,將檔案從映象層(lowerdir目錄)拷貝到容器層(upper目錄)。容器會將更改寫入容器層中檔案的新副本。但是,OverlayFS是在檔案級別,而不是塊級別上工作的。這意味著所有OverlayFS的copy_up操作都將複製整個檔案,即使該檔案非常大但只有一小部分被修改。這會對容器寫入效能產生顯著影響。然而,有兩點值得注意:

copy_up操作只在第一次寫入給定檔案時發生。對同一檔案的後續寫入操作是針對已複製到容器的檔案副本。

○ OverlayFS僅適用於兩層。這意味著效能應該比AUFS好,AUFS在搜尋具有多個層的映象中的檔案時,AUFS可能會有明顯的延遲。這項優勢應用於overlayoverlay2驅動。overlayfs2在初始讀取時,效能會略低於overlayfs。因為overlayfs2必須遍歷更多層,但它會快取結果,因此這只是一個很小的損失。

●刪除檔案和資料夾:

○ 當在容器中刪除檔案時,容器層(upperdir目錄)中會建立一個 whiteout檔案(空白檔案)。映象層(lowerdir目錄)中的檔案不會被刪除,因為映象層(lowerdir目錄)是隻讀的。但是,whiteout檔案(空白檔案)將阻止容器使用它。

○ 當在容器中刪除資料夾時,容器層(upperdir目錄)中會建立一個opaque資料夾(不透明資料夾)。這與whiteout檔案的工作方式相同,可以有效地防止目錄被訪問,即使它仍然存在於映象層(lowerdir目錄)。

●修改目錄名稱:只有當源路徑和目標路徑都在頂層時,才允許對目錄呼叫rename(2)。否則,會返回EXDEV錯誤(“不允許跨裝置連結”)。我們的程式需要設計處理EXDEV錯誤的功能,並回退到”copy and unlikn“策略。

OverlayFS和Docker的效能

overlay2overlay的效能都要比aufsdevicemapper好。在某些特定的場景中,overlay2的效能要比btrfs好。然後,我們需要明白如下細節:

●頁快取。OverlayFS支援頁快取共享。訪問同一檔案的多個容器共享該檔案的單個頁快取項。這使得overlay2overlay驅動具有高效的記憶體使用效率,並且是PaaS等高密度用例的好選擇。

●copy_up。與AUFS一樣,OverlayFS在容器第一次寫入檔案時執行copy_up操作。這會增加寫入操作的延遲,特別是對於大檔案。但是,一旦檔案被複制,對該檔案的所有後續寫入都發生在容器層(upper目錄),而不需要進一步的複製操作。

OverlayFS的copy_up操作比AUFS的copy_up操作更快。因為AUFS支援的層比OverlayFS多,在眾多的AUFS層中搜索檔案可能會造成比較大的延遲。overlay2同樣支援多層,但是overlay2通過快取技術減輕了多層搜尋對效能的影響。

●Inode限制。使用overlay驅動可能會消耗大量的innode。當主機上存在大量映象和容器時,這種情況會更加嚴重。增加檔案系統可用inode數量的唯一方法是重新格式化它。為了避免出現這個問題,強烈推薦使用overlay2驅動。

效能的最佳實踐

以下通用效能最佳實踐也適用於OverlayFS。

●使用更快的儲存介質:固態硬碟(SSDs)比傳統的磁碟具有更快的讀寫速度。

●對於寫操作繁重的工作負載,使用volumes:Volumes為寫操作繁重的工作負載提供了最佳的效能。這是因為volumes繞過了儲存驅動程式,並且不會產生thin provisioning和copy-on-write帶來的任何潛在開銷。volumes還有其他好處,比如允許我們在容器之間共享資料,並會持久化儲存資料即使沒有容器使用這些資料。

OverlayFS相容性的限制

下面概述OverlayFS與其他檔案系統不相容的方面:

●open(2):OverlayFS只是實現了POSIX標準的一個子集。這可能導致某些OverlayFS操作違反POSIX標準。其中一種操作是copy_up操作。我們的程式在執行操作fd1=open("foo", O_RDONLY)fd2=open("foo", O_RDWR)。在這種情況下,我們程式期望的是fd1fd2指向的是同一個檔案。但是,由於在第二次呼叫open(2)之後發生copy_up操作,檔案描述符引用了不同的檔案。fd1繼續引用映象層(lowerdir目錄)中的檔案,而fd2則引用容器層(upperdir目錄)中的檔案。一種解決方法是touch導致copy_up操作發生的檔案。所有後續的open(2)操作,無論是隻讀還是讀寫訪問模式,都會引用容器層(upperdir目錄)中的檔案。

據悉,yum會受到影響,除非安裝了yum-plugin-ov1軟體包。如果yum-plugin-ov1軟體包我們的系統中(比如RHEL/CentOS 6.8之前的版本,或者7.2)不支援,我們可能需要在執行yum install之前執行touch /var/lib/rpm/*yum-plugin-ov1軟體包實現了上面針對yumtouch操作。

●rename(2):OverlayFS並沒有完成支援rename(2)系統呼叫。我們的程式需要檢測這個失敗,並且回退到“copy and unlink”策略。

參考文章

[1] Use the OverlayFS storage driver,

[2] overlay和overlay2的區別,