ATC'22頂會論文RunD:高密高併發的輕量級 Serverless 安全容器執行時 | 龍蜥技術

OpenAnolis小助手發表於2022-09-02

編者按:目前的安全容器軟體棧 — 包括 host 作業系統中的 cgroup、guest 作業系統和用於函式工作負載的容器 rootfs,都會導致低部署密度和在低併發能力。為此,RunD 作為一種輕量級安全容器執行時,提出了 host-to-guest 的全棧最佳化方案來解決上述問題。本文整理自 龍蜥大講堂第 38 期,精彩分享影片回放已上傳至龍蜥官網(首頁-動態-影片),歡迎檢視!

ATC'22頂會論文RunD:高密高併發的輕量級 Serverless 安全容器執行時 | 龍蜥技術

摘要

在輕量級虛擬機器(MicroVM)中託管單個容器的安全容器現在已經廣泛應用於無伺服器計算。由於其中的使用者函式大多為細粒度抽象,因此為了提高資源利用率和使用者體驗,Serverless 需要支援高密度的容器部署和高併發的容器啟動。我們的調查顯示,目前的安全容器軟體棧 — 包括 host 作業系統中的 cgroup、guest 作業系統和用於函式工作負載的容器 rootfs,都會導致低部署密度和在低併發能力。為此,RunD 作為一種輕量級安全容器執行時,提出了 host-to-guest 的全棧最佳化方案來解決上述問題。使用 RunD 執行時,可以做到在一秒鐘內啟動超過 200個 安全容器,並且可以在一個 384GB 記憶體的節點上部署超過 2500 個安全容器。

一、介紹

函式計算作為無伺服器計算(或者伺服器無感知計算,Serverless)中的主要實現,透過隔離開發人員提供的細粒度函式並彈性管理使用資源,做到了更精細化的按量付費和更高的資料中心資源利用率。隨著雲原生技術的發展和微服務架構的流行,應用被越來越多的拆分成更細粒度的函式並部署為 Serverless 模式。

但基於傳統容器技術隔離的函式計算由於其較低的隔離性和安全性,已經逐步被替換成結合 MicroVM 和容器的安全容器技術為應用提供強隔離性和低延遲響應。安全容器通常會在一個普通的容器外額外巢狀一層輕量級的 microVM 中,如下圖1 (a)所示。透過這種方式,使用者可以基於現有的容器基礎設施和生態系統構建 Serverless 服務。安全容器能夠確保與 MicroVM 中的容器執行時相容。Kata Containers 和 FireCracker 都提供了實現這種安全容器的實踐經驗。

ATC'22頂會論文RunD:高密高併發的輕量級 Serverless 安全容器執行時 | 龍蜥技術

(圖1/目前主流的安全容器模型,以及對應的軟體棧架構)

在 Serverless 場景下,函式的輕量級和短期執行的特性使得高密度容器部署和高併發容器啟動對於無伺服器計算至關重要。例如,47% 的 Lambdas 在 AWS 上執行的最小記憶體規格是 128MB,在 Microsoft Azure 中,大約 90% 的應用程式的記憶體消耗從未超過 400MB。由於一個物理節點通常有很大的記憶體空間(如 384GB),它按理應該能夠部署大量的容器設計。同時,大量的函式呼叫可能會在短時間內到達。但是,安全容器的額外開銷大大降低了函式的部署密度和啟動容器的併發性。

圖1(b)顯示了安全容器的軟體棧層次結構。一般情況下,MicroVM 中的 guest 作業系統(GuestOS)和 host 上的資源排程都被轉交給雲提供商負責。rootfs 是一個檔案系統,充當使用者程式碼的執行環境。它是由 host 建立的,並傳遞給 microVM 中的容器執行時。在 host 端,cgroup 用於為安全容器分配資源,CPU 排程器負責管理資源分配。由此可見,安全容器的複雜層次結構必定會帶來了複雜、高額的額外開銷。

本文以 SOTA 的開源安全容器技術 Kata-containers 為出發點,透過深入分析從 host-to-guest 架構棧中的瓶頸點,提出了函式計算場景下安全容器高密高部署的 3 個關鍵觀察和挑戰:

  • 容器的檔案系統可以根據使用者映象只讀和不需要持久化的特點進行定製;

  • 客戶機中的作業系統基礎映像等可以在多個安全容器間共享和按需壓縮以降低記憶體開銷;

  • 高併發建立 cgroup 會導致高同步時延,尤其在高密場景下帶來的高排程開銷。

為此我們提出了 RunD — 超輕量級安全容器執行時,透過全棧的 host-to-guest 解決方案以提供高密度部署和高併發能力的支援。RunD 透過引入讀寫分離的高效檔案系統、預補丁和精簡的 guest 核心以及一個全域性可供維護的 lightweight cgroup 池以解決上述的三個高密高併發挑戰。

根據我們的評估,RunD 啟動到應用程式程式碼只需 88ms,並且每秒可以在一個節點上啟動 200 個安全容器。在一個擁有 384GB 記憶體的節點上,可以使用 RunD 部署超過 2500 個安全容器。目前 RunD 已經作為 Alibaba 的 Serverless 容器執行時,服務超過 100 萬個函式,每天呼叫近 40 億次。線上統計資料表明,RunD 使得每個節點的最大部署密度已經超過 2000 個容器,並支援超過 200 個容器建立的快速端到端響應。

二、背景

透過以上介紹,我們瞭解了 RunD 。那麼在本節中,我們將首先討論當前安全容器的設計,以及為何需要引入 RunD 的需求。

2.1 安全容器模型

根據不同級別的安全/隔離需求,目前 Serverless 的生產環境中通常有兩類主流的安全容器模型。圖2(a)顯示了”單虛擬機器多容器“的安全容器模型。在該模型中,一個虛擬機器(VM)承載著相同函式呼叫的容器,同一虛擬機器中的容器共享虛擬機器的 guest 作業系統。在這種情況下,對不同函式採用 VM 級別的隔離,但對同一函式的不同呼叫為 container 級別隔離。因為每個函式所建立和所需要的容器數量不同,該模型會導致潛在的記憶體碎片。雖然我們可以在執行時回收記憶體碎片,但這可能會嚴重影響函式效能,甚至在虛擬機器記憶體熱拔失敗時導致程式崩潰。

ATC'22頂會論文RunD:高密高併發的輕量級 Serverless 安全容器執行時 | 龍蜥技術

(圖2/兩種主流的安全容器模型隔離)

圖2(b)顯示了隔離每個函式呼叫的“單虛擬機器單容器”的安全容器模型。目前的無伺服器計算提供商主要使用這種安全容器模型。在此模型中,每個呼叫都在一個 microVM 中的容器中隔離並執行函式。這個模型沒有引入記憶體碎片,但是虛擬機器本身的記憶體開銷很大。很明顯,每個 microVM 都需要執行其專用的客戶作業系統,從而增加了記憶體佔用。安全容器依賴於硬體虛擬化和 VMM 的安全模型,透過系統呼叫檢查顯式地將 guest 核心視為不可信環境。在這種隔離和安全性的前提下,RunD 主要針對“單虛擬機器單容器”的安全容器模型。

2.2 Serverless 場景下的需求

在 Serverless 場景下如果應用安全容器進行隔離,天然的細粒度隔離帶來了新的複雜場景,我們需要滿足一個硬性需求和兩個衍生需求:

  • 高速:無伺服器計算的極致的自動擴充套件能力允許例項數量根據負載特性動態橫向擴充套件,建立速度要快,E2E 啟動時間達到百毫秒級。已有大部分工作致力於解決相關問題。但是還需要保證啟動時延的魯棒性,使得其不會明顯受到執行時干擾,比如當前併發數和部署密度的影響。

  • 高頻:每天上百萬的例項建立量,上億次的函式呼叫,需要支撐每秒內容器的高併發建立。與此同時,新興的網際網路服務往往呈現波動的負載模式(如外賣軟體在中午和晚上負載高,其他時間段負載低),並出現突發負載。當負載爆發時,需要建立大量的容器。雖然目前可以透過一些技術,如預熱容器來緩解容器冷啟動,但爆發性負載依舊會非常容易的擊穿預熱容器池。對於無伺服器平臺來說,支援高併發啟動的能力至關重要。

  • 高密:大量容器共存於工作節點中,為保證極致彈性和資源利用率,需要考慮記憶體和 CPU 資源佔用問題。如果一個安全容器沒有任務隔離開銷,擁有 256GB 記憶體的節點可以託管 8×256 = 2048 個容器。如果沒有特別的最佳化,安全容器會明顯降低無伺服器計算中的部署密度。在相同的基礎設施下,增加部署密度可以極大地提高資源利用率和多租戶服務效率。

三、問題分析和見解

在本節中,我們將分析使用安全容器實現高併發啟動和高密度部署的問題。

使用 Kata 容器作為安全容器執行時的代表來執行以下研究。

ATC'22頂會論文RunD:高密高併發的輕量級 Serverless 安全容器執行時 | 龍蜥技術

圖3/併發啟動Kata容器的步驟。併發瓶頸點在於建立rootfs(紅色塊步驟1)和建立cgroups(紅色線步驟3)密度瓶頸點在於MicroVM的高額記憶體開銷(藍色塊步驟2)和大量cgroups的排程維護開銷(藍色塊步驟3)

圖 3 顯示了啟動 Kata 容器的步驟。首先,containerd 併發地建立容器執行時 Kata-runtime 並準備 runc 容器的 rootfs。其次,hypervisor 載入 GuestOS 和準備好的 rootfs,在 microVM 中啟動 runc 容器。第三,函式工作負載被下載到容器中,然後開始執行。

與啟動傳統容器相比,在啟動安全容器時,我們有兩個觀察結果。

  • 當併發啟動 100 個或更多 Kata 容器時,在準備 Kata 執行時建立 rootfs 和 cgroup 會有明顯的效能下降。這種降低導致啟動容器的併發性較低。

  • 當在一個 384GB 記憶體和 104 個核的節點上部署超過 1000 個 128MB 記憶體規格的 Kata 容器時,虛擬機器的記憶體佔用(guest核心和rootfs)已經佔據了大部分記憶體空間。同時,容器的 I/O 效能也會嚴重下降。

圖 3 還顯示了我們發現的導致上述兩個結果的三個瓶頸。一般來說,建立 rootfs 和 cgroup 的低效會導致低容器啟動併發。較高的記憶體佔用和排程開銷導致較低的容器部署密度。我們將在下面的小節中分析各個瓶頸點。

3.1 容器的 rootfs 的高時空開銷

在 kata-runtime 中,容器的 rootfs 主要可以選擇 9pfs、devicemapper、virtio-fs 三種方案。9pfs 和 virtio-fs 屬於檔案級別的方案,devicemapper、屬於塊裝置層方案。

為了選擇合適的高併發場景下的容器rootfs方案,我們首先需要對比 9p、devicemapper 和 virtio-fs 方案之間的效能,主要分為 buffer I/O 和 direct I/O 在順序讀、順序寫、隨機讀、隨機寫和隨機讀寫幾個方面,關注 IOPS 和頻寬資料。另外也測試了 runc 在 host 上使用 ext4+overlayfs 儲存環境的資料作為對比。

ATC'22頂會論文RunD:高密高併發的輕量級 Serverless 安全容器執行時 | 龍蜥技術

(圖4/使用不同rootfs實現的隨機和順序讀、寫的IOPS和頻寬效能)

對比上述三種方案,我們首先摒棄基於 9p 方案的高密高併發容器架構,原因是由於其 mmap 操作上 POSIX 語義相容性問題和效能較差。然後,我們分別測試了基於 devicemapper 和 virtio-fs 的方案效能,並發現兩種方案各自的問題:

  • 在預設配置下(即writeback),devicemapper 的隨機/順序 寫 效能較好。但是在高併發場景下建立、刪除慢。在沒有 I/O 壓力情況下,建立一個device mapper thin-lv 需要 30-40ms 的時間,在大 I/O 壓力下,建立時間可能會到秒級甚至 10s級,造成建立 snapshot 失敗和容器建立失敗。高密場景下記憶體開銷高。devicemapper需要為每一個 microvm 準備 rootfs.img,但同時又不支援 host/guest 共享 volume 功能,於是 virtio-blk backend 讀取 host上rootfs image 檔案會產生這個檔案的 host pagecache;在 guest 裡再讀取這個 block 裝置內容時,在 guest 內部也會產生一次同樣內容的pagecache,就形成了 double pagecache。

  • virtio-fs 是專門為 KATA 場景設計的方案,有著良好的效能和完善的 POSIX 語義相容性。virtio-fs 在使用 DAX 的情況下,dax+virtio-fs 各項隨機/順序讀效能指標都很好,但是在高密高併發場景下寫效能很差,同時 virtiofsd 承載容器讀寫的壓力時,會造成 virtiofsd 在 host 上過高的 CPU 使用率。所以不適合大 I/O 壓力場景,和大量小檔案或者 metadata 操作的場景。

總結下來,我們可以得出這樣的見解:在現有 kata 架構和儲存方案下,任何單一儲存方案不適用於高密高併發的場景。需要將容器 rootfs 的儲存變為讀寫分離模式,其中:讀方面,可以使用 dax+virtio-fs 方案能夠在保證效能的前提下,共享 host/guest 之間 page cache,降低記憶體開銷。寫方面,應用現有基於device mapper的高開銷方案需要降低可寫層裝置的建立時間,以及解決 double pagecache 的問題。基於 virtio-fs 的低效能方案需要演進至擴充套件到滿足大 I/O 壓力場景的前提,以及降低 host 上的 CPU 開銷。因此現有方案都不適用,需要尋找替換方案。

3.2 單 MicroVM 的高記憶體空間開銷

在高密下單個例項資源直接影響部署容器數量。高密部署下單個例項的除了提供給使用者需要的記憶體規格大小以外,其他元件的資源佔用往往是一個高額開銷。firecracker 在釋出時將記憶體佔用減少到 5MB,但這個僅僅是 firecracker VMM 的佔用,執行核心包括程式碼段、資料段等,以及核心管理記憶體的 struct page,啟動的 rootfs 等記憶體資源,不能只從 VMM 的記憶體佔用來評估例項的額外佔用。比如,透過啟動數個 AWS 128MB 的 lambda 函式實際的平均記憶體開銷是 71MB,傳統的 QEMU 帶來的記憶體開銷則更大,達到了 145MB。

ATC'22頂會論文RunD:高密高併發的輕量級 Serverless 安全容器執行時 | 龍蜥技術

(圖5/Kata安全容器使用不同hypervisor的平均記憶體開銷)

目前 state-of-the-art 的方式是使用模版來解決 double page cache 以實現高密部署。一般情況下同一臺物理機上每一個安全容器例項的 guest 核心都是相同的,模版利用 mmap 技術替代檔案 IO 操作,將核心檔案直接對映到 guest 記憶體中。這樣,guest 核心就可以按需載入記憶體,並且其 text/rodata segment 可以在多個 kata 例項中進行共享,並且模板中沒有被訪問到的內容也不會被載入到實體記憶體中,安全容器例項的平均記憶體的消耗自然就會大大減少。

然而,由於作業系統核心中的自修改程式碼,模板技術並沒有我們想象的那麼高效。自修改程式碼技術在執行時按需修改指令,Linux 核心在很大程度上依賴於自修改程式碼來提高啟動和執行時的效能,因此這樣會導致有許多本應是隻讀的記憶體依舊被 guest 核心修改。我們從一個模板啟動一個帶有 CentOS 4.19 核心的虛擬機器,來研究自修改程式碼的影響。當透過模版啟動一個 128mb 例項後,發現17M(18432K)的 geust kernel 程式碼和只讀資料,啟動過程中實際只訪問了不到 10M(10012K),但是在被訪問的 10M 資料中居然有 7M(7928K)多的資料被修改了。這個例子表明,當使用 mmap 來減少核心映像檔案的記憶體消耗時,自修改程式碼降低了共享效率。

因此,我們的第二個見解是由於 kernel 的自修改特性導致利用模版技術時,可供共享的記憶體數量有所減少。如果能解決利用模版時建立過程中 guest 核心啟動時自修改程式碼,我們還能節省更多記憶體以覆蓋大部分動態記憶體消耗。

3.3 cgroup 的高 CPU 時間開銷

Cgroup 是為資源控制和流程抽象而設計的。在 Serverless 場景下,函式呼叫的頻率顯示出很大的變化。在這種情況下,平臺會頻繁建立並回收相應的安全容器。例如,在我們的無伺服器平臺中,在一秒鐘內最多可以在一個物理節點上同時建立和回收 200 個容器。頻繁地建立和回收對主機的 cgroup 機制提出了挑戰。

當併發建立 2000 個容器時,我們測量 cgroup 操作的相關指標和效能圖如圖 6 所示。在實驗中,我們使用不同數量的執行緒來執行 cgroup 操作。圖6(a)顯示了容器建立延遲的累積分佈。單執行緒場景中, 容器啟動時建立 cgroup 並初始化所用的耗時約為 1ms,在總建立的 cgroup 數量相同,多執行緒並行的場景下,低併發啟動場景下能夠獲得少許效能提升。但在高併發場景下,與我們的直覺相反,儘管每個執行緒建立的 cgroup 變少了,但總耗時反而增加了,甚至不如單執行緒的連續操作的效果。

ATC'22頂會論文RunD:高密高併發的輕量級 Serverless 安全容器執行時 | 龍蜥技術

(圖6/併發建立2000個容器時的cgroup效能)

其中的原因是:cgroup 實現非常複雜,涉及 10 多類資源控制(cgroup subsys),核心為了簡化 cgroup 的設計引入了 cgroup_mutex ,css_set_lock、freezer_mutex、freezer_lock 等大鎖,使得 cgroup 相關的操作都是序列化的;在高併發場景下,導致了整個建立鏈路上的序列執行會拖慢併發建立的效率,增加長尾延遲。圖6(b)顯示了同時使用 10 個執行緒建立 2000 個 cgroup 的火焰圖。在圖中,紅色部分顯示“互斥鎖”被啟用。當 mutex 中預設使用了 osq_lock 樂觀鎖最佳化,在 cgroup 搶不到鎖的情況下會自旋一會兒,導致多執行緒場景大量消耗 CPU 資源,不能及時退出臨界區,從而影響正在執行的函式例項。

除此之外,cgroup 機制是為通用業務場景設計的,設計之初並沒有考慮到雲原生場景會如此大規模的使用它們 。在一個上千容器共存的faas節點中,cgroup 總數輕鬆突破 1 萬。在 CFS 排程器中,有遍歷各自 cgroup subsys 所有物件的行為。在高密場景下,系統中 cgroup 數量很多時,導致排程器中的熱點函式變為瓶頸,導致高 CPU 排程開銷。(佔整機的平均排程開銷的 7.6%)

針對上述發現,我們的第三個見解是,Serverless 場景下 host 上的 cgroup 存在高密高併發的瓶頸,需要:

1)降低 cgroup 中由於 mutex 鎖引入的“臨界區”大小,最好是消除;

2)同時精簡 cgroup 的功能,同時降低 cgroup 設計的複雜度。

四、RunD 設計和實現

上述分析揭示了在 host、MicroVM、guest 三層架構棧中實現高併發啟動和高密度部署的瓶頸。為此,我們提出了 RunD,一個整體的安全容器解決方案,解決了跨容器的重複資料、每個虛擬機器的高記憶體佔用和 host 端 cgroup 的高開銷問題。在本節中,我們首先展示 RunD 的總體設計,然後介紹每個元件的細節,以解決相應的問題。

4.1 總體架構及流程

在設計 RunD 時,我們得到了關於無伺服器執行時的一個關鍵啟示。在高密度和高併發的 Serverless 場景中,傳統VM中微不足道的 host 端開銷可能會造成放大效應,任何微不足道的最佳化都可以帶來顯著的好處。

ATC'22頂會論文RunD:高密高併發的輕量級 Serverless 安全容器執行時 | 龍蜥技術

(圖7/輕量級Serverless執行時RunD架構圖)

圖7顯示了 RunD 設計並總結了 host-to-guest 的全棧解決方案。RunD 執行時透過 virtio-fs 提供只讀層,使用 built-in storage 為 virtio-blk 建立一個非持久的讀寫層,並使用 overlayfs 將前者和後者掛載為最終的容器 rootfs,從而進行讀/寫分離。RunD 利用整合了精簡核心的 microVM 模板,並採用預處理的映象建立一個新的 microVM,進一步分攤了不同的 microVM 的開銷。在建立安全容器時,RunD 從cgroup 池繫結一個輕量級的 cgroup 進行資源管理。

基於上述最佳化,當使用 RunD 作為安全容器執行時,安全容器將按照以下步驟啟動:

第一步:一旦 containerd 接收到使用者呼叫,它將請求轉發給 RunD 執行時。

第二步:RunD 為虛擬機器 hypervisor 準備 runc 容器的 rootfs。rootfs 被分為只讀層和可寫層。

第三步:hypervisor 使用 microVM 模板建立所需的沙箱,並透過 overlayfs 將 rootfs 掛載到沙箱中。

最後一個輕量級的 cgroup 從 cgroup 池中被重新命名,然後繫結到沙箱上,管理資源使用。

4.2 高效 rootfs 讀寫分離

我們分析了容器 rootfs 中的高密高併發場景下各種方案的優點與缺點。microvm 中container rootfs 所面臨的根本問題,其實都是資料持久化需求所帶來的問題。傳統儲存需求和場景需要非常高的穩定性和資料一致性,也就是在任何情況下都不能丟資料,但資料持久化在函式計算這個場景下完全是不需要的,我們需要的只是一個臨時儲存,host 節點當機重啟我們只需要重新初始化並拉起新的容器,之前的資料都不再需要了。所以,我們完全可以放下這個負擔,並且充分利用好不需要持久化的臨時儲存這個特性。

為此我們把容器映象分成只讀和可寫兩層,那麼 containerd 的 snapshotter 應該只負責提供映象的只讀內容,可寫層資源應該屬於具有臨時儲存特性的 sandbox,把容器建立和可寫層裝置建立解耦開來。為了讓可寫層裝置後端檔案和 sandbox 也就是 rund 的生命週期繫結到一起,並且儘可能避免 double pagecache 的問題,我們使用了 xfs 檔案系統的 reflink 功能實現可寫層檔案的 CoW,同時利用了open-but-unlinked 檔案的特性,在建立sandbox的時候只需要做一次 reflink copy,開啟檔案之後 rund unlink 檔案,不需要做其他生命週期管理了,實現了 sandbox 退出後檔案會自動刪除。我們引入了的架構如下圖 8 所示:

ATC'22頂會論文RunD:高密高併發的輕量級 Serverless 安全容器執行時 | 龍蜥技術

(圖8/容器rootfs的讀寫分離實現)

  • 使用 overlay snapshotter,建立容器映象 rootfs,並透過 virtiofs 透傳給 VM,作為容器 rootfs 的只讀層。

  • 將把 reflink copy 後的檔案作為 virtio-blk 裝置的後端,將 reflink copy 後的可寫層檔案作為 block 裝置內建在 microvm 中作為臨時儲存,作為容器 rootfs 的可寫層。

  • 在 VM 內使用 overlayfs,virtiofs 作為 lower layer,reflink 檔案的掛載點作為upper layer,掛載成 overlayfs,作為容器最終的 rootfs。

透過 reflink 方式實現可寫層檔案的 CoW,並且由於是本質上是 block 裝置的方案,在讀寫效能上和 devicemapper 基本持平。與傳統基於塊裝置的 rootfs 實現方式對比,在 200 容器併發建立的場景下,reflink 方式的磁碟使用率顯著下降 (60% (4500 iops, 100MB/s) -> 20% (1500 iops, 8MB/s)),建立可寫層裝置的平均時間從 207ms 下降到了 0.2ms,足足下降了 4 個數量級!

4.3 核心精簡和程式碼自修改補丁

在 Serverless 的場景下,guest 核心中的許多功能是不必要且佔用記憶體的,因此我們可以在編譯的時候關閉這些選項。精簡 guest 核心時,我們服從以下原則:

  • 儘量降低記憶體消耗

  • 不損失 faas 場景需要的功能

  • 基本不影響執行時效能

透過 case by case 的精簡,使得記憶體開銷下降了約 16MB,核心檔案體積下降了約4MB。我們首先將精簡過的 kernel 和上節介紹的 container rootfs 儲存應用在模板技術中,然後再解決從模版啟動容器後核心自修改的問題。

我們發現核心程式碼段的自修改只存在於啟動時,在啟動後,這些程式碼段將不再會被修改,並且同一物理機中的所有 rund 例項一般都使用同一核心,它們初始化過程是完全相同的,因此它們修改後的程式碼段也是相同的。於是,我們便可以預先生成一個自修改程式碼段後的核心檔案(pre-patched kernel image),並且使用其替換原來的核心檔案,這樣,我們就能夠儘可能多地在不同安全容器的例項間共享核心檔案了。為了適配這種模式,我們也對核心進行了少量 hack 工作,修復了一些使用 pre-patched kernel image 時可能導致 kernel panic 的問題。

使用模版技術不僅可以降低單個實力的記憶體佔用實現高密部署,還非常適合應用於安全容器的高併發場景。因為它可以在某個特定時刻對一個已經啟動的安全容器建立快照,並將其作為其它安全容器的啟動模板,這樣我們就可以透過這個模板快速拉起多個安全容器。本質就是將每個安全容器啟動過程中的相同步驟的結果進行記錄,然後直接跳過相同步驟,直接基於該中間結果啟動安全容器。

4.4 輕量級 cgroup 和池化

在之前的小節中我們分析了在 host 中 cgroup 成為了容器在高密高併發下的瓶頸之一,自然的想法就是減少 cgroup 的數量和相關操作。我們的進一步研究揭示了兩個方面的最佳化機會:

  • cgroup 的建立和銷燬比較耗時(臨界區很大),且不能並行化。但是cgroup 重新命名是一個非常輕量化的操作,且不涉及全域性鎖。

  • Serverless 高密場景下,所需要的 cgroup subsys 的層次結構是確定的,同時一個單機上所能建立的最大函式例項數量是可以預先定義的。

為此,我們使用輕量級 cgroup (lightweight cgroup)管理每個 RunD 例項。如圖 9 所示,我們使 mount cgroup 時將所有的 cgroup subsys (cpu, cpuacct, cpuset, blkio, memory, freezer))聚合到一個 lightweight cgroup 上,這幫助 RunD 減少容器啟動時冗餘的 cgroup 操作,顯著減少了cgroup 和系統呼叫的總數。

ATC'22頂會論文RunD:高密高併發的輕量級 Serverless 安全容器執行時 | 龍蜥技術

(圖9/輕量級cgroup和池化設計)

同時我們維護一個 cgroup 資源池,將所有初始建立的 cgroup 標記為空閒;透過一個 mutex 保護空閒連結串列,建立一個函式例項時,從空閒連結串列中獲取 cgroup,並標記為使用;當容器啟動時將執行緒 attach 到對應分配的 cgroup 即可。當銷燬一個函式計算例項時,只需要 kill 對應的例項程式,不需要刪除 cgroup;將 cgroup 還會資源池,標記為空閒。需要注意的是 cgroup 資源池管理涉及同步和互斥問題,必須要保證這個臨界區很小,否則不能達到提高並行度的效果。

Lightweight cgroup 池去除了cgroup 的建立和初始化操作,同時利用cgroup rename 機制為新的例項分配 cgroup,極大的縮小了臨界區,提高並行性。透過這一簡單思想,cgroup 操作耗時大幅度降低,建立速度提升了 15 倍,達到了近 94% 的效能最佳化。

五、效能測試

5.1 併發度指標測試

ATC'22頂會論文RunD:高密高併發的輕量級 Serverless 安全容器執行時 | 龍蜥技術

ATC'22頂會論文RunD:高密高併發的輕量級 Serverless 安全容器執行時 | 龍蜥技術

(圖10/高併發建立場景下端到端時延、分佈、和CPU開銷)

併發效能總結:RunD 能夠在 88ms 內啟動一個單獨沙盒,並具備在 1 秒內同時啟動 200 個沙箱的併發能力,與現有技術相比,具有最小的延遲波動和 CPU 開銷。

5.2 單例項開銷和部署密度測試

ATC'22頂會論文RunD:高密高併發的輕量級 Serverless 安全容器執行時 | 龍蜥技術

(圖11/不同執行時的沙箱記憶體開銷(100密度下))

ATC'22頂會論文RunD:高密高併發的輕量級 Serverless 安全容器執行時 | 龍蜥技術

(圖12/不同密度下的平均記憶體攤銷(缺失點表示已達到最大部署密度))

高密部署總結:RunD 支援在記憶體為 384GB 的節點上部署超過 2500 個 128MB 記憶體規格的沙盒。且每個沙盒的平均記憶體佔用小於 20MB。

5.3 部署密度對併發度的影響測試

ATC'22頂會論文RunD:高密高併發的輕量級 Serverless 安全容器執行時 | 龍蜥技術

(圖13/在不同密度下的併發效能(10c/200c表示10併發/200併發,Delta代表更高密度帶來的開銷增量))

高密下高併發能力總結:在高密度部署下,RunD 在支援高併發建立方面表現出了更好的效能和穩定性。

總結

根據實驗評估,RunD 可以在 88 毫秒內啟動,並且在 104 核 384GB 記憶體的單節點上每秒啟動超過 200 個安全容器,高密部署超過 2500 個函式例項。RunD 作為阿里雲的 Serverless 執行時已經部署上線,每天為超過 100 萬個函式和近 40 億的呼叫提供服務,並積極推進 Kata 社群 3.0 架構的演變。

—— 完 ——


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

相關文章