在寫“K8s”系列文章的過程中,很多讀者留言詢問 K8s 棄用 Docker 的事,擔(dān)心現(xiàn)在學(xué)習(xí) Docker 是否還值得,是不是該切換到 containerd 或其他運(yùn)行時。
這些懷疑有一定的道理。兩年前,K8s 發(fā)布“棄用 Docker”的消息時,確實(shí)在社區(qū)引起了“軒然大波”,影響甚至蔓延到了社區(qū)之外,K8s 不得不寫了好幾篇博客來重復(fù)解釋原因。
兩年過去了,雖然 K8s 1.24 已經(jīng)實(shí)現(xiàn)了“棄用 Docker”的目標(biāo),但很多人似乎對這一點(diǎn)還不是很清楚。所以本篇文章就來聊聊這個話題。
CRI(容器運(yùn)行時接口)
要理解 K8s 為何“棄用 Docker”,我們得回顧一下 K8s 的發(fā)展史。
2014 年,Docker 正處于鼎盛時期,而 K8s 剛剛誕生。雖然它得到了 google 和 Borg 的支持,但它還是比較新的。
因此,K8s 首先選擇支持 Docker 。
快進(jìn)到 2016 年,CNCF 成立一年,K8s 也發(fā)布了 1.0 版本,可以正式用于生產(chǎn)環(huán)境。這些都表明 K8s 已經(jīng)長大了。
于是宣布加入 CNCF,成為第一個 CNCF 托管項目。它想利用基金會的力量聯(lián)合其他廠商來“打倒”Docker。
在 2016 年底的 1.5 版本中,K8s 引入了新的接口標(biāo)準(zhǔn):CRI:Container Runtime Interface 容器運(yùn)行時接口。
CRI 使用ProtoBufferandgPRC來指定kubelet應(yīng)該如何調(diào)用容器運(yùn)行時來管理容器和鏡像,但這是一組與以前的 Docker 調(diào)用完全不兼容的新接口。
顯然它不想再和 Docker 綁定,在底層允許訪問其他容器技術(shù)(如 rkt、kata 等),可以隨時“踢開” Docker。
但此時 Docker 已經(jīng)非常成熟,市場的慣性也非常強(qiáng)。各大云廠商不可能一下子全部替換掉 Docker。
因此,K8s 只能同時提供一種“折中”的方案,在kubelet和 Docker 之間增加一個“適配器”,將 Docker 的接口轉(zhuǎn)換為 CRI 兼容的接口:
因?yàn)檫@個“適配器”夾在kubeletDocker 和 Docker 之間,所以形象地稱為“shim”,意思是“墊片”。
有了 CRI 和 shim,雖然 K8s 仍然使用 Docker 作為底層運(yùn)行時,但它也具備了與 Docker 解耦的條件,從而拉開了“棄用 Docker”大戲的帷幕。
Containerd
面對挑戰(zhàn),Docker 采取了“斷臂求生”的策略,推動自身重構(gòu),將原有單一架構(gòu)的 Docker Engine 拆分成多個模塊,其中 Docker daemon 部分捐贈給 CNCF,containerd 形成。
作為 CNCF 的托管項目,containerd 必須符合 CRI 標(biāo)準(zhǔn)。但是由于很多原因,Docker 只是 containerd 在 Docker Engine 中調(diào)用,對外的接口保持不變,也就是說不兼容 CRI。
由于 Docker 的“固執(zhí)”,此時 K8s 中有兩條調(diào)用鏈:
使用 CRI 接口調(diào)用 dockershim,然后 dockershim 調(diào)用 Docker,Docker 再去 containerd 操作容器。
使用 CRI 接口直接調(diào)用 containerd 操作容器。
顯然,因?yàn)?containerd 是用來管理容器的,所以這兩個調(diào)用鏈的最終效果是完全一樣的,但是第二種方法去掉了 dockershim 和 Docker Engine 這兩個環(huán)節(jié),更加簡潔明了,性能也更好。
2018 年 Kube.NETes 1.10 發(fā)布時,containerd 也更新到 1.1 版本,正式與 Kubernetes 集成,并發(fā)表[博文](https://kubernetes.io/blog/2018/05/24/kubernetes-containerd-integration- gos-ga/ "博文")顯示一些性能測試數(shù)據(jù):
從這些數(shù)據(jù)可以看出,相比當(dāng)時的 Docker 18.03,containerd1.1Pod 啟動延遲降低了 20% 左右,CPU 使用率降低了 68%,內(nèi)存使用率降低了 12%,這樣可觀的性能提升對云廠商來說是非常有誘惑力的。
棄用Docker
2020 年,K8s 1.20 終于正式向 Docker “宣戰(zhàn)”:kubelet將棄用 Docker 支持,并將在未來的版本中完全移除。
但由于 Docker 幾乎已經(jīng)成為容器技術(shù)的代名詞,而且 K8s 已經(jīng)使用 Docker 多年,該公告在傳播時很快“變味了”,“kubelet 將棄用 Docker 支持”被簡化為更吸人眼球的東西 “K8s 將棄用”Docker”。
這自然引起了 IT 界的恐慌,“不明真相的群眾”紛紛表示震驚:
用了這么久的 Docker 突然不能用了。
為什么 K8s 會這樣對待 Docker?
之前對 Docker 的投資會歸零嗎?現(xiàn)有的大量鏡像怎么辦?
其實(shí),如果你了解了上面提到的這兩個項目CRI,containerd你就會知道,K8s 的這一舉動并不奇怪,一切都是“自然”的:其實(shí)只是“棄用 dockershim ”,也就是dockershim搬出kubelet,并不是“棄用 Docker”的軟件產(chǎn)品。
因此,“棄用 Docker”對 K8s 和 Docker 的影響不大,因?yàn)樗鼈兌家呀?jīng)將底層改為開源containerd,原有的 Docker 鏡像和容器仍然可以正常運(yùn)行。唯一的變化是K8s繞過了Docker,直接調(diào)用Docker內(nèi)部的containerd。
然而,還是會有一些影響。如果K8s直接使用containerd來操作容器,那么它就是一個獨(dú)立于Docker的工作環(huán)境,兩者都無法訪問對方管理的容器和鏡像。換句話說,使用docker ps命令將不會看到K8s中運(yùn)行的容器。
這對一些人來說可能需要花一點(diǎn)時間來適應(yīng)并使用新工具crictl,但用于查看容器和鏡像的子命令仍然是相同的,例如ps,images等,不難適應(yīng)(如果你一直在用kubectl管理K8s,這個沒有影響)。
K8s 原本計劃用一年時間完成“棄用 Docker”的工作,但它確實(shí)低估了 Docker 的基礎(chǔ)。1.23版本還是沒能移除dockershim,只好延期半年。最后,1.24版本從kubelet中刪除了dockershim的代碼。
從此,Kubernetes 與 Docker 徹底“分道揚(yáng)鑣”。
Docker 的未來
那么,Docker 的未來會怎樣呢?云原生時代就沒有它的立足之地嗎?這個問題的答案顯然是否定的。
作為容器技術(shù)的奠基人,沒有人可以質(zhì)疑 Docker 的歷史地位。雖然 K8s 默認(rèn)不再綁定 Docker,但 Docker 仍然可以以其他形式的 K8s 共存。
首先,由于容器鏡像格式已經(jīng)標(biāo)準(zhǔn)化(OCI規(guī)范,Open Container Initiative),Docker鏡像在K8s中仍然可以正常使用,不需要改變原有的開發(fā)測試和CI/CD流程。我們?nèi)匀豢梢岳?Docker Hub,或者編寫一個 Dockerfile 來打包應(yīng)用程序。
其次,Docker是一個完整的軟件產(chǎn)品線,不僅僅是containerd,它還包括鏡像構(gòu)建、分發(fā)、測試等很多服務(wù),甚至連K8s都內(nèi)置于Docker Desktop中。
就容器開發(fā)的便利性而言,Docker暫時還難以被取代。大多數(shù)云原生開發(fā)人員可以繼續(xù)在這個熟悉的環(huán)境中工作,使用Docker來開發(fā)在K8s中運(yùn)行的應(yīng)用程序。
同樣,雖然 K8s 不再包含dockershim,Docker 已經(jīng)接管了這部分代碼并構(gòu)建了一個名為cri-dockerd的項目,該項目也同樣工作,將 Docker Engine 適配為 CRI 接口,這樣就kubelet可以通過它再次操作Docker,就好像它從來沒有發(fā)生過一樣。
總的來說,Docker雖然在容器編排大戰(zhàn)中敗下陣來,被K8s擠到了墻角,但依然具有很強(qiáng)的生命力。多年積累的眾多忠實(shí)用戶和大量應(yīng)用形象是其最大的資本和后盾。足以支持它在另一條不與 K8s 正面交鋒的道路上。
對于初學(xué)者來說,Docker簡單易用,工具鏈完整,界面友好,市面上很難找到與之相媲美的軟件。應(yīng)該說是入門級學(xué)習(xí)容器技術(shù)和云原生的上上選擇。