Kubernetes容器運(yùn)行時(shí)棄用Docker轉(zhuǎn)型Containerd

一、背景

Kubernetes社區(qū)在2020年7月份發(fā)布的版本中已經(jīng)開始了dockershim的移除計(jì)劃,在1.20版本中將內(nèi)置的dockershim進(jìn)行分離,這個(gè)版本依舊還可以使用dockershim,但是在1.24中被刪除。從1.24開始,大家需要使用其他受到支持的運(yùn)行時(shí)選項(xiàng)(例如containerd或CRI-O);如果選擇Docker Engine作為運(yùn)行時(shí),則需要使用cri-dockerd

二、Docker

從 Docker 1.11 版本開始,Docker 容器運(yùn)行就不是簡單通過 Docker Daemon 來啟動(dòng)了,而是通過集成 containerd、runc 等多個(gè)組件來完成的。雖然 Docker Daemon 守護(hù)進(jìn)程模塊在不停的重構(gòu),但是基本功能和定位沒有太大的變化,一直都是 CS 架構(gòu),守護(hù)進(jìn)程負(fù)責(zé)和 Docker Client 端交互,并管理 Docker 鏡像和容器。現(xiàn)在的架構(gòu)中組件 containerd 就會(huì)負(fù)責(zé)集群節(jié)點(diǎn)上容器的生命周期管理,并向上為 Docker Daemon 提供 gRPC 接口。

Docker架構(gòu)

當(dāng)我們要?jiǎng)?chuàng)建一個(gè)容器的時(shí)候,現(xiàn)在 Docker Daemon 并不能直接幫我們創(chuàng)建了,而是請求 containerd 來創(chuàng)建一個(gè)容器,containerd 收到請求后,也并不會(huì)直接去操作容器,而是創(chuàng)建一個(gè)叫做 containerd-shim 的進(jìn)程,讓這個(gè)進(jìn)程去操作容器,我們指定容器進(jìn)程是需要一個(gè)父進(jìn)程來做狀態(tài)收集、維持 stdin 等 fd 打開等工作的,假如這個(gè)父進(jìn)程就是 containerd,那如果 containerd 掛掉的話,整個(gè)宿主機(jī)上所有的容器都得退出了,而引入 containerd-shim 這個(gè)墊片就可以來規(guī)避這個(gè)問題了。

然后創(chuàng)建容器需要做一些 namespaces 和 cgroups 的配置,以及掛載 root 文件系統(tǒng)等操作,這些操作其實(shí)已經(jīng)有了標(biāo)準(zhǔn)的規(guī)范,那就是 OCI(開放容器標(biāo)準(zhǔn)),runc 就是它的一個(gè)參考實(shí)現(xiàn)(Docker 被逼無耐將 libcontainer 捐獻(xiàn)出來改名為 runc 的),這個(gè)標(biāo)準(zhǔn)其實(shí)就是一個(gè)文檔,主要規(guī)定了容器鏡像的結(jié)構(gòu)、以及容器需要接收哪些操作指令,比如 create、start、stop、delete 等這些命令。runc 就可以按照這個(gè) OCI 文檔來創(chuàng)建一個(gè)符合規(guī)范的容器,既然是標(biāo)準(zhǔn)肯定就有其他 OCI 實(shí)現(xiàn),比如 Kata、gVisor 這些容器運(yùn)行時(shí)都是符合 OCI 標(biāo)準(zhǔn)的。

所以真正啟動(dòng)容器是通過 containerd-shim 去調(diào)用 runc 來啟動(dòng)容器的,runc 啟動(dòng)完容器后本身會(huì)直接退出,containerd-shim 則會(huì)成為容器進(jìn)程的父進(jìn)程, 負(fù)責(zé)收集容器進(jìn)程的狀態(tài), 上報(bào)給 containerd, 并在容器中 pid 為 1 的進(jìn)程退出后接管容器中的子進(jìn)程進(jìn)行清理, 確保不會(huì)出現(xiàn)僵尸進(jìn)程。

而 Docker 將容器操作都遷移到 containerd 中去是因?yàn)楫?dāng)前做 Swarm,想要進(jìn)軍 PaaS 市場,做了這個(gè)架構(gòu)切分,讓 Docker Daemon 專門去負(fù)責(zé)上層的封裝編排,當(dāng)然后面的結(jié)果我們知道 Swarm 在 Kubernetes 面前是慘敗,然后 Docker 公司就把 containerd 項(xiàng)目捐獻(xiàn)給了 CNCF 基金會(huì),這個(gè)也是現(xiàn)在的 Docker 架構(gòu)。

三、CRI

我們知道 Kubernetes 提供了一個(gè) CRI 的容器運(yùn)行時(shí)接口,那么這個(gè) CRI 到底是什么呢?這個(gè)其實(shí)也和 Docker 的發(fā)展密切相關(guān)的。

在 Kubernetes 早期的時(shí)候,當(dāng)時(shí) Docker 實(shí)在是太火了,Kubernetes 當(dāng)然會(huì)先選擇支持 Docker,而且是通過硬編碼的方式直接調(diào)用 Docker API,后面隨著 Docker 的不斷發(fā)展以及 Google 的主導(dǎo),出現(xiàn)了更多容器運(yùn)行時(shí),Kubernetes 為了支持更多更精簡的容器運(yùn)行時(shí),Google 就和紅帽主導(dǎo)推出了 CRI 標(biāo)準(zhǔn),用于將 Kubernetes 平臺和特定的容器運(yùn)行時(shí)(當(dāng)然主要是為了干掉 Docker)解耦。

CRI(Container Runtime Interface 容器運(yùn)行時(shí)接口)本質(zhì)上就是 Kubernetes 定義的一組與容器運(yùn)行時(shí)進(jìn)行交互的接口,所以只要實(shí)現(xiàn)了這套接口的容器運(yùn)行時(shí)都可以對接到 Kubernetes 平臺上來。不過 Kubernetes 推出 CRI 這套標(biāo)準(zhǔn)的時(shí)候還沒有現(xiàn)在的統(tǒng)治地位,所以有一些容器運(yùn)行時(shí)可能不會(huì)自身就去實(shí)現(xiàn) CRI 接口,于是就有了 shim(墊片), 一個(gè) shim 的職責(zé)就是作為適配器將各種容器運(yùn)行時(shí)本身的接口適配到 Kubernetes 的 CRI 接口上,其中 dockershim 就是 Kubernetes 對接 Docker 到 CRI 接口上的一個(gè)墊片實(shí)現(xiàn)。

CRI

Kubelet 通過 gRPC 框架與容器運(yùn)行時(shí)或 shim 進(jìn)行通信,其中 kubelet 作為客戶端,CRI shim(也可能是容器運(yùn)行時(shí)本身)作為服務(wù)器。

CRI 定義的 API(https://github.com/kubernetes/kubernetes/blob/release-1.5/pkg/kubelet/api/v1alpha1/runtime/api.proto) 主要包括兩個(gè) gRPC 服務(wù),ImageServiceRuntimeService,ImageService 服務(wù)主要是拉取鏡像、查看和刪除鏡像等操作,RuntimeService 則是用來管理 Pod 和容器的生命周期,以及與容器交互的調(diào)用(exec/attach/port-forward)等操作,可以通過 kubelet 中的標(biāo)志 --container-runtime-endpoint--image-service-endpoint 來配置這兩個(gè)服務(wù)的套接字。

kubelet cri

不過這里同樣也有一個(gè)例外,那就是 Docker,由于 Docker 當(dāng)時(shí)的江湖地位很高,Kubernetes 是直接內(nèi)置了 dockershim 在 kubelet 中的,所以如果你使用的是 Docker 這種容器運(yùn)行時(shí)的話是不需要單獨(dú)去安裝配置適配器之類的,當(dāng)然這個(gè)舉動(dòng)似乎也麻痹了 Docker 公司。

dockershim

現(xiàn)在如果我們使用的是 Docker 的話,當(dāng)我們在 Kubernetes 中創(chuàng)建一個(gè) Pod 的時(shí)候,首先就是 kubelet 通過 CRI 接口調(diào)用 dockershim,請求創(chuàng)建一個(gè)容器,kubelet 可以視作一個(gè)簡單的 CRI Client, 而 dockershim 就是接收請求的 Server,不過他們都是在 kubelet 內(nèi)置的。

dockershim 收到請求后, 轉(zhuǎn)化成 Docker Daemon 能識別的請求, 發(fā)到 Docker Daemon 上請求創(chuàng)建一個(gè)容器,請求到了 Docker Daemon 后續(xù)就是 Docker 創(chuàng)建容器的流程了,去調(diào)用 containerd,然后創(chuàng)建 containerd-shim 進(jìn)程,通過該進(jìn)程去調(diào)用 runc 去真正創(chuàng)建容器。

其實(shí)我們仔細(xì)觀察也不難發(fā)現(xiàn)使用 Docker 的話其實(shí)是調(diào)用鏈比較長的,真正容器相關(guān)的操作其實(shí) containerd 就完全足夠了,Docker 太過于復(fù)雜笨重了,當(dāng)然 Docker 深受歡迎的很大一個(gè)原因就是提供了很多對用戶操作比較友好的功能,但是對于 Kubernetes 來說壓根不需要這些功能,因?yàn)槎际峭ㄟ^接口去操作容器的,所以自然也就可以將容器運(yùn)行時(shí)切換到 containerd 來。

切換到containerd

切換到 containerd 可以消除掉中間環(huán)節(jié),操作體驗(yàn)也和以前一樣,但是由于直接用容器運(yùn)行時(shí)調(diào)度容器,所以它們對 Docker 來說是不可見的。 因此,你以前用來檢查這些容器的 Docker 工具就不能使用了。

你不能再使用 docker ps 或 docker inspect 命令來獲取容器信息。由于不能列出容器,因此也不能獲取日志、停止容器,甚至不能通過 docker exec 在容器中執(zhí)行命令。

當(dāng)然我們?nèi)匀豢梢韵螺d鏡像,或者用 docker build 命令構(gòu)建鏡像,但用 Docker 構(gòu)建、下載的鏡像,對于容器運(yùn)行時(shí)和 Kubernetes,均不可見。為了在 Kubernetes 中使用,需要把鏡像推送到鏡像倉庫中去。

從上圖可以看出在 containerd 1.0 中,對 CRI 的適配是通過一個(gè)單獨(dú)的 CRI-Containerd 進(jìn)程來完成的,這是因?yàn)樽铋_始 containerd 還會(huì)去適配其他的系統(tǒng)(比如 swarm),所以沒有直接實(shí)現(xiàn) CRI,所以這個(gè)對接工作就交給 CRI-Containerd 這個(gè) shim 了。

然后到了 containerd 1.1 版本后就去掉了 CRI-Containerd 這個(gè) shim,直接把適配邏輯作為插件的方式集成到了 containerd 主進(jìn)程中,現(xiàn)在這樣的調(diào)用就更加簡潔了。

containerd cri

與此同時(shí) Kubernetes 社區(qū)也做了一個(gè)專門用于 Kubernetes 的 CRI 運(yùn)行時(shí) CRI-O,直接兼容 CRI 和 OCI 規(guī)范。

cri-o

這個(gè)方案和 containerd 的方案顯然比默認(rèn)的 dockershim 簡潔很多,不過由于大部分用戶都比較習(xí)慣使用 Docker,所以大家還是更喜歡使用 dockershim 方案。

但是隨著 CRI 方案的發(fā)展,以及其他容器運(yùn)行時(shí)對 CRI 的支持越來越完善,Kubernetes 社區(qū)在2020年7月份就開始著手移除 dockershim 方案了:https://github.com/kubernetes/enhancements/tree/master/keps/sig-node/2221-remove-dockershim,現(xiàn)在的移除計(jì)劃是在 1.20 版本中將 kubelet 中內(nèi)置的 dockershim 代碼分離,將內(nèi)置的 dockershim 標(biāo)記為維護(hù)模式,當(dāng)然這個(gè)時(shí)候仍然還可以使用 dockershim,目標(biāo)是在 1.23?1.24 版本發(fā)布沒有 dockershim 的版本(代碼還在,但是要默認(rèn)支持開箱即用的 docker 需要自己構(gòu)建 kubelet,會(huì)在某個(gè)寬限期過后從 kubelet 中刪除內(nèi)置的 dockershim 代碼)。

那么這是否就意味這 Kubernetes 不再支持 Docker 了呢?當(dāng)然不是的,這只是廢棄了內(nèi)置的 dockershim 功能而已,Docker 和其他容器運(yùn)行時(shí)將一視同仁,不會(huì)單獨(dú)對待內(nèi)置支持,如果我們還想直接使用 Docker 這種容器運(yùn)行時(shí)應(yīng)該怎么辦呢?可以將 dockershim 的功能單獨(dú)提取出來獨(dú)立維護(hù)一個(gè) cri-dockerd 即可,就類似于 containerd 1.0 版本中提供的 CRI-Containerd,當(dāng)然還有一種辦法就是 Docker 官方社區(qū)將 CRI 接口內(nèi)置到 Dockerd 中去實(shí)現(xiàn)。

但是我們也清楚 Dockerd 也是去直接調(diào)用的 Containerd,而 containerd 1.1 版本后就內(nèi)置實(shí)現(xiàn)了 CRI,所以 Docker 也沒必要再去單獨(dú)實(shí)現(xiàn) CRI 了,當(dāng) Kubernetes 不再內(nèi)置支持開箱即用的 Docker 的以后,最好的方式當(dāng)然也就是直接使用 Containerd 這種容器運(yùn)行時(shí),而且該容器運(yùn)行時(shí)也已經(jīng)經(jīng)過了生產(chǎn)環(huán)境實(shí)踐的。

資料來源:

一文搞懂容器運(yùn)行時(shí) Containerd

Kubernetes容器運(yùn)行時(shí)棄用Docker轉(zhuǎn)型Containerd

K8S Runtime CRI OCI contained dockershim 理解

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容