為 Kubernetes 選擇合適的 container runtime(譯)
原文地址:https://joejulian.name/post/kubernetes-container-engine-comparison
作者:Joe Julian
翻譯:狄衛華
發表時間: 2018/3/23
Kubelet 可通過配置項 container-runtime
, container-runtime-endpoint
和 image-service-endpoint
來選擇使用 docker,rkt(不建議使用)或任何 CRI API 相容的 container-runtime
。
意見
雖然基於虛擬機器的機制提供了額外的安全隔離,但資源限制太多,需要較多的開銷才能將軟體在生產環境上部署使用,但是為了方案的完整性,本文也涵蓋其相關的實現,但在整體解決方案章節將不再完整描述或分析。
Kubernetes 介面
Native
Docker
Docker 是迄今為止最常用的容器引擎,主要得益於其容器倉庫和社群。Docker 是 dockerd 、containerd、containerd-shim 和 runc 的組合,其中 runc 作為 container-runtime
。 由於 kubernetes 移除對於 container-runtime
的直接和非 CRI 介面的支援,因此 Native 支援的方式的後續將會被移除。
rktnetes
截至 kubernetes 1.10.0,直接整合 rkt(或 rktnetes)支援已被棄用。
CRI
容器執行時介面(CRI)的出現是因為 CoreOS 想要為 kubernetes 新增 rkt 支援。Kubernetes 初期提供了對於 docker 的支援,但在後續新增 rkt 支援的補丁後,程式碼變得醜陋務必,充斥著大量的 “if this do rkt else do docker”。為後續出現的 runtimes 新增支援將無法再繼續維護,基於此,CRI 誕生了。
CRI 是一個基於 protobuf 定義的 api,包括兩個 RPC/">gRPC 服務, ImageService
和 RuntimeService
。 ImageServic
e 提供 RPC 以從倉庫中提取映象,檢查和刪除映象。 RuntimeService
包含用於管理 pod 和容器生命週期的 RPC,也提供與容器互動的呼叫(exec/attach/port-forward)。
cri-containerd
cri-containerd
是一種向 containerd
新增 CRI 支援的服務, containerd
是由 Docker 建立並捐贈給 CNCF ,提供 runtime 管理和映象服務。後續 containerd
與 Docker 分離,將 runtime 管理器與 docker 其餘的工具分離,以利於不斷增長的容器管理工具生態系統在 docker api上實現標準化。這有助於 docker 社群的擴充套件並幫助在 docker 倉庫中提供豐富的容器映象。
cri-containerd 在kubernetes 1.9 中處於 beta 階段。
rktlet
rktlet
是一個使用 rkt 作為容器 runtime 的 Kubernetes 介面實現。
當 kubelet 請求建立一個 pod 時,rktlet 將啟動一個 systemd 服務,該服務將通過執行 rkt 二進位制檔案建立一個新的 rkt sandbox。 建立 sandbox 後,kubelet 可以請求為 pod 新增/刪除容器等。然後 rktlet 將使用相應的 rkt app 命令(app add,app rm,app start 或 app stop)執行 rkt 二進位制檔案。 其餘的 CRI 方法也通過執行 rkt 二進位制檔案來處理。
rktlet 在 kubernetes 1.9 中為 alpha 狀態。
cri-o
cri-o 是一個 CRI 實現,旨在為 kubernetes 提供 CRI 介面支撐。 cri-o 提供了一組最小的工具和介面來下載、提取和管理映象、維護容器生命週期、並提供滿足 CRI 所需的監控和日誌記錄。 它可以使用任何實現 OCI 執行時規範的 runtime,預設使用 runc。
cri-o 使用配置的 runtime(預設情況下為runc)建立容器 sandbox(pod),然後再次使用 runtime 在該 pod 中建立容器。
從kubernetes 1.9開始,cri-o 被認為是 stable 的。
frakti
Frakti 允許 Kubernetes 通過 runV 直接在虛擬機器管理程式中執行 pod 和容器 。
OCI (Open Container Initiative) 相容 Runtimes
Containers
bwrap-oci
bwrap-oci
是為容器工具 bubblewrap 提供 OCI 相容的包裝器,bubblewrap 是一個無特權的容器工具。作者為:Per Giuseppe Scrivano, bwrap-oci
不滿足e2e Kubernetes測試所需的許多功能,例如:無法指定繫結裝載的選項,也無法通過 cgroup 限制資源。
crun
crun
是用 C 編寫的 OCI 執行時規範實現。配合 cri-o,作者 Giuseppe Scrivano 用它來通過了完整的 e2e 套件測試。它比同類型功能中的任何其他工具更小更輕。
railcar
railcar 是 OCI runtime-spec 規範的 rust 語言實現。參考並實現了 runc 的部分功能。一般來說,railcar 與 runc 非常相似,但不支援某些 runc 命令。截至目前不受支援的命令列表為:checkpoint,events,exec,init,list,pause,restore,resume,spec。 railcar 始終執行與容器程序分開的 init 程序。 railcar 在其研發過程號稱發現了 OCI 執行時規範中的一些 ofollow,noindex" target="_blank">缺陷 。通過使用 rust 語言重寫 rust,作者能夠消除在 go 語言實現過程中對於 c 語言中介層的的需要。
我嘗試向編寫缺陷那篇文章的作者詢問了有關缺陷的具體內容,但是未得到回覆。
rkt
rkt 是一個在 LInux 上執行容器而編寫的 CLI 工具。它採用多層設計,允許 runtime 可根據實現者的需求更改 。 預設情況下,rkt 結合使用 systemd
和 systemd-nspawn
來建立容器。 systemd-nspawn 用於管理執行 systemd 以管理 cgroup 的名稱空間。容器應用程式作為 systemd 單元執行。通過使用不同與 “1階段” 映象,該工具可將執行應用程式的工具可以從 systemd 工具更改為其他任何工具。
rkt 包含與 runc 相同的功能,但是並不使用 OCI runtime-spec。相反,rkt 提供了一個命令列介面來提供類似的功能集。
runc
runc 是一個主要用 go 編寫的 CLI 工具(需要 c 語言的中介層 shim 來完成某些 go 不能完成的功能)。runc 是最受歡迎的 OCI runtime,被 containerd 和 cri-o 使用。
在大多數情況下,所有 runc
都會在生成程序時配置 namespace 和 cgroup。 這實際上就是是一個容器,包括 namespace 和 cgroups。
runc 依賴並跟蹤 OCI runtime-spec,以保證 runc 和 OCI 規範主要版本保持同步。這意味著 runc 1.0.0 實現了 OCI 1.0 版本的規範。
runlxc
runlxc
是阿里巴巴即將開源的 OCI 相容 runtime。 runlxc 目前尚未對外發布,與 pouch 配合使用。
Virtual Machines
Clear Containers
cc-runtime 相容 OCI runtime,其通過啟動一個 Intel VT-x 安全 [Clear Containers](https://github.com / clearcontainers / agent) 管理程式,而不是標準的Linux 容器。
runv
runv 是基於 hypervisor 的 OCI runtime,使用 KVM,Xen 或 QEMU 來執行 OCI 映象。它不會建立容器。
分析
技術
containerd
containerd 是 CRI 實現中最複雜的,提供映象和 runtime 服務所需的 3 個元件,採用 Go 編寫。
這些元件包含兩個獨立的專案 cri ,提供了 19,000 行 Go 程式碼和 containerd 提供 runtime 的守護程序,執行程式和中介層程式碼一共 112,000 行。 containerd 預設使用它自己的 runc 分支,但是可以配置任何 OCI runtime 規範相容的實現。
Docker/containerd 對 CRI 支援處於 beta 階段。
我無法在 Arch Linux 中編譯。由於時間有限,我沒有花很多時間在這上面。依據 README.md 中的構建指令無法成功完成 make install.deps
階段。
rkt
rkt 需要兩個元件,rkt 和 rktlet。 儘管如此,它並沒有遵循標準,而是以自己的方式做事情,儘管它的 github 描述說:“它是可組合的,安全的,並且建立在標準之上”。 它也是用 Go 編寫的。
rkt 元件是 rkt ,包含 71,000 行 Go 和 rktlet ,包含 4,000 行。
rktlet 對 CRI 支援是 alpha 版。
cri-o
坐在中間是 cri-o 。cri-o 為容器編排工具 Kubernetes 定製而生。 憑藉其聚焦的單一性,它很快就獲得了 “穩定” 狀態,僅用了 14,000 行 Go。 它與任何 OCI 執行時規範實現介面,預設為 runc。
cri-o 對 CRI 支援是穩定。
可用性
選擇 CRI 實現的一個明顯因素是可用性。 您應該能夠執行完整的 e2e 測試並能夠執行任何 OCI 容器。 如果出現問題,應該能夠快速診斷問題。
cri-o
cri-o 可以作為包裝安裝在大多數發行版中。 使用 cri-o 非常簡單。 只需更改 kubelet 標誌即可使用預設值以使用cri-o 套接字。可以應用其他配置來新增其他功能,例如 repo 限制,selinux,apparmor seccomp,映象簽名等。除錯問題很簡單,因為只有兩個部分,cri-o 守護程式和 conmon
控制檯監視器。
使用 runc 與kubernetes 1.9 我很容易通過完整的 e2e 測試。
cri-containerd
由於無法編譯,我無法測試其可用性。
rktlet
rktlet 可以在大多數發行版中作為包安裝。使用 rkt 可能需要一點學習曲線。有許多有效的方法來配置它,並且學習哪一個適合的用例並不是非常簡單。 rkt 使用 “階段”,並且很少有關於階段1 映象像用於什麼目的的文件。你 可以 建立自己的階段1 映象,但似乎有適當的文件可能會提供這種需要。
它只有三個部分,診斷問題通常非常簡單,因為它與 systemd 整合,將所有輸出留在日誌中。
在嘗試使用 rktlet 作為我的 CRI 提供程式時,某些容器不能夠正常工作。不幸的是,由於時間限制,所以我無法正確診斷。 我希望使用 rkt 就像更改 kubelet 標誌一樣簡單。
社群
作為開源社群的成員,社群的健康也很重要。 一個好的社群應該尋求積極參與,快速審查貢獻並有一個記錄良好的貢獻過程。 它應該有一個積極和有效的使用者群,在其中提出問題並獲得答案。
cri-o
自 2016 年 9 月 10 日以來已有 1354 Pull Requests,其中 23 個是 Open 的,最早的是 2017 年 9 月 19 日建立的,最後一次 Commit 是在 9 天前。 共有 75 名貢獻者,其中 20 人在上個月活躍。 過去 30 天提交的 75% 來自Red Hat 員工。 在過去的 30 天裡,已經添加了 5,141 行程式碼和 刪除了 694 行。 5,141 行代表了 37% 的程式碼變化。 cri-o 有一個積極的貢獻者基礎,其中大部分是紅帽員工,但有紅帽以外的員工。 拉取請求需要合併 4 天的平均值。 問題大致相同。有一個 IRC 頻道,開發人員在北美工作時間內回答使用者問題,響應時間約為 4 分鐘。
cri-containerd/containerd
自 2017 年 4 月 14 日以來,已有 484 次針對 containerd/cri 的 Pull Requests,其中 6 次 是 Open 的。最早的Open PR 創建於 2017 年 12 月 8 日,最後一次 Comment 時間是 2018 年 1 月 18 日。共有 31 位貢獻者,其中 8 位活躍於上個月。在過去的 30 天裡,IBM,Docker,中興通訊,谷歌和英特爾最終貢獻了很多。在過去的 30天裡,已經添加了 2,123 行程式碼和 刪除了 1,217 行。 2,123 行代表了 11% 的程式碼變化。對於 containerd /cri,並沒有一個非常活躍的貢獻者基礎,但是這代表了社群的一個更健康的體現。
自 2015 年 12月 7 日起,共向 containerd/containerd 提交了1,662 個 Pull Requests,其中 13 個是 Open 狀態。 最早的 Open PR 創建於 2017 年 8 月 24 日,最後一次 Commit 於 2017 年 9 月 19 日。共有 120 名貢獻者,其中 12人在上個月活躍。 過去 30 天中有 46% 來自 Docker 員工,NTT 和 IBM 分別增加了31%。 在過去的 30 天裡,已經添加了 4,444 行程式碼 和刪除了 1,724 行。 4,444 行代表 3% 的程式碼變化。 對於 containerd,有一個非常活躍的貢獻者基礎,但它仍然是多樣化的。
獲得對這兩者之一的支援需要註冊為 docker 社群成員。
rktlet/rkt
自 2014 年11月13 日起,共 2 ,406 次 Pull Requests,其中 49 份是開放的。 最舊的 Open PR 創建於 2015 年 8 月25 日,最後一次 Commente 記錄時間是 2016 年 8 月 9 日。我認為 rkt 維護者可以做得更好。 接受的 PR 通常在一週內進行稽核和合並。 共有 195 名貢獻者,上個月沒有任何活動。 rkt 的活動急劇下降。 我不確定這是不是一個活躍的專案。
在看了 rkt 的活躍狀況後,我選擇忽略了繼續檢視 rktlet 社群的健康狀況。
總結
標準和其相關的 apis 定義提供了非常大的靈活性,並有助於防止鎖定在特定的累積技術債中。通過使用 OCI(Open Container Initiative) 容器和 rumtime 標準以及 Kubernetes 的 CRI(Container Runtime Interface )API,可以選擇演進任何任何特定選項而不用更整個堆技術棧。
為此,應該棄用 kubernetes 原生支援的 docker 機制,並且只應使用 CRI。
基於 hypervisor 的解決方案,我並沒有在選擇繼續分析,因為在本文章編寫的時候,runlxc 尚未開源。
對於 CRI 介面,rktlet/rkt 沒有長期的社群支援,而且經過了很長的時間才演變成 beta 狀態;cri-containerd 並沒有提供一個良好的構建過程,也沒有發行版的包裝,我無法測試。cri-containerd 仍然只是一個 beta 版本,獲得支援有障礙。cri-o 相對穩定,快速,輕鬆地構建,而且更適合在生產環境穩定執行,並且具備非常好的社群支援。我的結論是,cri-o 是目前最好的選擇。
對於 runtime,雖然 railcar 和 crun在技術上可能更優越,但我沒有時間測試它們。由於它們是可替代品,因此預先為此工具選擇最佳技術並不重要。我建議使用當前預設的 runc,直到可以徹底評估它們為止。