CRI容器執行時
CRI容器執行時
我們知道 Kubernetes 提供了一個 CRI 的容器執行時介面,那麼這個 CRI 到底是什麼呢?這個其實也和 Docker 的發展密切相關的。
在 Kubernetes 早期的時候,當時 Docker 實在是太火了,Kubernetes 當然會先選擇支援 Docker,而且是通過硬編碼的方式直接呼叫 Docker API,後面隨著 Docker 的不斷髮展以及 Google 的主導,出現了更多容器執行時,Kubernetes 為了支援更多更精簡的容器執行時,Google 就和紅帽主導推出了 CRI 標準,用於將 Kubernetes 平臺和特定的容器執行時(當然主要是為了幹掉 Docker)解耦。
CRI
(Container Runtime Interface 容器執行時介面)本質上就是 Kubernetes 定義的一組與容器執行時進行互動的介面,所以只要實現了這套介面的容器執行時都可以對接到 Kubernetes 平臺上來。不過 Kubernetes 推出 CRI 這套標準的時候還沒有現在的統治地位,所以有一些容器執行時可能不會自身就去實現 CRI 介面,於是就有了 shim(墊片)
, 一個 shim 的職責就是作為介面卡將各種容器執行時本身的介面適配到 Kubernetes 的 CRI 介面上,其中 dockershim
就是 Kubernetes 對接 Docker 到 CRI 介面上的一個墊片實現。
Kubelet 通過 gRPC 框架與容器執行時或 shim 進行通訊,其中 kubelet 作為客戶端,CRI shim(也可能是容器執行時本身)作為伺服器。
CRI 定義的 API(https://github.com/kubernetes/kubernetes/blob/release-1.5/pkg/kubelet/api/v1alpha1/runtime/api.proto) 主要包括兩個 gRPC 服務,ImageService
和 RuntimeService
,ImageService
服務主要是拉取映象、檢視和刪除映象等操作,RuntimeService
則是用來管理 Pod 和容器的生命週期,以及與容器互動的呼叫(exec/attach/port-forward)等操作,可以通過 kubelet 中的標誌 --container-runtime-endpoint
--image-service-endpoint
來配置這兩個服務的套接字。
不過這裡同樣也有一個例外,那就是 Docker,由於 Docker 當時的江湖地位很高,Kubernetes 是直接內建了 dockershim
在 kubelet 中的,所以如果你使用的是 Docker 這種容器執行時的話是不需要單獨去安裝配置CRI shim介面卡之類的,當然這個舉動似乎也麻痺了 Docker 公司。
建立流程:
- kubelet通過gRPC呼叫dockershim發起建立容器,CRI即容器執行時介面(container runtime interface),目前dockershim的程式碼內嵌在kubele中,所以接受建立容器的就是kubelet程序。
- dockershim把建立容器的命令轉換成docker daemon可以識別的命令,之後傳送給docker daemon建立容器。
- docker daemon在1.12版本之後就會把建立容器的命令分發給另一個程序: comtainerd。
- containerd收到建立容器的命令後,建立另一個程序:containerd-shim程序,由該程序執行具體的建立命令,containerd程序做為父程序存在。
- 建立容器的時候需要namespace隔離容器啟動和建立需要的資源,cgroup限制容器可以使用資源的大小等操作,這些事情該怎麼做已經有看公開的規範OCI(open container initivtive 開放容器標準),它的一個參考實現叫做runc。於是containerd-shim在這一步需要呼叫runc命令,來啟動容器。
- runc啟動容器之後就直接退出,containerd-shim則會成為容器程序的父程序,收集容器程序的狀態,上報給contanierd,並在容器種pid為1的程序退出後接管容器中國的子程序進行清理,確保不會出現殭屍程序。
其實我們仔細觀察也不難發現使用 Docker 的話其實是呼叫鏈比較長的,真正容器相關的操作其實 containerd 就完全足夠了,Docker 太過於複雜笨重了,當然 Docker 深受歡迎的很大一個原因就是提供了很多對使用者操作比較友好的功能,但是對於 Kubernetes 來說壓根不需要這些功能,因為都是通過介面去操作容器的,所以自然也就可以將容器執行時切換到 containerd 來。
切換到 containerd 可以消除掉中間環節,操作體驗也和以前一樣,但是由於直接用容器執行時排程容器,所以它們對 Docker 來說是不可見的。 因此,你以前用來檢查這些容器的 Docker 工具就不能使用了。
從上圖可以看出在 containerd 1.0 中,對 CRI 的適配是通過一個單獨的 CRI-Containerd
程序來完成的,這是因為最開始 containerd 還會去適配其他的系統(比如 swarm),所以沒有直接實現 CRI,所以這個對接工作就交給 CRI-Containerd
這個 shim 了。然後到了 containerd 1.1 版本後就去掉了 CRI-Containerd
這個 shim,直接把適配邏輯作為外掛的方式整合到了 containerd 主程序中,現在這樣的呼叫就更加簡潔了。
與此同時 Kubernetes 社群也做了一個專門用於 Kubernetes 的 CRI 執行時 CRI-O,直接相容 CRI 和 OCI 規範。這個方案和 containerd 的方案顯然比預設的 dockershim 簡潔很多,不過由於大部分使用者都比較習慣使用 Docker,所以大家還是更喜歡使用 dockershim
方案。
但是隨著 CRI 方案的發展,以及其他容器執行時對 CRI 的支援越來越完善,Kubernetes 社群在2020年7月份就開始著手從kebelet中移除內建的 dockershim 方案了。如果想用docker這種容器執行時,可以將 dockershim 的功能單獨提取出來獨立維護一個 cri-dockerd
即可,就類似於 containerd 1.0 版本中提供的 CRI-Containerd
,當然還有一種辦法就是 Docker 官方社群將 CRI 介面內建到 Dockerd 中去實現。
當 Kubernetes 不再內建支援開箱即用的 Docker 的以後,最好的方式當然也就是直接使用 Containerd 這種容器執行時,而且該容器執行時也已經經過了生產環境實踐的。