Overlayfs是一種類似aufs的一種堆疊文件系統,於2014年正式合入Linux-3.18主線內核,目前其功能已經基本穩定(雖然還存在一些特性尚未實現)且被逐漸推廣,特別在容器技術中更是勢頭難擋。本系列博文將首先介紹overlayfs的基本概念和應用場景,然后通過若干實例描述它的使用方式,最后從源碼角度結合Linux VFS Layer和Ext4fs連通分析overlayfs的實現。本文先來大致認識一下什么是Overlayfs,它有什么應用場景和使用限制。
Overlayfs概述
基本概念
Overlayfs是一種堆疊文件系統,它依賴並建立在其它的文件系統之上(例如ext4fs和xfs等等),並不直接參與磁盤空間結構的划分,僅僅將原來底層文件系統中不同的目錄進行“合並”,然后向用戶呈現。因此對於用戶來說,它所見到的overlay文件系統根目錄下的內容就來自掛載時所指定的不同目錄的“合集”。見圖1。
其中lower dirA / lower dirB目錄和upper dir目錄為來自底層文件系統的不同目錄,用戶可以自行指定,內部包含了用戶想要合並的文件和目錄,merge dir目錄為掛載點。當文件系統掛載后,在merge目錄下將會同時看到來自各lower和upper目錄下的內容,並且用戶也無法(無需)感知這些文件分別哪些來自lower dir,哪些來自upper dir,用戶看見的只是一個普通的文件系統根目錄而已(lower dir可以有多個也可以只有一個)。
雖然overlayfs將不同的各層目錄進行合並,但是upper dir和各lower dir這幾個不同的目錄並不完全等價,存在層次關系。首先當upper dir和lower dir兩個目錄存在同名文件時,lower dir的文件將會被隱藏,用戶只能看見來自upper dir的文件,然后各個lower dir也存在相同的層次關系,較上層屏蔽叫下層的同名文件。除此之外,如果存在同名的目錄,那就繼續合並(lower dir和upper dir合並到掛載點目錄其實就是合並一個典型的例子)。
各層目錄中的upper dir是可讀寫的目錄,當用戶通過merge dir向其中一個來自upper dir的文件寫入數據時,那數據將直接寫入upper dir下原來的文件中,刪除文件也是同理;而各lower dir則是只讀的,在overlayfs掛載后無論如何操作merge目錄中對應來自lower dir的文件或目錄,lower dir中的內容均不會發生任何的改變(理論設計如此,但實際在一些極端場景存在偏差,后面我會詳細介紹)。既然lower dir是只讀的,那當用戶想要往來自lower層的文件添加或修改內容時,overlayfs首先會的拷貝一份lower dir中的文件副本到upper dir中,后續的寫入和修改操作將會在upper dir下的copy-up的副本文件中進行,lower dir原文件被隱藏。
以上就是overlayfs最基本的特性,簡單的總結為以下3點:(1)上下層同名目錄合並;(2)上下層同名文件覆蓋;(3)lower dir文件寫時拷貝。這三點對用戶都是不感知的。
應用
基本了解overlayfs的基本特性以后,來了解overlayfs特性所帶來的好處和應用場景。在實際的使用中,我們可能會存在以下的多用戶復用共享文件和目錄的場景。見圖2。

在同一個設備上,用戶A和用戶B有一些共同使用的共享文件(例如運行程序所依賴的動態鏈接庫等),一般是只讀的;同時也有自己的私有文件(例如系統配置文件等),往往是需要能夠寫入修改的;最后即使用戶A修改了被共享的文件也不會影響到用戶B。
對於以上的需求場景,我們並不希望每個用戶都有一份完全一樣的文件副本,因為這樣不僅帶來空間的浪費也會影響性能,因此overlayfs是一個較為完美的解決方案。我們將這些共享的文件和目錄所在的目錄設定為lower dir (1~n),將用戶私有的文件和目錄所在的目錄設定為upper dir,然后掛載到用戶指定的掛載點,這樣即能夠保證前面列出的3點需求,同時也能夠保證用戶A和B獨有的目錄樹結構。最后最為關鍵的是用戶A和用戶B在各自掛載目錄下看見的共享文件其實是同一個文件,這樣磁盤空間的節省自是不必說了,還有就是共享同一份cache而減少內存的使用和提高訪問性能,因為只要cache不被回收,只需某個用戶首次訪問時創建cache,后續其他所有用戶都可以通過訪問cache來提高IO性能。
上面說的這種使用場景在容器技術中應用最為廣泛,下面以docker容器為例來介紹overlay的兩種應用方式:Overlay和Overlay2.
Docker容器將鏡像層(image layer)作為lower dir,將容器層(container layer)作為upper dir,最后掛載到容器merge掛載點,即容器的根目錄下。遺憾的是,早期內核中的overlayfs並不支持多lower layer,在Linux-4.0以后的內核版本中才陸續支持完善。而容器中可能存在多層鏡像,所以出現了兩種overlayfs的掛載方式,早期的overlay不使用多lower layer的方式掛載而overlay2則使用該方式掛載。
1. Overlay Driver
Overlay掛載方式如下。見圖3(該圖引用自Miklos Szeredi的《overlayfs and containers》2017 linux內核大會演講材料)。
本圖黃色框中的部分是鏡像層和容器層的組織方式,各個鏡像層中,每下一層中的文件以硬鏈接的方式出現在它的上一層中,以此類推,最終掛載overlayfs的lower dir為最上層鏡像層目錄imager layer N。與此同時,容器的writable dir作為upper dir,掛載成為容器的rootfs。本圖中雖然只描述了一個容器的掛載方式,但是其他容器也類似,鏡像層lower dir N共享,只是各個容器的upper dir不同而已。
2. Overlay2 Driver
Overlay2掛載方式如下。見圖4(該圖引用自Miklos Szeredi的《overlayfs and containers》2017 linux內核大會演講材料)。

Overlay2的掛載方式比Overlay的要簡單許多,它基於內核overlayfs的Multiple lower layers特性實現,不在需要硬鏈接,直接將鏡像層的各個目錄設置為overlayfs的各個lower layer即可(Overlayfs最多支持500層lower dir),對比Overlay Driver將減少inode的使用。
注意事項
盡管Overlayfs看起來是這么的優秀,但是當前它還並不是那么的完美,依然存在一些缺點和使用限制(還沒有完全支持POSIX標准),這里簡單列出一些,先認識一下,以后遇到也能心中有數:
0. Mount Overlayfs之后就不允許在對原lower dir和upper dir進行操作
當我們掛載完成overlayfs以后,對文件系統的任何操作都只能在merge dir中進行,用戶不允許再直接或間接的到底層文件系統的原始lower dir或upper dir目錄下修改文件或目錄,否則可能會出現一些無法預料的后果(kernel crash除外)。
1. Copy-up
Overlayfs的lower layer文件寫時復制機制讓某一個用戶在修改來自lower層的文件不會影響到其他用戶(容器),但是這個文件的復制動作會顯得比較慢,后面我們會看到為了保證文件系統的一致性,這個copy-up實現包含了很多步驟,其中最為耗時的就是文件數據塊的復制和fsync同步。用戶在修改文件時,如果文件較小那可能不一定能夠感受出來,但是當文件比較大或一次對大量的小文件進行修改,那耗時將非常可觀。雖然自Linux-4.11起內核引入了“concurrent copy up”特性來提高copy-up的並行性,但是對於大文件也還是沒有明顯的效果。不過幸運的是,如果底層的文件系統支持reflink這樣的延時拷貝技術(例如xfs)那就不存在這個問題了。
2. Rename directory(POSIX標准支持問題)
如果Overlayfs的某一個目錄是單純來自lower layer或是lower layer和upper layer合並的,那默認情況下,用戶無法對該目錄執行rename系統調用,否則會返回-EXDEV錯誤。不過你會發現通過mv命令重命名該目錄依然可以成功,那是因為mv命令的實現對rename系統調用的-EXDEV錯誤進行規避(這當然是有缺點的,先暫不展開)。在Linux-4.10起內核引入了“redirect dir”特性來修復這個問題,為此引入了一個內核選項:CONFIG_OVERLAY_FS_REDIRECT_DIR,用戶想要支持該特性可以在內核中開啟這個選項,否則就應避免對這兩類目錄使用rename系統調用。
3. Hard link break(POSIX標准支持問題)
該問題源自copy-up機制,當lower dir目錄中某個文件擁有多個硬鏈接時,若用戶在merge layer對其中一個寫入了一些數據,那將觸發copy-up,由此該文件將拷貝到upper dir,那么和原始文件的hard link也就斷開了,變成了一個單獨的文件,用戶在merge layer通過stat和ls命令能夠直接看到這個變化。在Linux-4.13起內核引入了“index feature”來修復這個問題,同樣引入了一個內核選項:CONFIG_OVERLAY_FS_INDEX,用戶想要修復該問題可以打開這個選項,不過該選項不具有向前兼容性,請謹慎使用。
4. Unconstant st_dev&st_ino(POSIX標准支持問題)
該問題同樣源自copy-up機制,當原來在lower dir中的文件觸發了copy-up以后,那用戶在merge layer見到了將是來自upper dir的新文件,那也就意味着它倆的inode是不同的,雖然inode中很多的attr和xattr是可以copy的,但是st_dev和st_ino這兩個字段卻具有唯一性,是不可以復制的,所以用戶可以通過ls和stat命令看到的該字段將發生變化。在Linux-4.12和Linux-4.13分別進行了部分的修復,目前在lower dir和upper dir都在同一個文件系統掛載點的場景下,問題已經修復,但lower dir和upper dir若來自不同的文件系統,問題依然存在。
5. File descriptor change(POSIX標准支持問題)
該問題也同樣源自copy-up機制,用戶在文件發生copy-up之前以只讀方式open文件(這操作不會觸發copy-up)得到的文件描述符fd1和copy-up之后open文件得到的文件描述符fd2指向不同的文件,用戶通過fd2寫入的新數據,將無法從fd1中獲取到,只能重新open一個新的fd。該問題目前社區主線內核依然存在,暫未修復。
以上這6點列出了目前Overlayfs的主要問題和限制,將在后文中陸續展開。社區為了讓Overlayfs能夠更加向支持Posix標准的文件系統靠攏,做出了很多的努力,后續將進一步修復上面提到且未修復的問題,還會增加對NFS Export、freeze snapshots、overlayfs snapshots等的支持,進一步完善overlayfs。
小結
Overlayfs在以它特有的機制已經使用的越來越廣泛,在Docker容器技術中以它優異的性能將會漸漸成為首選。不過overlayfs也尚存諸多限制,到目前為止,它還不是一個完全符合Posix規范的文件系統,但社區的開發人員們一直在努力完善,相信不久的將來我們會看到一個非常易用且成熟的Overlayfs。
