雲計算

從零開始入門 K8s | Kata Containers 創始人帶你入門安全容器技術

0.png

作者 | 王旭  螞蟻金服資深技術專家

本文整理自《CNCF x Alibaba 雲原生技術公開課》第 28 講,點擊直達課程頁面

關注“阿里巴巴雲原生”公眾號,回覆關鍵詞“入門”,即可下載從零入門 K8s 系列文章 PPT。

一、緣起:安全容器的命名

Phil Karlton 有一句名言:“計算機科學界只有兩個真正的難題——緩存失效和命名。”

對我們容器圈而言,我相信「命名」絕對配得上這句話。這毫無疑問是一件讓老開發者沉默、讓新人落淚的事情。僅就係統軟件而言,我們當今比較通行地稱為「Linux 容器技術」這個概念,它曾經用過的名字還有 Jail, Zone, Virtual Server, Sandbox 等。同樣,在早期虛擬化的技術棧裡也把一類虛擬機叫做容器,畢竟這個詞本身就指代那些用來包容、封裝和隔離的器物。它實在太過常見了,以至於以嚴謹著稱的 Wikipedia,它的詞條叫做「OS-Level Virtualization」(系統級虛擬化) ,從而回避了「什麼是容器」這個問題。

在 2013 年,Docker 問世之後,容器這個概念伴隨著不可變基礎設施、雲原生這一系列概念在隨後的幾年間以摧枯拉朽之勢顛覆了基於“軟件包+配置”這種細粒度組合的應用部署,用簡單的聲明式策略和不可變的容器就清爽地定義了軟件棧。應用怎麼部署,在這兒似乎有點離題了,我在這裡想要強調的是:

“雲原生語境下的容器,實質是「應用容器」——是以標準格式封裝的,運行於標準操作系統環境(常常是 Linux ABI)上的應用打包——或運行這一應用打包的程序/技術。”

這個定義是我下的,但它並不是我的個人意志,是基於 OCI 規範這一共識寫出來的。這個規範規定了容器之中應用被放到什麼樣的環境下、如何運行,比如說容器的根文件系統上哪個可執行文件會被執行,是用什麼用戶執行,需要什麼樣的 CPU,有什麼樣的內存資源、外置存儲,還有什麼樣的共享需求等等。

所以說,標準格式的封裝、標準的操作系統環境在一起以應用為中心就構成了應用容器的打包。

以這個共識為基礎,就可以來說說安全容器了。當年,我和我的聯合創始人趙鵬使用「虛擬化容器」這個名字來命名我們的技術的,不過為了博人眼球,我們用了「Secure as VM, Fast as Container」這樣的 Slogan,於是,被容器安全性問題戳中心坎的人們立刻用「Secure Container」或者說「安全容器」來稱呼這種東西了,一發而不可收。雖然在我們的內心裡,這個技術是一層額外的隔離,它只是安全中的一環,但是呢,用戶還是願意用安全容器這個名字來稱呼它。我們給安全容器下的定義就是:

安全容器是一種運行時技術,為容器應用提供一個完整的操作系統執行環境(常常是 Linux ABI),但將應用的執行與宿主機操作系統隔離開,避免應用直接訪問主機資源,從而可以在容器主機之間或容器之間提供額外的保護。

這就是我們的安全容器。

二、間接層:安全容器的精髓

說安全容器的時候,就要提到「間接層」這個詞。它出自於 Linus Torvalds 在 2015 年的 LinuxCon 上提出的:

“安全問題的唯一正解在於允許那些(導致安全問題的)Bug 發生,但通過額外的隔離層來阻擋住它們。”

為了安全,為什麼要引入隔離層呢?其實 Linux 本身這樣的規模是非常大的,無法從理論上來驗證程序是沒有 Bug 的,於是,一旦合適的 Bug 被利用,安全性風險就變成安全性問題了。安全性的框架和修補並不能確保安全,所以我們需要進行一些額外的隔離來減少漏洞以及因為這些漏洞造成的被徹底攻破的風險。

這就是安全容器的由來。

三、Kata Containers:雲原生化的虛擬化

2017 年 12 月,我們在 KubeCon 上對外發布了 Kata Containers 的安全容器項目,這個項目有兩個前身:由我們之前開始的 runV 以及 Intel 的 Clear Container 項目。這兩個項目都是 2015 年 5 月開始開展的,實際上是早於 Linus 在 KubeCon 2015 說的那番話的。

它們的思路都很簡單:

  • 操作系統本身的容器機制沒法解決安全性問題,需要一個隔離層;
  • 虛擬機本身,VM,它是一個現成的隔離層,比如說像阿里雲、AWS,它們都使用了虛擬化技術,所以對於全世界來說,大家已經普遍地相信,對於用戶來說,只要能做到「secure of VM」,那這個安全性就可以滿足公有云的需求了;
  • 虛擬機中如果有個內核,就可以支持我們剛才所提到的 OCI 的定義,也就是說提供了 Linux ABI 的運行環境,在這個運行環境中跑一個 Linux 應用不太難實現。

現在的問題是虛機不太夠快,阻礙了它在容器環境中的應用,如果能擁有「speed of container」的話,那我們就可能可以有一個用虛擬機來做隔離的安全容器技術了。這個也就是 Kata Containers 本身的一個思路,就是用虛擬機來做 Kubernetes 的 PodSandbox。在 Kata 裡面被拿來做 VM 的先後有 qemu, firecracker, ACRN, cloud-hypervisor 等。

下圖就是 Kata Containers 怎麼去和 Kubernetes 集成的,這裡的例子用的是 containerd,當然 CRI-O 也是一樣的。

1.png

目前,Kata Containers 通常是在 Kubernetes 中使用。首先 Kubelet 通過 CRI 接口找到 containerd 或者 CRI-O,這個時候比如鏡像這樣的操作一般也是由 containerd 或者 CRI-O 來執行的。根據請求,它會把 runtime 部分的需求變成一個 OCI spec,並交給 OCI runtime 執行。比如說上圖上半部分中的 kata-runtime,或者說下半部分精簡過後的 containerd-shim-kata-v2。具體的過程是這樣的:

  • 當 containerd 拿到一個請求的時候,它會首先創建一個 shim-v2。這個 shim-v2 就是一個 PodSandbox 的代表,也就是那個VMM 的代表;
  • 每一個 Pod 都會有一個 shim-v2 來為 containerd/CRI-O 來執行各種各樣的操作。shim-v2 會為這個 Pod 啟動一個虛擬機,在裡面運行著一個 linux kernel,也就是圖裡面的 Guest kernel。如果這個裡面用的是 qemu 的話,我們會通過一些配置和一些補丁,讓它變得小一些。同時這個裡面也沒有額外的 Guest 操作系統,不會跑一個完整的像 CentOS, Ubuntu 這樣的操作系統;
  • 後我們會把這個容器的 spec 以及這個容器本身打包的存儲,包括 rootfs 和文件系統,交給這個 PodSandbox。這個 PodSandbox 會在虛機中由 kata-agent 把容器啟動起來;
  • 依照 CRI 語義和 OCI 規範,在一個 Pod 裡面是可以啟動多個相關聯的容器的。它們會被放到同一個虛擬機裡面,並且可以根據需求共享某些 namespace;
  • 除了這些之外,其它的一些外置的存儲和卷也可以通過熱插拔的方式來插到這個 PodSandbox 裡面來;
  • 對於網絡來說,目前使用 tcfilter 就可以無縫地接入幾乎所有的 Kubernetes 的 CNI 插件。而且我們還提供了一個 enlightened 的模式,這樣的話會有一個特製的 CNI 插件來提高容器的網絡能力。

2.png

可以看到,在我們的 PodSandbox 裡面,實際上只有一個 Guest Kernel 跑著一些容器本身的打包和容器應用,並不包含一個完整的操作系統。就是說,這個過程,它用起來並不像是傳統的虛擬機,對於容器來說,它只有容器的引擎,並且通過少用不必要的內存、共享能共享的內存來進一步地降低內存的開銷。

與傳統的虛擬機比起來,開銷更小、啟動更輕快,對於大部分的場景來說,它可以做到「secure as VM」、「fast as container」。同時,在安全性技術以外,相比傳統的虛機,它有更多的彈性,更少了機器的那種物理操作的手感,比如說這裡面說過的包括動態資源的插拔以及使用 virtio-fs 這樣的技術等。它是一個專門為我們這種場景、為像 kata 這樣的場景來做的一個把 host 的基本文件系統的內容(比如說容器的 rootfs )共享給虛擬機的這樣一個技術。

通過其中一些之前為非易失存儲、非易失內存來做的 DAX 的技術,能夠在不同的 PodSandbox 之間,也就是不同的 Pod 之間、不同的容器之間,共享一些可以共享的只讀的內存部分。這樣可以在不同的 PodSandbox 之間去節省很多的內存。同時所有的 Pod 的管理都是通過 Kubernetes 從外部進行的容器管理,並且從外部來獲取 metrics 和 debug 信息,並沒有登陸虛擬機這樣一種手感。所以它看起來是一種非常容器化的操作,雖然從底層來看,它還是一個虛擬機,但是實際上它是一個面向雲原生的虛擬化。

四、gVisor:進程級虛擬化

gVisor,我們又把它叫做進程級的虛擬化,它是和 kata 不一樣的另外一種方式。

在 2018 年的 5 月份,哥本哈根的 KubeCon 上,Google 開源了他們內部開發了 5 年的 gVisor 安全容器作為對 kata containers 的迴應,表明了他們有一種不同的安全容器的解決方案。

3.png

如果說 Kata Containers 是通過對現有的隔離技術進行組合和改造來構建容器間的隔離層的話,那麼 gVisor 的設計顯然是更加簡潔的。

如上圖右側所示,它是一個用 Go 語言重寫的運行在用戶態的操作系統內核,這個內核的名字叫做 sentry,它並不依賴於虛擬化和虛擬機技術,相反,它是藉助一個它們內部叫做一個 Platform(平臺)的能力,讓宿主機的操作系統做一個操作,把應用所有的期望對操作系統的操作都轉交給 sentry 來進行,sentry 做處理之後會把其中的一部分交給操作系統來幫它完成,大部分則由自己來完成。

gVisor 是一個純粹的面向應用的隔離層,從一開始就不是一個完全等同於虛擬機的東西,它就是用來在 Linux 上面跑一個 Linux 程序的。作為一個隔離層,它的安全性依據在於:

  • gVisor 的開發者們首先要把攻擊面變小,宿主機的操作系統將只為沙箱裡的應用提供大約 20% 的系統調用;

Linux 大概有 300 多個 Syscall,實際上 sentry 最後向操作系統發起的調用只會集中在 60 多個 Syscall 上。這個是源於 gVisor 的開發者們對操作系統的安全做了一些研究,他們發現,大多數對操作系統的成功的攻擊都是來自於不常用的系統調用的。

這個很容易理解,因為不常用的系統調用,它的實現路徑一般都是比較老的路徑,也就是說這些部分的開發一般不是太積極,只有很少的開發者來維護,那些熱門路徑上的代碼要更安全一些,因為那些代碼被 review 的次數比較多。所以 gVisor 的設計就是讓應用對那些並不常用的 Syscall 的訪問根本就到不了操作系統層面,而只在 sentry 裡就把它處理掉。

從 sentry 訪問宿主機的,只使用那些被驗證過的、比較成熟、比較熱的路徑上的系統調用,這樣的話,安全性就會比原來看起來好很多。我們現在 Syscall 是原來的 1/5,但是被攻擊的可能性是並不到 1/5 的。

  • 其次,他們發現,一些經常被攻擊的系統調用需要把它隔離出來,比如 open(),就是打開文件的那個操作;

在 Unix 系統裡面,大部分東西都是一些文件,所以 open 可以做太多的事情了,大部分的攻擊都是通過 open 來進行的。gVisor 的開發者就單獨地把 open 放到了一個獨立的進程裡面去實現,這個進程叫做 Gofer。一個獨立的進程實際上是更容器被 seccomp、被一些系統的限制、一些 "capbility drop" 來保護。Gofer 可以做更少的事情,可以用非 root 去執行,如此一來整個系統的安全性就被進一步地被提高了。

  • 最後,sentry 和 Gofer 都是用 Go 語言來實現的,不是用傳統的 C 語言實現的。

Go 語言本身是一個內存更安全的一個實現,因此整個 gVisor 就更不容易被攻擊,更不容易發生一些內存上的問題。當然,Go 語言在有些地方還是不夠太系統級的,gVisor 的開發者也坦言,他們為了做這件事情,也對 Go Runtime 做了很多調整,並把這些東西也反饋回給了 Go 語言的社區。

可以說 gVisor 的架構很漂亮,有很多開發者跟我坦誠,他們其實很喜歡 gVisor 的架構,覺得這個更簡單、更純粹、更乾淨。當然了,雖然它的架構很漂亮,但重新實現一個內核這件事情也只有 Google 這樣的巨頭能做得出來,類似的可能還有微軟的 WSL 1。而且這個設計是比較超前的,它其實存在一些問題:

  • 首先,sentry 並不是 Linux,所以在兼容性方面與 kata 這樣的方案比起來還是有一定的差距的。這個沒有辦法,但是對於特定應用來說,這個可能並不是問題;
  • 其次,對於當前的系統調用的實現方式,還有 CPU 的指令系統來說,我們從應用去攔截 Syscall,再把這個 Syscall 送給 sentry 去執行,這個過程本身是有相當大的開銷的。在一定場景之下,gVisor 是可以有更好的性能的。但是,在大部分的場景之下,gVisor 的性能仍然是比不上 kata 這樣的解決方案的。

所以短時間之內,gVisor 這樣的解決方案並不能成為一個終極的解決方案,不過它可以適應一些特定的場景,並且它也帶來一些啟示性。我覺得這個啟示性對未來的操作系統、CPU 指令集的發展都可能會有一些作用。而且我相信,在未來,不管是 kata 還是 gVisor,都會有一個演進,我們期待著最後會有一個公共的解決方案來統一地解決應用的執行問題。

五、安全容器:不止於安全

安全容器的名字雖叫安全,但是它提供的是一個隔離性。它的作用是不止於安全的。

安全容器通過隔離層讓應用的問題——不管是來自於外部的惡意攻擊還是說意外的錯誤,都不至於影響主機,也不會在不同的 Pod 之間相互影響,所以實際上,這個額外的隔離層,它所帶來的影響不只是安全,還有其它的方面。它對於系統的調度、服務質量,還有應用信息的保護都是有好處的。

我們說傳統的操作系統容器技術是內核進程管理的一個延伸,容器進程本身是一組相關聯的進程,對於宿主機的調度系統來說,它是完全可見的,一個 Pod 裡的所有容器或進程,同時也都被宿主機調度和管理。這就意味著,如果你有一個大量容器的環境,宿主機本身內核的負擔就會很重,在很多實際環境中已經可以觀察到這個負擔帶來的開銷了。

尤其是現在計算機技術的不斷髮展,一個操作系統會有大量的內存,大量的 CPU,幾百 G 的內存都是可以見到的。在這個情況下,如果分配的容器數量很多,調度系統就會有非常沉重的開銷。在採納安全容器之後,在宿主機上就看不到這些完整的信息了,這個隔離層同時承擔了一些對隔離層上面應用的調度,於是在主機上面就只需要調度這些沙箱本身,降低了宿主機的調度開銷,這也就是它為什麼會提高調度效率的原因。

提高調度效率的同時,它會把所有的應用彼此隔離起來,這樣就避免了容器之間、容器和主機之間的干擾,提高了服務質量。從另外一個方向來看,我們做安全容器的初衷是為了保護宿主機不受到容器內惡意或者有問題的應用的影響,反過來,作為一個雲來說,我們有可能會面對有惡意的攻擊,所以也是保護我們自己。

同時用戶也不願意讓我們過多地去訪問用戶的資源,用戶需要使用資源,但它並不需要我們看到它的數據。安全容器可以把用戶運行的東西完全封裝在容器裡,這樣的話可以讓主機的運維管理操作並不能訪問到應用的數據,從而把應用的數據保護在沙箱裡,不需要去碰到用戶數據。如果我們要訪問用戶數據,作為一個雲的話,那就必須得讓用戶給你授權,這個時候,用戶不確定你是不是有什麼惡意的操作,如果我們的沙箱封裝得很好的話,那也就不需要額外的對用戶授權的要求,這對於保護用戶的私密性是更好的。

當我們把目光看向未來的時候,可以看到,安全容器不僅僅是在做安全隔離,安全容器隔離層的內核相對於宿主機的內核是獨立的,專門對應用服務,從這個角度來說,主機和應用的功能之間實際上是一個合理的功能分配與優化。它可以展現出很多的潛力,未來的安全容器,可能不僅僅是隔離性能開銷的降低,同時也是在提高應用的性能。隔離技術會讓雲原生基礎設施更加完美。

六、本文總結

本文的主要內容就到此為止了,這裡為大家簡單總結一下:

  • 現在,所謂“安全容器”是指一種容器運行時技術,為容器應用提供一個完整的操作系統執行環境(常常是 Linux ABI),但將應用的執行與宿主機操作系統隔離開,避免應用直接訪問主機資源,從而可以在容器主機之間或容器之間提供額外的保護;
  • Kata Containers 是一個使用虛擬化來提供隔離層的開源安全容器項目,完全兼容 Kubernetes 等雲原生生態系統,項目託管在OpenStack Foundation,由螞蟻金服和Intel共同領導;
  • gVisor 是一種利用進程級虛擬化技術實現的安全容器技術,由 Google 開發並開源,用 Go 語言實現了一個用戶態的兼容內核;
  • 最後,安全容器提供的隔離性不止是安全中的一環,也可以提供性能、調度、管理方面的隔離。

3 群直播海報.png

阿里巴巴雲原生關注微服務、Serverless、容器、Service Mesh 等技術領域、聚焦雲原生流行技術趨勢、雲原生大規模的落地實踐,做最懂雲原生開發者的公眾號。”

Leave a Reply

Your email address will not be published. Required fields are marked *