容器技術之容器引擎與江湖門派

阿里雲技術發表於2020-12-16

來來來,搬好小板凳我們繼續開聊容器技術。讀過本系列第一篇文章“容器技術之發展簡史”的讀者,可能已經理解了容器和雲原生的關係,以及容器技術恆等式:

我們今天先聊執行引擎,後續將有一篇關於容器映象的專題文章具體聊映象格式和映象加速。

從集裝箱革命說起

有一本非常有名的書,叫《集裝箱改變世界》,說的是看起來平淡無奇的鐵箱子,如何從二十世紀起永久性的改變了這個世界,並促進了全球化和全球分工。集裝箱的出現和發展是實體貨物包裝、運輸、交付方式的一次革命。

《經濟學家》雜誌曾經評價說“沒有集裝箱,不可能有全球化”。集裝箱為什麼具有革命性?經濟全球化的基礎就是現代運輸體系,而一個高度自動化、低成本和低複雜性的貨物運輸系統的核心就是集裝箱。集裝箱最大的成功在於其產品的標準化及由此建立的一整套運輸體系。能夠讓一個載重幾十噸的龐然大物實現標準化,並且以此為基礎逐步實現全球範圍內的船舶、港口、航線、公路、中轉站、橋樑、隧道、多式聯運相配套的物流系統,這的確堪稱人類有史以來創造的偉大奇蹟之一,而撬動這個系統的理念就是標準化和系統化

改變世界的不僅僅是集裝箱本身,還有一整套貨物處理的新方法,包括港口、貨船、起重機、卡車,還有發貨人的自身操作方式等。容器技術對於IT領域的意義非常類似於集裝箱,只是裡面裝載的不再是實體貨物,而是虛擬世界的二進位制程式碼和軟體。

製作集裝箱可以採用不同的材料與工藝流程,只需要符合相應的標準。同理,容器技術也有不同架構和實現,只要遵循相應的技術規範,就是合格的容器技術。在具體介紹容器執行引擎方案及技術路線之前,先上一張容器引擎技術江湖門派概覽圖,以饗讀者。

提到容器引擎(Container Engine)、容器執行時(Container Runtime)這些名稱,總是有點容易讓人犯暈。為了讓事情簡化一些,我們關注在雲原生、K8S場景下的定義吧。從CNCF Cloud Native Interactive Landscape可以發現,容器引擎領域是一個很活躍的技術領域,該領域的大致發展(戰爭)史如右下圖。

相對較為正式的術語定義如下圖,可以把容器管理系統分為三層:

  1. High-level Container Management:容器管控的UI層。直接實現容器的管控和使用介面,也是使用者最熟悉的子系統。
  2. High-level Container Runtime:容器狀態及資源供給。包括映象管理、網路接入、容器狀態、呼叫Low Level Runtime執行容器等功能。習慣上這層稱之為容器引擎(Container Engine)。
  3. Low-level Container Runtime:容器執行層。負責具體構建容器執行環境並執行容器程式。習慣上這層直接簡稱為容器執行時(Container Runtime)。

High-level Container Management和Container Engine之間的介面規範是CRI,Container Engine和Container Runtime之間的介面規範是OCI。

下文將分別從容器引擎(High-level container runtime)和容器執行時(Low-level container runtime)兩個緯度討論容器執行引擎相關技術。

容器引擎

容器引擎的核心是準備執行容器所需要的資源以及管理容器生命週期。在K8S生態圈中,容器編排系統通過CRI介面來呼叫容器引擎。支援CRI介面的容器引擎主要有docker、rkt、pouch、containerd和cri-o等,其中活躍度比較高的是containerd和CRI-O。

containerd

Containerd是從dockerd中抽離出來的容器管理的核心功能,是在社群影響下dockerd模組化的結果,也是現在最熱門的容器引擎。由於containerd是從dockerd演化出來的,使用介面是針對容器管理而設計,內建管理物件是Image和Container。K8S CRI的管控物件是Image/Pod/Container,為了讓containerd支援Pod物件以及實現CRI介面,引入了CRI-containerd元件來粘合Kubelet和containerd兩個子系統。現在cri-containerd元件已經作為一個功能模組內建到containerd。Containerd依賴後端container runtime來具體管理容器,最常見的container runtime是runC。

CRI-O

而CRI-O則是專門支援K8S CRI介面而設計實現的容器引擎,它拋棄了對docker的支援而專注於支援K8S CRI,所以架構相對containerd要簡潔不少。但是,雖然架構簡潔優雅,但是從實際使用體驗看CRI-O在成熟度、可擴充套件性方面相對cantainerd還是有一定差距。今年CRI-O也增加了對shimv2介面的支援,從而可以支援runC之外其它容器執行時,但是實際測試發現功能還不成熟。

進一步看CRI-O的架構,一個CRI實現需要實現的核心功能包括:映象管理(Image service)、執行管理(runtime service,管理Pod、container生命週期)、容器網路(CNI)和對接OCI相容的容器執行時。這兒有一個非常重要的細節,雖然cri-containerd和CRI-O都是同時實現CRI的image service和runtime service,但是CRI規範其實允許用不同的元件來分別實現image service和runtime service

容器執行時

runC是目前使用最廣泛的容器執行時,但是runC也不是完美的。業界對runC的擔心主要集中在runC的隔離能力,眾所周知runC容器共享一個host核心,利用cgroup和namespace機制來構建相互隔離的容器執行環境。Docker、LXC、RKT和runC這類原生容器都是基於共享主機作業系統的,這些技術的優點是資源利用率高、彈效能力強,缺點則是受攻擊面大和攻擊後果嚴重。特別是在多租戶的雲環境中,不同客戶的容器會被編排部署到同一個伺服器系統,這種威脅就變得尤其明顯了。

其實,容器引擎技術領域也存在類似CAP理論的三邊關係:資源效率、安全隔離和標準通用。目前所有的容器執行時技術最多都只能滿足資源效率、安全隔離和標準通用中的兩項,還沒有一個技術能做到同時滿足三項。比如:runC在資源效率和標準通用方面最強,但是在安全隔離方面卻最弱。Kata containers/firecracker-containerd/runD在安全隔離和標準通用上有優勢,但是在資源效率方面卻有不足。

基於共享核心的OS虛擬化技術總是讓人不放心,“安全容器”便應運而生了。安全容器的核心思路是摒棄共享核心,為每個Pod/Container例項配置一個專用的容器OS,再輔以硬體虛擬化、應用核心等技術以獲得更好的隔離性。我們可以按是否使用共享核心、是否使用硬體虛擬化、是否使用Linux Kernel作為容器核心幾個緯度來對容器執行時進行分類。從下面的分類表可以看出,基於硬體虛擬化+Linux分離核心是目前行業的主流技術路線

runC

runC的介紹,為了節省讀者的時間,此處省略一萬字。。。

Kata Containers

出於對傳統容器安全性的擔憂,Intel 在 2015 年啟動了它們以虛擬機器為基礎的容器技術:Clear Container。Clear Container 依賴 Intel VT 的硬體虛擬化技術以及高度定製的 QEMU-KVM(qemu-lite)來提供高效能的基於虛擬機器的容器。在 2017 年,Clear container 專案加入了 Hyper RunV,這是一個基於 hypervisor 的 OCI 執行時,從而啟動了 Kata 容器專案。Kata containers的核心思路是:

  1. 作業系統本身的容器機制沒辦法解決安全性問題,需要一個隔離層;
  2. 虛擬機器是一個現成的隔離層,雲服務已經讓全世界相信,對戶來說,"secure of VM" 是可以滿足需求的;
  3. 手機裡面只要有個核心,就可以支援 OCI 規範的語義,在核心上跑個 Linux 應用這並不太難實現;
  4. 虛機可能不夠快,阻礙了它在容器環境的應用,那麼可不可以擁有 "speed of container" 呢?

所以,Kata containers核心之一是把VM變得輕快穩,使之能滿足容器高密彈性的需求。同時,作為一個容器執行時,必須深度融入生態,支援相關規範。

Kata containers 最大的特點是它專注於實現一個開放的符合OCI標準的安全容器runtime實現,對於對接什麼樣的虛擬化方案,它抽象了一套hypervisor介面,如今已經對接了多種虛擬化實現,比如qemu、nemu、firecracker、cloud-hypervisor等。

2019年,Kata containers有個非常重要的技術進步,和containerd社群共同制定了shimv2介面規範,並率先在Kata containers支援了該規範。通過containerd-shim-v2和vsock技術,kata精簡了大量的元件,配合輕量級hypervisor和精簡核心,kata可以大幅降低記憶體開銷和容器啟動時間。更關鍵的是,降低系統部署複雜度還大幅提高了穩定性,特別是在系統過載情況下的穩定性。從實際使用體感看,阿里雲沙箱容器1.0從shimv1升級到shimv2後,穩定性得到大幅提升,缺陷數量大幅下降。一個技術,同時服務於“”、“”、“”三個目標,當之無愧重要的技術進步!

第二個熱點則是Kata containers 2.0架構。從2019年8月開始,阿里雲、螞蟻和intel三方共同推動Kata containers 2.0架構定義及設計,核心是進一步提升多租隔離能力及可觀測性。現在螞蟻可信原生團隊、阿里雲容器團隊和阿里雲作業系統團隊三方正在合力推進Kata containers 2.0架構。

回頭再聊聊Kata Containers和runC的關係吧!Kata Containers和runC不是替代與被替代,而是青出於藍而勝於藍。Kata Containers把runC一層基於OS虛擬化的隔離機制擴充套件為三層隔離:Guest OS 虛擬化(等效於runC),硬體虛擬化和Host OS虛擬化。所以Kata Containers通過引入更多的間接層來提升系統的隔離能力,但是需要付出“更多間接層”的代價。

Firecracker-containerd

Firecracker-containerd是firecracker和containerd的合體。Firecracker是AWS基於Google crosvm開發的、配合KVM使用的一個輕量級安全VMM,用以支援和實現MicroVM。Firecracker MicroVM 同時具備傳統虛擬機器的安全性和工作負載隔離能力以及容器的速度和資源利用率。Firecracker具有如下一些特色:

  1. 拋棄 QEMU 使用的 C 語言,選擇記憶體安全的 Rust 作為開發語言。
  2. 基於 crosvm 使用極簡裝置模型,模擬儘可能少的必要裝置,減小暴露的攻擊面。
  3. 高效能和低開銷:得益於極簡的裝置模型, Firecracker 取消了 SeaBIOS (開源的 X86 BIOS),移除了 PCI 匯流排,取消了 VGA 顯示等等硬體模擬,嚴格的說它甚至不是一臺完整的虛擬計算機。而 Firecracker 執行的 GuestOS 使用的也是 AWS 定製過的精簡 Linux 核心,同樣裁剪掉了對應的裝置驅動程式、子系統等等。因此叫它 MicroVM,其啟動步驟和載入項要遠遠少於傳統虛擬機器。因此 Firecracker 目前已經能提供小於125ms 的 MircroVM 啟動速度,每秒150臺的啟動能力,小於5MiB 的記憶體開銷,併發執行4000臺的極限承載容量(AWS i3.metal EC2 作為宿主機),以及熱升級能力等。這些都是傳統虛擬機器所遙不可及,但現代化彈性工作負載又有強烈需求的效能指標。

但是,Firecracker畢竟只是一個VMM,還必須配合上containerd才能支援容器生態。所以,AWS又開源了firecracker-containerd專案,用於對接K8S生態。本質上Firecracker-containerd是另外一個私有化、定製化的Kata containers,整體架構和Kata containers類似,只是放棄了一些相容性換取更簡化的實現。

gVisor

Google gVisor 是 GCP App Engine、Cloud Functions Cloud Run和 CloudML 中使用的沙箱技術,正式商用名稱是Google Sandbox。Google 意識到在公有云基礎設施中執行不受信容器的風險,以及虛擬機器沙箱的低效,因此開發了使用者空間的核心作為沙箱來執行不受信應用。gVisor是沿著libdune的系統呼叫攔截思路發展而來的使用者態核心或程式虛擬化技術。gVisor 通過攔截所有從應用到主機核心的系統呼叫,並使用使用者空間中 gVisor 的核心實現來處理這些呼叫。

本質上來說,gVisor 是 VMM 和客戶核心的組合,或者說gVisor是syscall虛擬化。基於lock-in-pop理論,假設Linux核心被高頻訪問的syscall是安全的,這部分可以開放給容器程式直接使用而不會導致嚴重的安全風險;對於冷僻Linux syscall則需單獨處理,要不就是sentry模擬實現,要不就是在一個專用受限環境中執行(gofer)。sentry實現了多數的 Linux 系統呼叫,尤其是核心功能,例如訊號分發、記憶體管理、網路棧以及執行緒模型。gVisor 和 Nabla 有很相似的策略:保護主機。它們都使用了不到 10%的系統呼叫來和主機核心通訊。gVisor 建立通用核心,而 Nabla 依賴的是 Unikernel,它們都是在使用者空間執行特定的客戶核心來支援沙箱應用的執行。

gVisor 還在嬰兒期,也一樣有一些限制。gVisor 要攔截和處理沙箱應用中的系統呼叫,總要有一定開銷,因此不適合系統呼叫繁重的應用。gVisor 沒有直接的硬體訪問(透傳),所以如果應用需要硬體(例如 GPU)訪問,就無法在 gVisor 上執行。最後,gVisor 沒有實現所有的系統呼叫,因此使用了未實現系統呼叫的應用是無法在 gVisor 上執行的。

出於對使用C語言開發系統軟體導致的安全風險和軟體缺陷的擔憂,Google開創了使用安全語言開發系統軟體的先河。2013年開發的gVisor選擇了Golang,2017年開源的crosvm則使用Rust。從目前狀態看,Rust更適合開發底層系統軟體,Golang則更適合開發上層便應用管理的系統軟體。也許Rust早成熟幾年gVisor就會有不同的選擇啦!

Nabla Containers

Nabla Containers的核心思想是用libOS作為容器執行時的隔離機制。通過增加一個隔離層,libOS kernel,把容器應用和host OS給隔離開來。nabla是繼承於unikernel的隔離方式,應用採用rumprun打包成一個unikernel映象,直接執行在一個專為執行unikernel定製虛擬機器(ukvm)中。應用直接打包首先可以降低很多核心態和使用者態轉換的開銷,另外通過ukvm暴露非常有限的主機上的syscall(只剩7個),可以大大縮小主機的攻擊面。它是這些安全容器實現中,最安全的。

這條技術路徑從學術理論角度看是很好的路線,但是libOS一二十年發展不起來是有原因的。它要求應用打包成unikernel映象,因此和當前docker的映象標準是不相容的。另外,unikernel應用在諸如支援建立的程式等一些常規操作上都有很難解決的問題。核心原因是應用侵入性沒法解決,需要修改應用來適配容器執行時,和K8S部分語義也是衝突。

最近Nabla containers專案發生了一個有意思的轉變,從採用rumpkernel切換到user model linux。估計背後的原因是rumpkernel已經有6年沒有更新了,背後的NetBSD活躍度也不高,Linux compatibility layer的質量有限。但是,即使切換到UML解決部分syscall ABI相容相容問題,這條路前途也很渺茫。

我的觀點是用libOS做容器執行時是條死路,我們在這個上面有血的教訓!2018年5月加入阿里雲就開始unikernel,基本思路和軟體架構和Nabla基本一摸一樣,rumpkernel + miniOS + ukvm + OCI介面實現。搞了四五個月之後rumpkernel + miniOS + ukvm的核心架構基本ready,但是當我們計劃支援OCI時,發現模擬Linux namespace、支援多程式難度太大,如果一步一步做下去最後就會做成另外一個簡化版的KVM + linux實現。進一步評估發現解決OS ABI相容或應用改造的代價都很大,所以這條技術路線不具有廣泛落地生產的可能性。所以,當IBM宣佈Nabla Containers專案時,我們已經基本放棄這條路線了。

Windows容器

受Windows作業系統的限制導致Windows容器天然有臃腫及生態的問題,所以Windows容器一直相對比較小眾。但是從軟體架構上看,Windows容器架構反而是一個非常優秀的案例,值得探討一番。Windows 2016開始支援Windows Containers,具有兩種形態:Windows Server Containers和Hyper-v Containers,其具體架構如下圖。

直觀的理解,Windows Server Container相當於runC,而Hypver-V Containers則相當於Kata Containers。也就是從誕生的一天開始,Windows就提供了兩種不同的Container Runtime,以提供不同的特性來滿足不同場景的需求。

具體到Windows Server Containers的架構細節,基本上和Linux容器技術類似,沒有太多的驚喜。核心支援容器技術的關鍵子系統名稱都是一致的:Control Groups、Namespaces、Layer Capabilities。比較特殊的是Windows核心子系統Host Compute Service實現了containerd + runc的能力,也就是說Windows核心內建支援容器物件。

為了更好地與K8S生態融合,Windows 2019在docker之外增加了對Containerd/CRI的支援,以及相關元件runhcs(run Host Computer Service,對應Linux runC)。Windows 容器技術整體架構升級為下圖的架構。至此,Windows容器通過支援OCI規範基本無縫融入了K8S生態。

Windows容器技術架構中有兩個很有意思的元件。一是Host Compute Service(hcs),這個是專門為容器服務的service。相對Linux直接暴露支撐容器的底層技術(cgroup,namespace,seccomp等),Windows選擇了對底層基礎技術進行一層封裝由核心直接容器暴露容器兌現而不暴露底層技術。阿里雲作業系統團隊內部也進行過類似的技術探討,Linux kernel是否應該內建支援container object,目前還沒結論。二是輕量化的Hyper-V虛擬機器,Microsoft基於輕量化的Hyper-V虛擬機器長出了Hyper-V container,Windows Subsystem for Linux 2和Windows Sandbox三個技術。最近Redhat基於rust-vmm搞的runK類似Windows Sandbox的思路,值得關注。

VMware Project Pacific

相對其它家的技術路線,VMware Project Pacific走了一條與眾不同的路線來融入K8S生態。Project Pacific沒有采用常規路線實現一個runP之類的容器執行時,而是選擇了全鏈路技術改造。Project Pacific通過spherelet、CRX等元件完全重構kubelet、containerd、runc等元件,直接對接到K8S API server。從下圖的架構可以看出,Project Pacific核心是在節點上實現容器和虛擬機器的混合部署,同時支援虛擬機器管理系統vCenter和容器編排系統K8S。

進一步展開Project Pacific節點級的架構細節。為了支援K8S容器生態,ESXi節點上增加了Spherelet、Image Service、Spherelet Agent等元件。ESXi上同時部署了hostd(相當於pync + libvirt)和Spherelet(相當於Kubelet)兩種管控系統以支援容器虛擬機器混合部署,同時還為容器專門部署了Image Service用以管理容器映象。

VMware Project Pacific和ECI使用場景和部署形態有很多共通之處,部分設計理念值得借鑑

Alibaba Cloud Sandbox - runD

敬請期待下回分解!

小結

結尾一張圖,功法全靠悟!

 

原文連結

本文為阿里雲原創內容,未經允許不得轉載。

相關文章