您好,登錄后才能下訂單哦!
作者 | 王旭 螞蟻金服資深技術專家
本文整理自《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 被利用,安全性風險就變成安全性問題了。安全性的框架和修補并不能確保安全,所以我們需要進行一些額外的隔離來減少漏洞以及因為這些漏洞造成的被徹底攻破的風險。
這就是安全容器的由來。
2017 年 12 月,我們在 KubeCon 上對外發布了 Kata Containers 的安全容器項目,這個項目有兩個前身:由我們之前開始的 runV 以及 Intel 的 Clear Container 項目。這兩個項目都是 2015 年 5 月開始開展的,實際上是早于 Linus 在 KubeCon 2015 說的那番話的。
它們的思路都很簡單:
現在的問題是虛機不太夠快,阻礙了它在容器環境中的應用,如果能擁有「speed of container」的話,那我們就可能可以有一個用虛擬機來做隔離的安全容器技術了。這個也就是 Kata Containers 本身的一個思路,就是用虛擬機來做 Kubernetes 的 PodSandbox。在 Kata 里面被拿來做 VM 的先后有 qemu, firecracker, ACRN, cloud-hypervisor 等。
下圖就是 Kata Containers 怎么去和 Kubernetes 集成的,這里的例子用的是 containerd,當然 CRI-O 也是一樣的。
目前,Kata Containers 通常是在 Kubernetes 中使用。首先 Kubelet 通過 CRI 接口找到 containerd 或者 CRI-O,這個時候比如鏡像這樣的操作一般也是由 containerd 或者 CRI-O 來執行的。根據請求,它會把 runtime 部分的需求變成一個 OCI spec,并交給 OCI runtime 執行。比如說上圖上半部分中的 kata-runtime,或者說下半部分精簡過后的 containerd-shim-kata-v2。具體的過程是這樣的:
可以看到,在我們的 PodSandbox 里面,實際上只有一個 Guest Kernel 跑著一些容器本身的打包和容器應用,并不包含一個完整的操作系統。就是說,這個過程,它用起來并不像是傳統的虛擬機,對于容器來說,它只有容器的引擎,并且通過少用不必要的內存、共享能共享的內存來進一步地降低內存的開銷。
與傳統的虛擬機比起來,開銷更小、啟動更輕快,對于大部分的場景來說,它可以做到「secure as VM」、「fast as container」。同時,在安全性技術以外,相比傳統的虛機,它有更多的彈性,更少了機器的那種物理操作的手感,比如說這里面說過的包括動態資源的插拔以及使用 virtio-fs 這樣的技術等。它是一個專門為我們這種場景、為像 kata 這樣的場景來做的一個把 host 的基本文件系統的內容(比如說容器的 rootfs )共享給虛擬機的這樣一個技術。
通過其中一些之前為非易失存儲、非易失內存來做的 DAX 的技術,能夠在不同的 PodSandbox 之間,也就是不同的 Pod 之間、不同的容器之間,共享一些可以共享的只讀的內存部分。這樣可以在不同的 PodSandbox 之間去節省很多的內存。同時所有的 Pod 的管理都是通過 Kubernetes 從外部進行的容器管理,并且從外部來獲取 metrics 和 debug 信息,并沒有登陸虛擬機這樣一種手感。所以它看起來是一種非常容器化的操作,雖然從底層來看,它還是一個虛擬機,但是實際上它是一個面向云原生的虛擬化。
gVisor,我們又把它叫做進程級的虛擬化,它是和 kata 不一樣的另外一種方式。
在 2018 年的 5 月份,哥本哈根的 KubeCon 上,Google 開源了他們內部開發了 5 年的 gVisor 安全容器作為對 kata containers 的回應,表明了他們有一種不同的安全容器的解決方案。
如果說 Kata Containers 是通過對現有的隔離技術進行組合和改造來構建容器間的隔離層的話,那么 gVisor 的設計顯然是更加簡潔的。
如上圖右側所示,它是一個用 Go 語言重寫的運行在用戶態的操作系統內核,這個內核的名字叫做 sentry,它并不依賴于虛擬化和虛擬機技術,相反,它是借助一個它們內部叫做一個 Platform(平臺)的能力,讓宿主機的操作系統做一個操作,把應用所有的期望對操作系統的操作都轉交給 sentry 來進行,sentry 做處理之后會把其中的一部分交給操作系統來幫它完成,大部分則由自己來完成。
gVisor 是一個純粹的面向應用的隔離層,從一開始就不是一個完全等同于虛擬機的東西,它就是用來在 Linux 上面跑一個 Linux 程序的。作為一個隔離層,它的安全性依據在于:
Linux 大概有 300 多個 Syscall,實際上 sentry 最后向操作系統發起的調用只會集中在 60 多個 Syscall 上。這個是源于 gVisor 的開發者們對操作系統的安全做了一些研究,他們發現,大多數對操作系統的成功的攻擊都是來自于不常用的系統調用的。
這個很容易理解,因為不常用的系統調用,它的實現路徑一般都是比較老的路徑,也就是說這些部分的開發一般不是太積極,只有很少的開發者來維護,那些熱門路徑上的代碼要更安全一些,因為那些代碼被 review 的次數比較多。所以 gVisor 的設計就是讓應用對那些并不常用的 Syscall 的訪問根本就到不了操作系統層面,而只在 sentry 里就把它處理掉。
從 sentry 訪問宿主機的,只使用那些被驗證過的、比較成熟、比較熱的路徑上的系統調用,這樣的話,安全性就會比原來看起來好很多。我們現在 Syscall 是原來的 1/5,但是被攻擊的可能性是并不到 1/5 的。
在 Unix 系統里面,大部分東西都是一些文件,所以 open 可以做太多的事情了,大部分的攻擊都是通過 open 來進行的。gVisor 的開發者就單獨地把 open 放到了一個獨立的進程里面去實現,這個進程叫做 Gofer。一個獨立的進程實際上是更容器被 seccomp、被一些系統的限制、一些 “capbility drop” 來保護。Gofer 可以做更少的事情,可以用非 root 去執行,如此一來整個系統的安全性就被進一步地被提高了。
Go 語言本身是一個內存更安全的一個實現,因此整個 gVisor 就更不容易被攻擊,更不容易發生一些內存上的問題。當然,Go 語言在有些地方還是不夠太系統級的,gVisor 的開發者也坦言,他們為了做這件事情,也對 Go Runtime 做了很多調整,并把這些東西也反饋回給了 Go 語言的社區。
可以說 gVisor 的架構很漂亮,有很多開發者跟我坦誠,他們其實很喜歡 gVisor 的架構,覺得這個更簡單、更純粹、更干凈。當然了,雖然它的架構很漂亮,但重新實現一個內核這件事情也只有 Google 這樣的巨頭能做得出來,類似的可能還有微軟的 WSL 1。而且這個設計是比較超前的,它其實存在一些問題:
所以短時間之內,gVisor 這樣的解決方案并不能成為一個終極的解決方案,不過它可以適應一些特定的場景,并且它也帶來一些啟示性。我覺得這個啟示性對未來的操作系統、CPU 指令集的發展都可能會有一些作用。而且我相信,在未來,不管是 kata 還是 gVisor,都會有一個演進,我們期待著最后會有一個公共的解決方案來統一地解決應用的執行問題。
安全容器的名字雖叫安全,但是它提供的是一個隔離性。它的作用是不止于安全的。
安全容器通過隔離層讓應用的問題——不管是來自于外部的惡意攻擊還是說意外的錯誤,都不至于影響主機,也不會在不同的 Pod 之間相互影響,所以實際上,這個額外的隔離層,它所帶來的影響不只是安全,還有其它的方面。它對于系統的調度、服務質量,還有應用信息的保護都是有好處的。
我們說傳統的操作系統容器技術是內核進程管理的一個延伸,容器進程本身是一組相關聯的進程,對于宿主機的調度系統來說,它是完全可見的,一個 Pod 里的所有容器或進程,同時也都被宿主機調度和管理。這就意味著,如果你有一個大量容器的環境,宿主機本身內核的負擔就會很重,在很多實際環境中已經可以觀察到這個負擔帶來的開銷了。
尤其是現在計算機技術的不斷發展,一個操作系統會有大量的內存,大量的 CPU,幾百 G 的內存都是可以見到的。在這個情況下,如果分配的容器數量很多,調度系統就會有非常沉重的開銷。在采納安全容器之后,在宿主機上就看不到這些完整的信息了,這個隔離層同時承擔了一些對隔離層上面應用的調度,于是在主機上面就只需要調度這些沙箱本身,降低了宿主機的調度開銷,這也就是它為什么會提高調度效率的原因。
提高調度效率的同時,它會把所有的應用彼此隔離起來,這樣就避免了容器之間、容器和主機之間的干擾,提高了服務質量。從另外一個方向來看,我們做安全容器的初衷是為了保護宿主機不受到容器內惡意或者有問題的應用的影響,反過來,作為一個云來說,我們有可能會面對有惡意的攻擊,所以也是保護我們自己。
同時用戶也不愿意讓我們過多地去訪問用戶的資源,用戶需要使用資源,但它并不需要我們看到它的數據。安全容器可以把用戶運行的東西完全封裝在容器里,這樣的話可以讓主機的運維管理操作并不能訪問到應用的數據,從而把應用的數據保護在沙箱里,不需要去碰到用戶數據。如果我們要訪問用戶數據,作為一個云的話,那就必須得讓用戶給你授權,這個時候,用戶不確定你是不是有什么惡意的操作,如果我們的沙箱封裝得很好的話,那也就不需要額外的對用戶授權的要求,這對于保護用戶的私密性是更好的。
當我們把目光看向未來的時候,可以看到,安全容器不僅僅是在做安全隔離,安全容器隔離層的內核相對于宿主機的內核是獨立的,專門對應用服務,從這個角度來說,主機和應用的功能之間實際上是一個合理的功能分配與優化。它可以展現出很多的潛力,未來的安全容器,可能不僅僅是隔離性能開銷的降低,同時也是在提高應用的性能。隔離技術會讓云原生基礎設施更加完美。
本文的主要內容就到此為止了,這里為大家簡單總結一下:
“ 阿里巴巴云原生關注微服務、Serverless、容器、Service Mesh 等技術領域、聚焦云原生流行技術趨勢、云原生大規模的落地實踐,做最懂云原生開發者的公眾號。”
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。