開箱即用!Linux 核心首個原生支援,讓你的容器體驗飛起來!| 龍蜥技術

阿里巴巴雲原生發表於2022-06-27

文/阿里雲核心儲存團隊,龍蜥社群高效能儲存技術 SIG 成員

容器化是最近幾年 devops 界流行的趨勢,通過業務的容器化我們將建立一個完全打包、自包含的計算環境,讓軟體開發人員能夠更加快速地建立和部署自己的應用程式。然而長期以來,由於映象格式的限制,容器啟動映象的載入是很慢的,相關背景細節可以參考“容器技術之容器映象篇”。為了加速容器的啟動,我們可以將優化後的容器映象搭配 P2P 網路等技術,從而有效降低容器部署啟動的時間,並可保障容器持續穩定執行,“讓容器應用管理更快更安全,Dragonfly 釋出 Nydus 容器映象加速服務”。

除了啟動速度,映象分層、去重、壓縮、按需載入等核心特性在容器映象領域也尤為重要。但是由於沒有原生的檔案系統支援,大多數都選擇了使用者態方案,Nydus 最初亦如此。隨著方案和需求的不斷演進,使用者態方案遇到了越來越多的挑戰,如效能與原生檔案系統相比有較大差距、高密場景下資源開銷較大等等。究其原因,主要是在使用者態實現映象格式的解析、按需載入等操作,將帶來大量核心態/使用者態通訊開銷。所以看起來解決了啟動速度,這些核心功能的研發就會有不小的挑戰,有點顧此失彼。

那麼有沒有可能做到魚和熊掌兼得呢?為此,龍蜥社群做了一個大膽的嘗試,我們設計並實現了相容核心原生 EROFS 檔案系統的 RAFS v6 格式,希望將容器映象方案下沉到核心態。同時為了一勞永逸,我們也嘗試將這個方案推進到核心主線以讓更多的人收益。最終,隨著 Linux 核心各位大佬的挑戰和我們的不斷完善,erofs over fscache 按需載入技術終於被合入 5.19 核心(連結見文末),至此 Nydus 映象服務的下一代容器映象分發方案逐漸清晰起來。這也是 Linux 主線核心首個原生支援、開箱即用的容器映象分發方案,容器映象的高密、高效能、高可用和易用性從此不再是個問題!作者為此也給自己加了個雞腿,哈哈!

本文將從 Nydus 架構回顧、RAFS v6 映象格式和 EROFS over Fscache 按需載入技術三個角度來分別介紹這一技術的演變歷程,並通過對比資料展示了當前方案的卓越效能,希望大家能夠儘早享受到容器啟動飛一樣的體驗!

一、Nydus 架構回顧

一句話總結一下,Nydus 映象加速服務是一種優化了現有的 OCIv1 容器映象架構,設計了 RAFS (Registry Acceleration File System) 磁碟格式,最終呈現為一種檔案系統的容器映象格式的映象加速實現。

容器映象的根本需求,本質上是為了提供容器的根目錄 (rootfs),這可以通過檔案系統 (file system) 或者是歸檔格式 (archive format) 來承載,當然也可以在檔案系統的基礎上二次套娃 (例如通過自定義的塊格式來承載),但本質載體是一個目錄樹,體現為檔案介面。

先看一下 OCIv1 標準映象,OCIv1 格式是一種基於 Docker Image Manifest Version 2 Schema 2 格式的映象格式規範,由 manifest、映象索引 (optional)、一系列容器映象層及配置檔案組成,細節可以參見相關文件,本文不再贅述。本質上說 OCI 映象是一個以層為基本單位的映象格式,每個層儲存了檔案級別的 diff data,以 tgz 歸檔格式儲存,如下所示:

由於 tgz 的限制,OCIv1 存在一些固有問題,例如無法按需載入、較粗的層級的去重粒度、每層 hash 值易變等等。

而一些“二次套娃”方案 (例如基於自定義塊格式的容器映象方案),也存在一些原理性的設計缺陷。例如:

  • 容器映象最終要體現為一棵目錄樹,那麼就需要相應的檔案系統來承載 (例如 ext4),這樣整個鏈路為“自定義塊格式 + 使用者態塊裝置 + 檔案系統”,相對於檔案系統方案其鏈路更長更復雜,端到端穩定性不可控;
  • 由於塊格式對上層的檔案系統不感知,無法區分檔案系統的後設資料和資料並分別處理 (例如壓縮);
  • 無法實現基於檔案的映象分析特性例如安全掃描、熱點分析和執行時攔截等;
  • 對於多個“二次套娃”容器映象,無法做到不修改 blob 內容直接 merge 成一個大映象,也無法做到不修改 blob 內容的情況下篩選部分檔案形成一個子映象,而這是檔案系統方案的天然能力。

而我們實現的 Nydus 則是一種基於檔案系統的容器映象儲存方案。其中將容器映象檔案系統的資料 (blobs) 和後設資料 (bootstrap) 分離,讓原來的映象層只儲存檔案的資料部分。並且把檔案以 chunk 為粒度分割,每層 blob 儲存對應的 chunk 資料;因為採用了 chunk 粒度,這細化了去重粒度,chunk 級去重讓層與層之間,映象與映象之間共享資料更容易,也更容易實現按需載入。由於後設資料被單獨分離出來合為一處,因此對於後設資料的訪問不需拉取對應的 blob 資料,需要拉取的資料量要小很多,I/O 效率更高。Nydus RAFS 映象格式如下圖所示:

二、RAFS v6 映象格式

2.1 RAFS 映象格式演變

在 RAFS v6 格式引入之前,Nydus 使用的是一個完全使用者態實現的映象格式,通過 FUSE 或 virtiofs 介面提供服務。但使用者態檔案系統方案在設計上存在以下缺陷:

  • 大量系統呼叫開銷不可忽略,例如深度為 1 的隨機小 I/O 訪問;
  • 當容器映象中存在大量檔案時,頻繁的檔案操作會產生大量的 fuse 請求,造成核心態/使用者態上下文的頻繁切換,造成效能瓶頸;
  • 非 FSDAX 場景下,使用者態到核心態的 buffer copy 會消耗 CPU 佔用;
  • 在 FSDAX (virtiofs 作為介面) 場景下,大量小檔案會大量佔用 DAX window 資源,存在潛在的效能抖動;頻繁切換訪問小檔案也會產生大量 DAX mapping setup 開銷。

這些問題是使用者態檔案系統方案的天然限制帶來的,而如果將容器映象格式的實現下沉到核心態,就可以從原理上根治上述問題。因而我們引入了 RAFS v6 映象格式,一個依託於核心 EROFS 檔案系統,實現於核心態的容器映象格式。

2.2 EROFS 檔案系統介紹

EROFS 檔案系統自 Linux 4.19 核心開始存在於 Linux 主線中,過去主要用於嵌入式和移動終端領域,存在於當前各大流行發行版中 (例如 Fedora、Ubuntu、Archlinux、Debian、Gentoo 等等)。使用者態工具 erofs-utils 也已經存在於這些發行版和 OIN Linux system definition 列表中,社群較活躍。

EROFS 檔案系統具備如下特徵:

  • 適用於多種場景的原生本地只讀塊檔案系統,磁碟格式具備最小 I/O 單位定義;
  • page-sized 塊對齊的不壓縮後設資料;
  • 通過 Tail-packing 內聯技術有效節省空間,同時維持高訪問效能;
  • 資料均以塊為單位定址 (mmap I/O 友好,不需 I/O 後處理);
  • 隨機訪問友好的磁碟目錄格式;
  • 核心磁碟格式非常簡單,且易於增加 payload,擴充套件性更好;
  • 支援 DIRECT I/O 訪問,支援塊裝置、FSDAX 等多種後端;
  • 同時 EROFS 預留了 boot sector,可支援 bootloader 自啟動等需求。

2.3 RAFS v6 映象格式

過去一年,阿里雲核心團隊對 EROFS 檔案系統進行了一系列的改進與增強,擴充其在雲原生下的使用場景,使其適應容器映象儲存系統的需求,最終呈現為一個實現於核心態的容器映象格式 RAFS v6。而除了將映象格式下沉到核心態,RAFS v6 還在映象格式上進行了一系列優化,例如塊對齊、更加精簡的後設資料等等。

新的 RAFS v6 映象格式如下:

改進後的 Nydus 映象服務架構如下圖所示,增加了對 (EROFS based) RAFS v6 映象格式的支援:

三、EROFS over Fscache 按需載入技術

erofs over fscache 是阿里雲核心團隊為 Nydus 開發的下一代容器映象按需載入技術,同時也是 Linux 核心原生的映象按需載入特性,於 5.19 版本合入 Linux 核心主線。

並被 Linux 核心權威媒體 LWN.net 整合入 5.19 合併視窗高亮特性(連結地址見文末):

在此之前業界已有的按需載入幾乎都是使用者態方案。使用者態方案會涉及頻繁的核心態/使用者態上下文切換,以及核心態/使用者態之間的記憶體拷貝,從而造成效能瓶頸。這一問題在容器映象已經全部下載到本地的時候尤其突出,此時容器執行過程中涉及的檔案訪問,都還是會陷出到使用者態的服務程式。

事實上我們可以將按需載入的 1) 快取管理和 2) 快取未命中的時候,通過各種途徑 (例如網路) 獲取資料 ,這兩個操作解耦開。快取管理可以下沉到核心態執行,這樣當映象在本地 ready 的時候,就可以避免核心態/使用者態上下文的切換。而這也正是 erofs over fscache 技術的價值所在。

3.1 方案原理

fscache/cachefiles (以下統稱 fscache) 是 Linux 系統中相對成熟的檔案快取方案,廣泛應用於網路檔案系統 (例如 NFS、Ceph 等)。我們對其進行了功能增強與擴充,使其支援本地檔案系統 (例如 erofs) 的按需載入特性。在該方案中,fscache 接管了快取管理的工作。

此時容器在訪問容器映象的時候,fscache 會檢查當前請求的資料是否已經快取,如果快取命中 (cache hit),那麼直接從快取檔案讀取資料。這一過程全程處於核心態之中,並不會陷出到使用者態。

否則 (cache miss) 需要通知使用者態的 Nydusd 程式以處理這一訪問請求,此時容器程式會陷入睡眠等待狀態;Nydusd 通過網路從遠端獲取資料,通過 fscache 將這些資料寫入對應的快取檔案,之後通知之前陷入睡眠等待狀態的程式該請求已經處理完成;之後容器程式即可從快取檔案讀取到資料。

3.2 方案優勢

正如之前所描述的,在映象資料已經全部下載到本地的情況下,使用者態方案會導致訪問檔案的程式頻繁陷出到使用者態,並涉及核心態/使用者態之間的記憶體拷貝。而 erofs over fscache 下則不會再陷出到使用者態,讓按需載入真的“按需”,從而在提前下載容器映象的場景下實現幾乎無損的效能和穩定性,最終獲得 1) 按需載入與 2) 提前下載容器映象這兩種場景下真正統一、無損的方案。

具體來說 erofs over fscache 相對於使用者態方案具有以下優勢。

3.2.1 非同步預取

容器建立之後,當容器程式尚未觸發按需載入 (cache miss) 的時候,使用者態的 Nydusd 就可以開始從網路下載資料並寫入快取檔案,之後當容器訪問的檔案位置恰好處於預取範圍內的時候,就會觸發 cache hit 直接從快取檔案讀取資料,而不會再陷出到使用者態。使用者態方案則無法實現該優化。

3.2.2 網路 IO 優化

當觸發按需載入 (cache miss) 的時候,Nydusd 可以一次性從網路下載比當前實際請求的資料量更多的資料,並將下載的資料寫入快取檔案。例如容器訪問 4K 資料觸發的 cache miss,而 Nydusd 實際一次性下載 1MB 資料,以減小單位檔案大小的網路傳輸延時。之後容器訪問接下來的這 1MB 資料的時候,就不必再陷出到使用者態。使用者態方案則無法實現該優化,因為即使觸發 cache miss 的時候,使用者態的服務程式同樣實現了該優化,由於使用者態方案實現的快取管理在使用者態,下一次容器訪問位於讀放大範圍內的檔案資料的時候,同樣需要陷出到使用者態以檢查當前訪問的資料是否已經快取。

3.2.3 更佳的效能表現

當映象資料已經全部下載到本地的時候 (即不考慮按需載入的影響),erofs over fscache 的效能表現顯著優於使用者態方案,同時與原生檔案系統的效能相近,從而實現與原生容器映象方案 (未實現按需載入) 相近的效能表現。以下是幾個工作負載下的效能測試資料 [1]。

read/randread IO

以下是檔案 read/randread buffer IO [2] 的效能對比:

"native" 表示測試檔案直接位於本地的 ext4 檔案系統中

"loop" 表示測試檔案位於 erofs 映象內,通過 loop 裝置的 DIRECT IO 模式掛載 erofs 映象

"fscache" 表示測試檔案位於 erofs 映象內,通過 erofs over fscache 方案掛載 erofs 映象

"fuse" 表示掛載測試檔案位於 fuse 檔案系統 [3] 內

"效能" 一欄對各個模式下的效能進行歸一化處理,以原生 ext4 檔案系統的效能為基準,比較其他模式下的效能

可以看到,fscache 模式下的 read/randread 效能與 loop 模式下的效能基本持平,同時要優於 fuse 模式;但是與原生 ext4 檔案系統的效能仍存在一定差距,我們正在進一步分析和優化,理論上該方案可以達到原生檔案系統的水平。

檔案後設資料操作測試

通過對大量小檔案執行 tar 操作 [4] 測試檔案後設資料操作的效能。

可以看到 erofs 格式的容器映象的後設資料效能甚至優於原生 ext4 檔案系統,這是 erofs 特殊的檔案系統格式導致的。由於 erofs 是一個只讀 (read-only) 檔案系統,因而其所有後設資料可以緊密排布在一起,而 ext4 作為可寫檔案系統,其後設資料則分散排布在多個 BG (block group) 中。

典型工作負載測試

測試 Linux 原始碼編譯 [5] 這一典型工作負載下的效能表現。

可以看到,fscache 模式下的 Linux 編譯負載效能與 loop 模式、原生 ext4 檔案系統的效能基本持平,同時要優於 fuse 模式。

3.2.4 高密部署

由於 erofs over fscache 方案基於檔案實現,即每個容器映象都表現為 fscache 下的一個快取檔案,因而其天然支援高密部署的場景。例如一個典型的 node.js 容器映象在該方案下對應 ~20 個快取檔案,那麼在一個部署有上百個容器的機器中,只需要維護上千個快取檔案。

3.2.5 故障恢復與熱升級

當映象檔案全部下載到本地的時候,映象中檔案的訪問不再需要使用者態服務程式的介入,因而使用者態服務程式存在更加充裕的時間視窗來實現故障恢復與熱升級功能。這種場景下甚至不再需要使用者態程式,從而實現與原生容器映象方案 (未實現按需載入) 相近的穩定性表現。

3.2.6 統一的容器映象方案

有了 RAFS v6 映象格式和 erofs over fscache 按需載入技術,Nydus 同時適用於 runc 與 rund,作為這兩種容器場景下的統一的容器映象分發方案。

另外更重要的,erofs over fscache 是 1) 按需載入與 2) 提前下載容器映象這兩種場景下真正統一、無損的方案。一方面,它實現了按需載入特性,在容器啟動的時候不需要容器映象全部下載到本地,從而助力極致的容器啟動速度。另一方面,它又完美相容容器映象已經下載到本地的這一場景,在檔案訪問過程中不再頻繁陷出到使用者態,從而實現與原生容器映象方案 (未實現按需載入) 近乎無損的效能和穩定性表現。

四、展望與感謝

之後我們會對 erofs over fscache 方案進行持續迭代與完善,例如不同容器之間的映象複用、FSDAX 支援以及效能優化等。

此外,目前 erofs over fscache 方案已經合入 Linux 5.19 主線,後續我們也會將該方案回合到 OpenAnolis (5.10 和 4.19 核心) ,使得龍蜥核心真正開箱可用,屆時歡迎大家使用。

最後感謝方案開發過程中支援和幫助過我們的所有個人與團隊,感謝位元組跳動與快手的同學對該方案的大力支援,包括但不限於社群聲援、測試、程式碼貢獻等,歡迎感興趣的小夥伴加入龍蜥社群 SIG 釘釘群(搜尋群號:34264214)和 Nydus映象服務 釘釘群(群號:34971767)交流,讓我們攜手一起構建一個更好的容器映象生態。

[1] 測試環境 ECS ecs.i2ne.4xlarge (16 vCPU, 128 GiB Mem),本地 NVMe 盤

[2] 測試命令 "fio -ioengine=psync -bs=4k -direct=0 -rw=[read|randread] -numjobs=1"

[3] 使用 passthrough 作為 fuse daemon,e.g. "passthrough_hp <src_dir> <tgt_dir>"

[4] 測試 "tar -cf /dev/null <linux_src_dir>" 命令的執行時間

[5] 測試 "time make -j16" 命令的執行時間

相關連結地址可移步龍蜥公眾號(OpenAnolis龍蜥)2022年5月27日相同推送檢視。

—— 完 ——



來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/70004278/viewspace-2898030/,如需轉載,請註明出處,否則將追究法律責任。

相關文章