一面粮、背景
Kubernetes社區(qū)在2020年7月份發(fā)布的版本中已經(jīng)開始了dockershim的移除計劃,在1.20版本中將內(nèi)置的dockershim進行分離继低,這個版本依舊還可以使用dockershim熬苍,但是在1.24中被刪除。從1.24開始袁翁,大家需要使用其他受到支持的運行時選項(例如containerd或CRI-O)柴底;如果選擇Docker Engine作為運行時,則需要使用cri-dockerd
二粱胜、Docker
從 Docker 1.11 版本開始柄驻,Docker 容器運行就不是簡單通過 Docker Daemon 來啟動了,而是通過集成 containerd焙压、runc 等多個組件來完成的鸿脓。雖然 Docker Daemon 守護進程模塊在不停的重構(gòu)抑钟,但是基本功能和定位沒有太大的變化,一直都是 CS 架構(gòu)野哭,守護進程負(fù)責(zé)和 Docker Client 端交互味赃,并管理 Docker 鏡像和容器。現(xiàn)在的架構(gòu)中組件 containerd 就會負(fù)責(zé)集群節(jié)點上容器的生命周期管理虐拓,并向上為 Docker Daemon 提供 gRPC 接口。
當(dāng)我們要創(chuàng)建一個容器的時候傲武,現(xiàn)在 Docker Daemon 并不能直接幫我們創(chuàng)建了蓉驹,而是請求 containerd 來創(chuàng)建一個容器,containerd 收到請求后揪利,也并不會直接去操作容器态兴,而是創(chuàng)建一個叫做 containerd-shim 的進程,讓這個進程去操作容器疟位,我們指定容器進程是需要一個父進程來做狀態(tài)收集瞻润、維持 stdin 等 fd 打開等工作的,假如這個父進程就是 containerd甜刻,那如果 containerd 掛掉的話绍撞,整個宿主機上所有的容器都得退出了,而引入 containerd-shim 這個墊片就可以來規(guī)避這個問題了得院。
然后創(chuàng)建容器需要做一些 namespaces 和 cgroups 的配置傻铣,以及掛載 root 文件系統(tǒng)等操作,這些操作其實已經(jīng)有了標(biāo)準(zhǔn)的規(guī)范祥绞,那就是 OCI(開放容器標(biāo)準(zhǔn))非洲,runc 就是它的一個參考實現(xiàn)(Docker 被逼無耐將 libcontainer 捐獻出來改名為 runc 的),這個標(biāo)準(zhǔn)其實就是一個文檔蜕径,主要規(guī)定了容器鏡像的結(jié)構(gòu)两踏、以及容器需要接收哪些操作指令,比如 create兜喻、start梦染、stop、delete 等這些命令虹统。runc 就可以按照這個 OCI 文檔來創(chuàng)建一個符合規(guī)范的容器弓坞,既然是標(biāo)準(zhǔn)肯定就有其他 OCI 實現(xiàn),比如 Kata车荔、gVisor 這些容器運行時都是符合 OCI 標(biāo)準(zhǔn)的渡冻。
所以真正啟動容器是通過 containerd-shim 去調(diào)用 runc 來啟動容器的,runc 啟動完容器后本身會直接退出忧便,containerd-shim 則會成為容器進程的父進程, 負(fù)責(zé)收集容器進程的狀態(tài), 上報給 containerd, 并在容器中 pid 為 1 的進程退出后接管容器中的子進程進行清理, 確保不會出現(xiàn)僵尸進程族吻。
而 Docker 將容器操作都遷移到 containerd 中去是因為當(dāng)前做 Swarm帽借,想要進軍 PaaS 市場,做了這個架構(gòu)切分超歌,讓 Docker Daemon 專門去負(fù)責(zé)上層的封裝編排砍艾,當(dāng)然后面的結(jié)果我們知道 Swarm 在 Kubernetes 面前是慘敗,然后 Docker 公司就把 containerd 項目捐獻給了 CNCF 基金會巍举,這個也是現(xiàn)在的 Docker 架構(gòu)脆荷。
三、CRI
我們知道 Kubernetes 提供了一個 CRI 的容器運行時接口懊悯,那么這個 CRI 到底是什么呢蜓谋?這個其實也和 Docker 的發(fā)展密切相關(guān)的。
在 Kubernetes 早期的時候炭分,當(dāng)時 Docker 實在是太火了桃焕,Kubernetes 當(dāng)然會先選擇支持 Docker,而且是通過硬編碼的方式直接調(diào)用 Docker API捧毛,后面隨著 Docker 的不斷發(fā)展以及 Google 的主導(dǎo)观堂,出現(xiàn)了更多容器運行時,Kubernetes 為了支持更多更精簡的容器運行時呀忧,Google 就和紅帽主導(dǎo)推出了 CRI 標(biāo)準(zhǔn)师痕,用于將 Kubernetes 平臺和特定的容器運行時(當(dāng)然主要是為了干掉 Docker)解耦。
CRI(Container Runtime Interface 容器運行時接口)本質(zhì)上就是 Kubernetes 定義的一組與容器運行時進行交互的接口荐虐,所以只要實現(xiàn)了這套接口的容器運行時都可以對接到 Kubernetes 平臺上來七兜。不過 Kubernetes 推出 CRI 這套標(biāo)準(zhǔn)的時候還沒有現(xiàn)在的統(tǒng)治地位,所以有一些容器運行時可能不會自身就去實現(xiàn) CRI 接口福扬,于是就有了 shim(墊片)腕铸, 一個 shim 的職責(zé)就是作為適配器將各種容器運行時本身的接口適配到 Kubernetes 的 CRI 接口上,其中 dockershim 就是 Kubernetes 對接 Docker 到 CRI 接口上的一個墊片實現(xiàn)铛碑。
Kubelet 通過 gRPC 框架與容器運行時或 shim 進行通信狠裹,其中 kubelet 作為客戶端,CRI shim(也可能是容器運行時本身)作為服務(wù)器汽烦。
CRI 定義的 API(https://github.com/kubernetes/kubernetes/blob/release-1.5/pkg/kubelet/api/v1alpha1/runtime/api.proto) 主要包括兩個 gRPC 服務(wù)涛菠,ImageService
和 RuntimeService
,ImageService
服務(wù)主要是拉取鏡像撇吞、查看和刪除鏡像等操作俗冻,RuntimeService
則是用來管理 Pod 和容器的生命周期,以及與容器交互的調(diào)用(exec/attach/port-forward)等操作牍颈,可以通過 kubelet 中的標(biāo)志 --container-runtime-endpoint
和 --image-service-endpoint
來配置這兩個服務(wù)的套接字迄薄。
不過這里同樣也有一個例外,那就是 Docker煮岁,由于 Docker 當(dāng)時的江湖地位很高讥蔽,Kubernetes 是直接內(nèi)置了 dockershim 在 kubelet 中的涣易,所以如果你使用的是 Docker 這種容器運行時的話是不需要單獨去安裝配置適配器之類的,當(dāng)然這個舉動似乎也麻痹了 Docker 公司冶伞。
現(xiàn)在如果我們使用的是 Docker 的話新症,當(dāng)我們在 Kubernetes 中創(chuàng)建一個 Pod 的時候,首先就是 kubelet 通過 CRI 接口調(diào)用 dockershim响禽,請求創(chuàng)建一個容器徒爹,kubelet 可以視作一個簡單的 CRI Client, 而 dockershim 就是接收請求的 Server,不過他們都是在 kubelet 內(nèi)置的芋类。
dockershim 收到請求后, 轉(zhuǎn)化成 Docker Daemon 能識別的請求, 發(fā)到 Docker Daemon 上請求創(chuàng)建一個容器瀑焦,請求到了 Docker Daemon 后續(xù)就是 Docker 創(chuàng)建容器的流程了,去調(diào)用 containerd梗肝,然后創(chuàng)建 containerd-shim 進程,通過該進程去調(diào)用 runc 去真正創(chuàng)建容器铺董。
其實我們仔細(xì)觀察也不難發(fā)現(xiàn)使用 Docker 的話其實是調(diào)用鏈比較長的巫击,真正容器相關(guān)的操作其實 containerd 就完全足夠了,Docker 太過于復(fù)雜笨重了精续,當(dāng)然 Docker 深受歡迎的很大一個原因就是提供了很多對用戶操作比較友好的功能坝锰,但是對于 Kubernetes 來說壓根不需要這些功能,因為都是通過接口去操作容器的重付,所以自然也就可以將容器運行時切換到 containerd 來顷级。
切換到 containerd 可以消除掉中間環(huán)節(jié),操作體驗也和以前一樣确垫,但是由于直接用容器運行時調(diào)度容器弓颈,所以它們對 Docker 來說是不可見的。 因此删掀,你以前用來檢查這些容器的 Docker 工具就不能使用了翔冀。
你不能再使用 docker ps 或 docker inspect 命令來獲取容器信息。由于不能列出容器披泪,因此也不能獲取日志纤子、停止容器,甚至不能通過 docker exec 在容器中執(zhí)行命令款票。
當(dāng)然我們?nèi)匀豢梢韵螺d鏡像控硼,或者用 docker build 命令構(gòu)建鏡像,但用 Docker 構(gòu)建艾少、下載的鏡像卡乾,對于容器運行時和 Kubernetes,均不可見姆钉。為了在 Kubernetes 中使用说订,需要把鏡像推送到鏡像倉庫中去抄瓦。
從上圖可以看出在 containerd 1.0 中,對 CRI 的適配是通過一個單獨的 CRI-Containerd 進程來完成的陶冷,這是因為最開始 containerd 還會去適配其他的系統(tǒng)(比如 swarm)钙姊,所以沒有直接實現(xiàn) CRI,所以這個對接工作就交給 CRI-Containerd 這個 shim 了埂伦。
然后到了 containerd 1.1 版本后就去掉了 CRI-Containerd 這個 shim煞额,直接把適配邏輯作為插件的方式集成到了 containerd 主進程中,現(xiàn)在這樣的調(diào)用就更加簡潔了沾谜。
與此同時 Kubernetes 社區(qū)也做了一個專門用于 Kubernetes 的 CRI 運行時 CRI-O膊毁,直接兼容 CRI 和 OCI 規(guī)范。
這個方案和 containerd 的方案顯然比默認(rèn)的 dockershim 簡潔很多基跑,不過由于大部分用戶都比較習(xí)慣使用 Docker婚温,所以大家還是更喜歡使用 dockershim
方案。
但是隨著 CRI 方案的發(fā)展媳否,以及其他容器運行時對 CRI 的支持越來越完善栅螟,Kubernetes 社區(qū)在2020年7月份就開始著手移除 dockershim 方案了:https://github.com/kubernetes/enhancements/tree/master/keps/sig-node/2221-remove-dockershim,現(xiàn)在的移除計劃是在 1.20 版本中將 kubelet 中內(nèi)置的 dockershim 代碼分離篱竭,將內(nèi)置的 dockershim 標(biāo)記為維護模式
力图,當(dāng)然這個時候仍然還可以使用 dockershim,目標(biāo)是在 1.23?1.24 版本發(fā)布沒有 dockershim 的版本(代碼還在掺逼,但是要默認(rèn)支持開箱即用的 docker 需要自己構(gòu)建 kubelet吃媒,會在某個寬限期過后從 kubelet 中刪除內(nèi)置的 dockershim 代碼)。
那么這是否就意味這 Kubernetes 不再支持 Docker 了呢吕喘?當(dāng)然不是的赘那,這只是廢棄了內(nèi)置的 dockershim 功能而已,Docker 和其他容器運行時將一視同仁氯质,不會單獨對待內(nèi)置支持漓概,如果我們還想直接使用 Docker 這種容器運行時應(yīng)該怎么辦呢?可以將 dockershim 的功能單獨提取出來獨立維護一個 cri-dockerd 即可病梢,就類似于 containerd 1.0 版本中提供的 CRI-Containerd胃珍,當(dāng)然還有一種辦法就是 Docker 官方社區(qū)將 CRI 接口內(nèi)置到 Dockerd 中去實現(xiàn)。
但是我們也清楚 Dockerd 也是去直接調(diào)用的 Containerd蜓陌,而 containerd 1.1 版本后就內(nèi)置實現(xiàn)了 CRI觅彰,所以 Docker 也沒必要再去單獨實現(xiàn) CRI 了,當(dāng) Kubernetes 不再內(nèi)置支持開箱即用的 Docker 的以后钮热,最好的方式當(dāng)然也就是直接使用 Containerd 這種容器運行時填抬,而且該容器運行時也已經(jīng)經(jīng)過了生產(chǎn)環(huán)境實踐的。
資料來源: