當前位置:
首頁 > 最新 > 解密容器運行時

解密容器運行時

容器運行時(container runtime) 是一個既熟悉又陌生的話題。在過去的一年裡,隨著Kubernetes的進一步發展,以及CNCF和OCI 在標準化方向的努力,市面上可供選擇的容器運行時也不再只是Docker一家了。容器運行時是什麼? 市面上有哪些可選的運行時?各有什麼優缺點? 在這篇文章里,你都會得到答案。

我們在之前關於KubeCon + CloudNativeCon概述的文章中曾經簡要提到 現在市面上已經有多個容器「運行時」(container runtime)。容器運行時的定義是:能夠基於在線獲取的鏡像來創建和運行容器的程序。 容器運行時領域的標準和實現正在慢慢地走向成熟: Docker在KubeCon上發布了containerd 1.0, 幾個月前CRI-O 1.0也發布了, 同時還有rkt也是一個運行時。 如果你正打算從0開始設計部署一個自己的容器系統或者Kubernetes集群的話, 面對這麼多的容器運行時,你或許會感到有點迷茫。這篇文章我就會嘗試解釋什麼是容器運行時,它們是做什麼的,比較它們的特點,然後告訴你該如何選擇適合自己的。 同時本文也會介紹容器規範和標準化方面的基礎知識。

容器是什麼?

在我們探索容器運行時之前,讓我們先來看看容器到底是什麼。當一個容器被啟動時,主要會發生如下活動:

這些概念最先是在Docker的標準容器宣言[1]中描述的, 但是後來被從Docker裡面刪除了。儘管如此,標準化的努力一直在持續。Open Container Initiative(OCI)現在通過如下的一些規範涵蓋了上面所述的大部分內容:

這些標準在不同項目裡面的實現是不同的。比如Docker除了鏡像格式之外的東西基本都是兼容的。 Docker早在標準化之前就有了自己的鏡像格式,而且它也承諾未來很快會轉換到新標準上。容器的運行時介面的實現也是不同的,因為Docker裡面並不是所有的東西都是標準化的,我們接下來會說明。

Docker和rkt的故事

因為Docker是第一個流行的容器,我們就從它開始說起。最先,Docker使用的是LXC但是層次隔離不太完整,所以後來Docker開發了libcontainer,最後演變為了runC。接下來容器迎來了爆發,而Docker也成為容器部署的事實標準。2014年Kubernetes誕生了,它很自然地使用了Docker,因為Docker是當時唯一的容器運行時。但是,Docker是一家很有野心的公司,一直在獨立開發新的功能和工具。比如Docker Compose,與Kubernetes 同時發布了1.0,而這兩個項目有很多的重複的東西。儘管我們使用Kcompose這樣的工具可以讓它們兩個互相交互,但是Docker給大家的感覺是:這個項目太大,做了太多的事情。 這導致CoreOS發布了一個簡單,獨立的運行時rkt。 當時rkt這樣描述的:

Docker現在正在構建工具做了很多事情,比如啟動雲伺服器,建集群系統,還有一堆的功能:構建鏡像,運行鏡像,上傳下載,甚至做覆蓋網路,最後都編譯進一個大的運行時裡面,當作你伺服器裡面的root程序。標準化容器宣言早就被它刪除了。我們應該停止談論Docker容器,開始談論Docker平台,因為它已經不再是那個我們曾經希望的,用起來簡單的構建容器的基礎組件了。

rkt的一個創新是通過appc規範來標準化容器格式,這我們在2015年曾經提到過[2]。CoreOS一直都沒有一個容器運行時介面的完整的標準的實現。到現在為止,rkt的Kubernetes兼容層(rktlet)並沒通過Kubernetes的所有集成測試,仍然還在開發中。CoreOS CTO Brandon Philips在一封郵件裡面這麼說:

rkt初始是支持OCI image規範的, 但是有些地方不全。在當前支持OCI還不是那麼重要,因為在容器倉庫方面對OCI的支持才剛開始,而且Kubernetes裡面這部分也是沒有的。在rkt裡面,OCI標準的運行時規範沒有使用,消費,也沒有被處理。這是因為rkt是基於pod語義的,而容器運行時規範針對的是單個容器的執行。

儘管如此,在Red Hat容器團隊的主管Dan Walsh在一封郵件訪談中說道:在Kubernetes生態系統中的容器標準化部分,CoreOS功不可沒: 「沒有CoreOS我們可能就沒有CNI, CRI可能還在OCI裡面艱難抗爭, CoreOS的成就在市場上沒有得到應有的認可」。 確實如此, Philips也說 「CNI項目和很多規範最開始都來自於rkt,最後我們將它捐給了CNCF。 CNI現在仍然在rkt中廣泛使用, 無論是內部還是用戶配置裡面」。 當前,CoreOS已經把重心轉向了構建自己的Kubernetes平台(Tectonic)和鏡像發布服務(Quay),而不是在運行時這個層面競爭。

CRI-O 最小的運行時

見到這些新標準以後,Red Hat的一些人開始想他們可以構建一個更簡單的運行時,而且這個運行時僅僅為Kubernetes所用。這樣就有了「skunkworks」項目,最後定名為CRI-O, 它實現了一個最小的CRI介面。在2017 Kubecon Austin的一個演講[3]中, Walsh解釋說,「CRI-O被設計為比其他的方案都要小,遵從Unix只做一件事並把它做好的設計哲學,實現組件重用。」

根據Red Hat的CRI-O開發者Mrunal Patel在研究裡面說的, 最開始Red Hat在2016年底為它的OpenShift平台啟動了這個項目,同時項目也得到了Intel和SUSE的支持。CRI-O與CRI規範兼容,並且與OCI和Docker鏡像的格式也兼容。它也支持校驗鏡像的GPG簽名。 它使用CNI的包來做網路部分,支持CNI插件,OpenShift也用它來做軟體定義存儲層。 它支持多個CoW文件系統,比如常見的overlay,aufs,也支持不太常見的Btrfs。

但是CRI-O最出名的特點是它支持「受信容器」和「非受信容器」的混合工作負載。比如,CRI-O可以使用Clear Containers做強隔離,這樣在多租戶配置或者運行非信任代碼時很有用。這個功能如何集成進Kubernetes現在還不太清楚,Kubernetes現在認為所有的後端都是一樣的。

CRI-O有一個有趣的架構(見下圖,摘錄於slides[4]),它重用了很多基礎組件,比如runC來啟動容器,使用containers/image和containers/storage 軟體庫來拉取容器鏡像,創建容器的文件系統(這2個庫是為skopeo項目創建的)。還有一個名為oci-runtime-tool的庫來準備容器配置。CRI-O引入了一個新的deamon來處理容器,名字為conmon。 根據Patel所說,conmon程序是「純C編寫的,用來提高穩定性和性能」,conmon負責監控,日誌,TTY分配,以及類似out-of-memory情況的雜事。

conmon需要去做所有systemd不做或者不想做的事情。但是即使CRI-O不直接使用systemd來管理容器,它也將容器分配到sytemd兼容的cgroup中,這樣常規的systemd工具比如systemctl就可以看見容器資源使用情況了。因為conmon(不是CRI daemon)是容器的父進程,它允許CRI-O的部分組件重啟而不會影響容器,這樣可以保證更加平滑的升級。現在Docker部署的問題就是Docker升級需要重起所有的容器。 通常這對於Kubernetes集群來說不是問題,但因為它可以將容器遷移來滾動升級。

CRI-O是OCI標準的實現裡面第一個通過所有Kubernetes集成測試的(拋開Docker本身)。Patel通過一個CRI-O支撐的Kubernetes集群展現了這些功能。Dan Walsh在一篇博客[5]裡面解釋了CRI-O與Kubernetes交互的方式:

與其他容器運行時不同,我們的第一目標是永遠不弄壞Kubernetes。為Kubernetes提供穩定可靠的容器運行時是CRI-O的唯一任務。

根據Patel所說,CRI-O現在性能與一個常規的Docker部署對比已經不相上下,但是開發團隊正在進一步優化性能實現超越。CRI-O 提供Debian和RPM的包,並且類似minikube、kubeadm這樣的部署工具也都支持切換到CRI-O。在現有的集群上,切換運行時相當的直接明了:只需要一個環境變數來改運行時的socket(Kubernetes用它來與運行時溝通)。

containerd:Docker帶API的運行時

當Redhat在做OCI的實現時,Docker也在朝標準努力,他們創建了另一個運行時,containerd。 這個新的Daemon是對Docker內部組件的一個重構以便支持OCI規範,比如執行,存儲,網路介面管理部分等。它在Docker1.12中就是一個特性了,但是直到containerd 1.0發布時才完成, 並會成為Docker 17.12版本的一部分(Docker已經採用年+月的方式做版本號)。雖然我們稱containerd為運行時,但是它不是直接實現CRI介面,而且是由一個叫cri-containerd的獨立daemon來實現。所以containerd需要的daemon比CRI-O要多(5個, CRI-O 3個)。在寫此文的時候,cri-containerd還是beta版本,但是containerd已經在眾多的生產環境中以Docker的形式被使用了。

在KubeCon的Node SIG會議上, Stephen Day 如此表述containerd: 它被設計為一組松耦合組件的緊核心。 與CRI-O不同,containerd可以通過一個Go API支持Kubernetes生態系統以外的工作負載。不過這個API現在還沒穩定,但是containerd已經定義了一個清晰的發布流程[6]來更新API和命令行工具。與CRI-O類似的是,containerd本身功能齊全,並通過了Kubernetes的所有測試,但是它無法與systemd的cgroup互操作。

項目的下一步是開發更多測試,在內存使用以及延遲上提高性能,他們也在努力提高穩定性。他們準備提供Debian和RPM的包以方便安裝, 並與minikub和kops集成等。 他們也計劃與Kata Containers更平滑的集成;runC已經能在基礎集成方面被Kata替換,但是cri-containerd的集成還沒完全實現。

互操作性與默認項

所有以上這些運行時選項給社區裡面帶來了相當大的困惑。在KubeCon上,應該使用哪個runtime被反覆問到。 Kubernetes很可能從Docker轉到別的運行時,因為它不需要Docker提供的所有功能,人們擔心這種轉換會帶來兼容性的問題,因為新的運行時並不是實現了Docker一模一樣的介面。比如日誌文件,在CRI標準裡面就是不同的。有些程序直接監控Docker socket,相關操作也不是符合標準的, 在新的運行時裡面這些實現方法會不同,甚至完全沒有實現。這些都可能導致在切換到一個不同的運行時的情況下會有風險。

Kubernetes會切換到哪個運行時(如果它確定要改),這個問題也是開放的。這也直接導致了運行時之間的競爭。在KubeCon上這個問題甚至帶來了一些爭議,因為在CNCF的keynote裡面CRI-O都沒被提到。Vicent Batts, Red Hat的一名高級工程師因此在Twitter上發表不滿:

簡直有毛病,KubeCon的keynote上提到了containerd和rktlet這些CRI實現,但是根本隻字未提CRI-O,而CRI-O是Kubernetes項目中的,已經1.0並已經在生產上使用了。

當我諮詢他相關細節時,他解釋到:

健康的競爭是好的,問題在於不健康的競爭。CNCF應該更好管理好自家的項目而不是迫於壓力吹捧某些項目比其他的好。

Batts補充道, Red Hat可能正處於一個臨界點,一些應用可能開始以容器部署而不是RPM,安全擔憂(也就是安全補丁的跟蹤,在容器格式的包中是缺乏的)是這種轉換的一個阻礙。 通過Atomic項目,Red Hat看來正轉向容器為核心,但是對於Linux發行版卻有大的風險。

當我在KubeCon上問 CNCF的COO Chris Aniszczyk時,他解釋說CNCF現在的政策時優先營銷頂級項目:

類似於CRI-O,Helm的項目一定程度上是Kubernetes的一部分,因此我們可以說它們也是CNCF的一部分。我們只是沒有像我們的頂級項目那樣重點營銷而已,因為頂級項目都已經通過了CNCF TOC的標準。

他補充到, 「我們希望提供幫助,我們聽到了反饋,也計劃在2018著手解決」, 同時他建議的一個解決方法是CRI-O申請畢業[7]成為CNCF的一個頂級項目。

在一個container運行時的發布會上,Philips解釋到,社區會在取得共識後作出決定哪個運行時會成為Kubernetes的預設項。他把運行時比作瀏覽器,說可以把容器的OCI標準比做HTML5或者javascript標準:這些標準通過不同的實現得到進化發展。 他重申這種競爭是健康的,表明有更多的創新。

現在寫這些新的運行時的很多人當初都是Docker的貢獻者:Patel是OCI實現的最初維護者,後來這個實現成為了runC;Philips在啟動rkt項目前也是Docker的核心開發者。這些人與Docker開發者積極合作,標準化介面,都希望看到Kubernetes穩定和提高。如Patrick Chazenon, Docker Inc所說,「目標是為了讓容器運行時成熟穩定,讓人們厭煩在它上面做創新。「 發布會上開發者都為他們的成就感到高興和自豪:他們成功的創建了一個容器的互操作性規範,而且規範還在成長。

2018:融合與標準化仍將持續

現在容器標準化領域的火熱話題已經從運行時轉向了鏡像發布(例如,容器倉庫),很可能在圍繞Docker的發布系統產生出一個標準。也有一些工作正跟蹤Linux內核的更新,比如cgroups v2。

實際情況是,每個運行時有它自己的優點:containerd有一個API,所以它可以被用來構建自己的自定義平台;CRI-O只是一個僅針對Kubernetes的簡單運行時。Docker和rkt在另一個層面上,提供的東西比運行時要多: 比如他們也提供構建容器的方式,發布推送到倉庫的方式等。

現在大多數的共有雲基礎架構仍然使用Docker做運行時。實際上甚至CoreOS也用Docker而不是rkt在自家的Tectonic平台上。根據Philips所說,這是因為「我們的客戶依賴docker engine為核心的持續集成系統。相比其他的Kubernetes產品,它是被測試的最充分的。 如果其他的容器運行時提供給Kubernetes更大的提高的話,Tectnoic可能會考慮用它。

在此刻containerd和CRI-O都還是非常年輕的項目,尤其要考慮到每個項目在今年都引入了大量的新代碼。接下來,它們需要通過與生態系統中的第三方集成測試達到成熟:比如日誌、監控、安全等等。

Philips在博客中深入解釋了CoreOS的位置:

目前,CRI對於Kubernetes的主要優點是更好的代碼組織和kubelet裡面更多的代碼覆蓋率,這樣給代碼更高質量,也比以前更加深入徹底的測試。儘管如此,對於幾乎所有的部署來說,我們期望Kubernetes社區在短期內仍然使用Docker Engine。

在發布會的討論上,Patel也說了類似的話,"我們不需要Kubernetes用戶知道什麼是運行時"。說來也是,只要它工作正常,用戶根本不用關心。 而且OpenShift,Tectonic 等平台都把運行時的決策抽象化了,它們都會自動選他們自己的最佳預設項來滿足用戶需求。所以Kubernetes到底選用哪個運行時作為預設的這個問題對於開發者來說根本不用操心,只要他們在一個共識的標準里工作就行。在充滿衝突的世界裡面,看到這些開發者一起開誠布公的工作本身就是難得的了。


喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!


請您繼續閱讀更多來自 Docker 的精彩文章:

Kubernetes存儲系統介紹及機制實現

TAG:Docker |