ImageApparate(幻影)鏡像加速服務讓鏡像分發效率提升 5-10 倍

作者介紹

李昂,騰訊高級開發工程師,主要關注容器存儲和鏡像存儲相關領域,目前主要負責騰訊容器鏡像服務和鏡像存儲加速系統的研發和設計工作。

李志宇,騰訊雲後台開發工程師。負責騰訊雲 TKE 集群節點和運行時相關的工作,包括 containerd、docker 等容器運行時組件的訂製開發和問題排查。

洪志國,騰訊雲架構師,負責 TKE 產品容器運行時,K8s,容器網路,mesh 數據面等基礎組件研發。

背景

在業務普遍已經完成容器化的大環境下,不同的業務場景對於容器啟動需求也是不同的,在離線計算和一些需要快速增加計算資源(伸縮組)的在線服務場景下,往往對於容器的啟動速度有較高的要求。

在容器啟動的整個周期中鏡像拉取的時間往往佔據 70% 甚至更多。據統計,某離線計算業務因容器鏡像較大,每次擴容上千 Pod 耗時高達 40 分鐘。鏡像分發成為容器快速彈性伸縮的主要障礙。

ImageApparate(幻影)

為了解決這個問題,騰訊雲容器服務 TKE 團隊開發了下一代鏡像分發方案ImageApparate(幻影), 將大規模大鏡像分發的速度提升 5-10倍

應對既有 Docker 下載鏡像模式帶來的問題,社區新方案的討論主要在鏡像數據的延遲載入(Lazy-Pull)和新鏡像格式的設計不再以層為最小單位,而是 chuck 或者鏡像內文件本身。

不過,目前看OCI V2離我們依然還很遠,當前我們通過何種方式來應對這類場景呢?

回到問題本身,當前OCI V1和容器運行時交互邏輯需要先下載完整鏡像才能運行容器,但是容器啟動和運行時到底會使用鏡像內的多少內容,這篇論文FAST ’16[1]統計了 DockerHub 中一些常見的官方鏡像在其使用啟動後需要讀取的數據量,得出的結論是僅有平均 6.4% 的內容需要讀取。也就是說鏡像中的大部分內容可能在容器的整個生命周期內根本不需要,那麼如果我們只載入 6% 的數據就可以大幅減少鏡像拉取時間,從而加速容器啟動速度,這也就為後續的優化提供了理論前提。

因此減少容器啟動時間的重點就在容器的 rootfs 即容器鏡像的獲取上。

基於此前提,在兼容OCI V1的框架下,TCR 推出了 ImageApparate(幻影) 容器鏡像加速服務。首先直接放結論,在 200 節點且鏡像內容占鏡像總大小的 5% 到 10%。如上所述,相比於傳統的下載全部鏡像的方式,ImageApparate 在容器全部啟動時間上都有 5-10倍 的提升。而且本測試主要並不只是關注容器創建時間,而是繼續測試了從容器啟動到業務進程可以提供服務後的總體時間:

  • 順序讀取 500MB 大文件測試了包括從容器啟動後到順序讀取 500MB 文件完成後的時間

  • 隨機讀取 1000 小文件測試了包括從容器啟動後到隨即讀取 1000個 4k-16k 完成後的時間

  • 執行 python 程式測試了包括從容器啟動後載入 Python 解釋器執行一段簡單的 python 程式碼完成後的時間

  • 執行 gcc 編譯測試了包括從容器啟動後執行 gcc 編譯一段簡單 C 程式碼並運行完成後的時間

ImageApparate 方案設計

傳統模式的問題

自 Docker 發布以來雲計算領域發生了巨大的變革,傳統虛擬機逐步被容器替代。Docker 秉持 Build, Ship And Run 的理念出色的完成了容器運行時和容器鏡像的設計,引領整個容器行業。但是隨著時間的推移容器的 Ship And Run 在面對廣泛的用戶需求場景中也逐漸暴露出一些問題。

傳統容器啟動和鏡像下載方式為:

  1. 訪問鏡像倉庫服務獲取許可權認證以及獲取鏡像存儲地址
  2. 通過網路訪問鏡像存儲地址下載全部鏡像層並解壓
  3. 根據鏡像的層資訊使用聯合文件系統掛載全部層作為rootfs,在此文件系統上創建並啟動容器img

容器鏡像的設計從 Docker 發布至今一直沿用下來,並已經成為事實標準也就是我們現在使用的OCI V1,使用分層的設計大大減少空間佔用,利用各類聯合文件系統(Aufs、Overlayfs)將每層聯合掛載起來形成一個完整的RootFS只讀根文件系統,容器運行時的寫入操作會在聯合文件系統的最上層的讀寫層,非常精巧的設計。

但是,開發者和用戶對於速度追求是永無止境的,隨著業務上雲的廣泛普及,為了充分發揮雲上資源的彈性能力,用戶往往需要新擴出來的計算節點可以用最快的速度使用容器化的計算能力(容器啟動服務可以接受流量),而此時這個全新節點就需要下載容器鏡像全部的層,大大拖慢容器啟動速度,在這個場景下容器鏡像的分層設計沒有得到充分的利用,完全失效了。

針對OCI V1容器鏡像格式的一些問題社區也開始有集中的討論,當前tar包作為OCI V1的鏡像層分發格式主要有以下問題:

  1. 不同層之間的內容冗餘

  2. 沒有基於文件的定址訪問能力,需要全部解包後才能訪問

  3. 沒有並發解包能力

  4. 使用 whiteout 處理文件刪除在不同存儲類型中轉換導致解壓效率低下

TCR-Apparate OCI 製品

我們設計的目標是面向生產級別,在節點上同時支援鏡像加速模式和普通模式,為了和正常OCI V1鏡像存儲解耦,我們開發了鏡像附加存儲IAS(ImageAttachStorage)結合鏡像Manifest中的外部層類型(Foreign Layer),可以在契合OCI V1語義下完成加速鏡像的製作、上傳和下載,繼承原有鏡像許可權的同時,加速後的鏡像Manifest索引以 OCI 製品形式存儲在鏡像倉庫本身的存儲中。

在鏡像格式方面為了支援按需載入和克服tar格式之前的一些缺點,ImageApparate 使用了只讀文件系統代替了 tar 格式。只讀文件系統解決了鏡像層內文件定址能力同時又具備成為Rootfs可靠的性能。ImageApparate 仍然使用分層的設計在Manifest外部層中直接指定附件存儲地址,附加存儲層IAS在下載鏡像時就可以按需掛載。

用戶開啟鏡像加速功能並設置相關規則後,push 鏡像後 ImageApparate 會在後台運行如下流程:

  1. 用戶以任意符合OCI V1介面標準的客戶端(包括 Docker)Push 鏡像到 TCR 倉庫
  2. TCR 的鏡像服務會將用戶數據寫入到鏡像倉庫本身的後端存儲中,一般為 COS 對象存儲。
  3. TCR 的鏡像服務會檢查鏡像加速規則,如果符合規則會給 Apparate-client 組件發出 Webhook 通知,請求轉換鏡像格式。
  4. Apparate-client 組件收到通知後會把 COS 數據寫入到IAS中,使用特定演算法把此鏡像的每個 Layer 逐個轉換為支援 ImageApparate 掛載的 Layer 格式。

因此,對於 TCR 用戶來說只需要定義規則標記哪些鏡像需要加速,而 CI/CD 的使用方式上沒有任何變化,原來的開發模式順理成章地繼承下來。

鏡像附加存儲 IAS(ImageAttachStorage)

顧名思義,狹義的鏡像附加存儲IAS是除了本身的鏡像後端存儲之外的數據存儲地址,IAS既可以和鏡像倉庫的使用相同的對象存儲,也可以使用 NFS 或者 Lustre。Apparate 中的鏡像附加存儲除了存儲地址外,還包含一套插件化的介面(兼容Posix)和鏡像層IAS中的布局(Layout)。IAS中每個目錄代表一個 Layer,這裡依然會使用基於內容定址(Content Addressable)復用內容相同層, 只讀文件系統文件包含了這個原始層中的全部內容,隨時可以通過載入元數據索引獲取整個目錄樹。目前 Apparate 使用了騰訊雲CFS[2]高性能版作為IAS的一種實現,高吞吐低延遲 CFS 目前和鏡像下載場景非常契合。

鏡像本地快取由不同的IAS附加存儲插件自身實現,目前 CFS 實現使用了 FScache 框架作為本地快取可以自動按頁快取訪問過的在遠端存儲上的部分數據,根據當前磁碟通過本地快取能力,有效提升鏡像數據重複訪問的性能和穩定性。

運行時實現

當前 ImageApparate 在節點上使用的IAS附加存儲插件被稱之為 Apparate-snapshotter,是通過 containerd 的 proxy-snapshotter 能力實現的。

Apparate-snapshotter 主要負責解析記錄在鏡像層中的IAS資訊,從而拿到另外數據存儲地址,接下來 Apparate-snapshotter 會去數據存儲服務中載入遠程數據,並在本地提供訪問的 Posix 入口。

比如在 CFS 場景下,會把遠端數據 mount 到本地,並把掛載點作為接下來本地訪問的入口。當需要使用遠端數據時便由 snapshotter 或內核來提供按需載入的能力。

只讀鏡像格式

對於支援 Lazy-Pull 的鏡像文件系統來說,只讀是非常關鍵的屬性,因為只讀文件系統不需要考慮數據寫入和刪除造成的碎片和垃圾回收,可以提前在製作文件系統的時候優化數據塊和索引的分布,這樣可以大幅提高文件系統的讀取性能。

當前 IAS 支援的只讀文件系統還增加了基於字母順序排序的目錄項索引(directory index),可以大大加速目錄項的Lookup操作。

ImageApparate 在 TCR 中使用方式

創建加速組件

當前 ImageApparate 在 TCR 中為 alpha 功能需要白名單開啟。開啟加速組件需要選擇對應 CFS 的高性能版,請確認所在地域有此版本 CFS。

img

創建加速規則,只有規則中匹配的鏡像或者 Tag 才會自動加速。之後再向 TCR 推送鏡像後可以看到匹配加速規則的鏡像會生成後綴為-apparateOCI製品。

img

在 TKE 集群中創建 TCR 插件時開啟鏡像加速配置,之後可以給需要加速的集群中節點打標籤kubectl label node xxx cloud.tencent.com/apparate=true,集群中 Pod 的鏡像可以仍然使用原鏡像名字(例如上述test/nginx:1.9),加速插件支援自動選取已加速的鏡像來進行掛載。如果鏡像已被加速,那麼觀察 TKE 集群中 Pod 的 image 欄位可以看到已被替換為 test/nginx:1.9-apparate。img

後續工作

當容器鏡像是按需載入後,Layer(層)可能已經不再是復用的最小單位了, ImageApparate 後續也會探索基於文件或者塊鏡像格式以及轉換工具以獲得更高的性能和效率。在介面側鏡像附加存儲IAS也會支援更多數據源,包括和 TKE P2P 組件的集成,按需載入與 P2P 結合可以更好的應對超大規模鏡像載入場景,大大減輕源站壓力。

內測邀請

ImageApparate(幻影)鏡像加速服務現已開啟內測,我們誠摯邀請您參與內測申請 ~ 名額有限,快快識別下方二維碼,直達內測申請頁面進行資訊提交:

參考資料

[1]FAST ’16: //www.usenix.org/conference/fast16/technical-sessions/presentation/harter
[2]CFS: //console.cloud.tencent.com/cfs
[3]Image Manifest V 2, Schema 2: //docs.docker.com/registry/spec/manifest-v2-2/
[4]EROFS: A Compression-friendly Readonly File System for Resource-scarce Devices: //www.usenix.org/system/files/atc19-gao.pdf