學習容器技術的過程中耙考,我看到有不少同學留言問 Kubernetes“棄用 Docker”的事情,擔心現(xiàn)在學 Docker 是否還有價值潭兽,是否現(xiàn)在就應該切換到 containerd 或者是其他 runtime倦始。
這些疑慮的確是有些道理。兩年前山卦,Kubernetes 放出消息要“棄用 Docker”的時候鞋邑,確確實實在 Kubernetes 社區(qū)里掀起了一場“軒然大波”,影響甚至波及到社區(qū)之外账蓉,也導致 Kubernetes 不得不寫了好幾篇博客來反復解釋這么做的原因枚碗。
兩年過去了,雖然最新的 Kubernetes 1.24 已經達成了“棄用”的目標铸本,但很多人對這件事似乎還是沒有非常清晰的認識肮雨,所以今天我們就來聊聊這個話題,我也講講我的一些看法箱玷。
什么是 CRI怨规?
要了解 Kubernetes 為什么要“棄用 Docker”,還得追根溯源锡足,回頭去看 Kubernetes 的發(fā)展歷史波丰。
2014 年,Docker 正如日中天舶得,在容器領域沒有任何對手呀舔,而這時 Kubernetes 才剛剛誕生,雖然背后有 Google 和 Borg 的支持扩灯,但還是比較弱小的媚赖。所以,Kubernetes 很自然就選擇了在 Docker 上運行珠插,畢竟“背靠大樹好乘涼”惧磺,同時也能趁機“養(yǎng)精蓄銳”逐步發(fā)展壯大自己。
時間一轉眼到了 2016 年捻撑,CNCF 已經成立一年了磨隘,而 Kubernetes 也已經發(fā)布了 1.0 版缤底,可以正式用于生產環(huán)境,這些都標志著 Kubernetes 已經成長起來了番捂,不再需要“看臉色吃飯”个唧。于是它就宣布加入了 CNCF,成為了第一個 CNCF 托管項目设预,想要借助基金會的力量聯(lián)合其他廠商徙歼,一起來“扳倒”Docker。
那它是怎么做的呢鳖枕?
在 2016 年底的 1.5 版里魄梯,Kubernetes 引入了一個新的接口標準:CRI ,Container Runtime Interface宾符。
CRI 采用了 ProtoBuffer 和 gPRC酿秸,規(guī)定 kubelet 該如何調用容器運行時去管理容器和鏡像,但這是一套全新的接口魏烫,和之前的 Docker 調用完全不兼容辣苏。
Kubernetes 意思很明顯,就是不想再綁定在 Docker 上了哄褒,允許在底層接入其他容器技術(比如 rkt稀蟋、kata 等),隨時可以把 Docker“踢開”读处。
但是這個時候 Docker 已經非常成熟糊治,而且市場的慣性也非常強大,各大云廠商不可能一下子就把 Docker 全部替換掉罚舱。所以 Kubernetes 也只能同時提供一個“折中”方案井辜,在 kubelet 和 Docker 中間加入一個“適配器”,把 Docker 的接口轉換成符合 CRI 標準的接口管闷。
因為這個“適配器”夾在 kubelet 和 Docker 之間粥脚,所以就被形象地稱為是“shim”,也就是“墊片”的意思包个。
有了 CRI 和 shim刷允,雖然 Kubernetes 還使用 Docker 作為底層運行時,但也具備了和 Docker 解耦的條件碧囊,從此就拉開了“棄用 Docker”這場大戲的帷幕树灶。
什么是 containerd?
面對 Kubernetes“咄咄逼人”的架勢糯而,Docker 是看在眼里痛在心里天通,雖然有苦心經營了多年的社區(qū)和用戶群,但公司的體量太小熄驼,實在是沒有足夠的實力與大公司相抗衡像寒。
不過 Docker 也沒有“坐以待斃”烘豹,而是采取了“斷臂求生”的策略,推動自身的重構诺祸,把原本單體架構的 Docker Engine 拆分成了多個模塊携悯,其中的 Docker daemon 部分就捐獻給了 CNCF,形成了 containerd筷笨。
containerd 作為 CNCF 的托管項目憔鬼,自然是要符合 CRI 標準的。但 Docker 出于自己諸多原因的考慮奥秆,它只是在 Docker Engine 里調用了 containerd逊彭,外部的接口仍然保持不變咸灿,也就是說還不與 CRI 兼容构订。
由于 Docker 的“固執(zhí)己見”,這時 Kubernetes 里就出現(xiàn)了兩種調用鏈:
第一種是用 CRI 接口調用 dockershim避矢,然后 dockershim 調用 Docker悼瘾,Docker 再走 containerd 去操作容器。
第二種是用 CRI 接口直接調用 containerd 去操作容器审胸。
·
顯然亥宿,由于都是用 containerd 來管理容器,所以這兩種調用鏈的最終效果是完全一樣的砂沛,但是第二種方式省去了 dockershim 和 Docker Engine 兩個環(huán)節(jié)烫扼,更加簡潔明了,損耗更少碍庵,性能也會提升一些映企。
在 2018 年 Kubernetes 1.10 發(fā)布的時候,containerd 也更新到了 1.1 版静浴,正式與 Kubernetes 集成堰氓,同時還發(fā)表了一篇博客文章(https://kubernetes.io/blog/2018/05/24/kubernetes-containerd-integration-goes-ga/),展示了一些性能測試數據:
從這些數據可以看到苹享,containerd1.1 相比當時的 Docker 18.03双絮,Pod 的啟動延遲降低了大約 20%,CPU 使用率降低了 68%得问,內存使用率降低了 12%囤攀,這是一個相當大的性能改善,對于云廠商非常有誘惑力宫纬。
正式“棄用 Docker”
有了 CRI 和 containerd 這兩件強大的武器焚挠,勝利的天平已經明顯向 Kubernetes 傾斜了。
又是兩年之后哪怔,到了 2020 年宣蔚,Kubernetes 1.20 終于正式向 Docker“宣戰(zhàn)”:kubelet 將棄用 Docker 支持向抢,并會在未來的版本中徹底刪除。
但由于 Docker 幾乎成為了容器技術的代名詞胚委,而且 Kubernetes 也已經使用 Docker 很多年挟鸠,這個聲明在不斷傳播的過程中很快就“變味”了,“kubelet 將棄用 Docker 支持”被簡化成了更吸引眼球的“Kubernetes 將棄用 Docker”亩冬。
這自然就在 IT 界引起了恐慌艘希,“不明真相的廣大群眾”紛紛表示震驚:用了這么久的 Docker 突然就不能用了,Kubernetes 為什么要如此對待 Docker硅急?之前在 Docker 上的投入會不會就全歸零了覆享?現(xiàn)有的大量鏡像該怎么辦?
其實营袜,如果你理解了前面講的 CRI 和 containerd 這兩個項目撒顿,就會知道 Kubernetes 的這個舉動也沒有什么值得大驚小怪的,一切都是“水到渠成”的:它實際上只是“棄用了 dockershim”這個小組件荚板,也就是說把 dockershim 移出了 kubelet凤壁,并不是“棄用了 Docker”這個軟件產品。
所以跪另,“棄用 Docker”對 Kubernetes 和 Docker 來說都不會有什么太大的影響拧抖,因為他們兩個都早已經把下層都改成了開源的 containerd,原來的 Docker 鏡像和容器仍然會正常運行免绿,唯一的變化就是 Kubernetes 繞過了 Docker唧席,直接調用 Docker 內部的 containerd 而已。
這個關系你可以參考下面的這張圖來理解:
當然嘲驾,影響也不是完全沒有淌哟。如果 Kubernetes 直接使用 containerd 來操縱容器,那么它就是一個與 Docker 獨立的工作環(huán)境距淫,彼此都不能訪問對方管理的容器和鏡像绞绒。換句話說,使用命令 docker ps 就看不到在 Kubernetes 里運行的容器了榕暇。
這對有的人來說可能需要稍微習慣一下蓬衡,改用新的工具 crictl,不過用來查看容器彤枢、鏡像的子命令還是一樣的狰晚,比如 ps、images 等等缴啡,適應起來難度不大(但如果我們一直用 kubectl 來管理 Kubernetes 的話壁晒,這就是沒有任何影響了)。
“宣戰(zhàn)”之后业栅,Kubernetes 原本打算用一年的時間完成“棄用 Docker”的工作秒咐,但它也確實低估了 Docker 的根基谬晕,到了 1.23 版還是沒能移除 dockershim,不得已又往后推遲了半年携取,終于在今年 5 月份發(fā)布的 1.24 版把 dockershim 的代碼從 kubelet 里刪掉了攒钳。
自此,Kubernetes 徹底和 Docker“分道揚鑣”雷滋,今后就是“大路朝天不撑,各走一邊”。
Docker 的未來
那么晤斩,Docker 的未來會是怎么樣的呢焕檬?難道云原生時代就沒有它的立足之地了嗎?
這個問題的答案很顯然是否定的澳泵。
作為容器技術的初創(chuàng)者实愚,Docker 的歷史地位無人能夠質疑,雖然現(xiàn)在 Kubernetes 不再默認綁定 Docker烹俗,但 Docker 還是能夠以其他的形式與 Kubernetes 共存的爆侣。
首先萍程,因為容器鏡像格式已經被標準化了(OCI 規(guī)范幢妄,Open Container Initiative),Docker 鏡像仍然可以在 Kubernetes 里正常使用茫负,原來的開發(fā)測試蕉鸳、CI/CD 流程都不需要改動,我們仍然可以拉取 Docker Hub 上的鏡像忍法,或者編寫 Dockerfile 來打包應用潮尝。
其次,Docker 是一個完整的軟件產品線饿序,不止是 containerd勉失,它還包括了鏡像構建、分發(fā)原探、測試等許多服務乱凿,甚至在 Docker Desktop 里還內置了 Kubernetes。
單就容器開發(fā)的便利性來講咽弦,Docker 還是暫時難以被替代的徒蟆,廣大云原生開發(fā)者可以在這個熟悉的環(huán)境里繼續(xù)工作,利用 Docker 來開發(fā)運行在 Kubernetes 里的應用型型。
再次段审,雖然 Kubernetes 已經不再包含 dockershim,但 Docker 公司卻把這部分代碼接管了過來闹蒜,另建了一個叫 cri-dockerd(https://github.com/mirantis/cri-dockerd)的項目寺枉,作用也是一樣的抑淫,把 Docker Engine 適配成 CRI 接口,這樣 kubelet 就又可以通過它來操作 Docker 了姥闪,就仿佛是一切從未發(fā)生過丈冬。
綜合來看,Docker 雖然在容器編排戰(zhàn)爭里落敗甘畅,被 Kubernetes 排擠到了角落埂蕊,但它仍然具有強韌的生命力,多年來積累的眾多忠實用戶和數量龐大的應用鏡像是它的最大資本和后盾疏唾,足以支持它在另一條不與 Kubernetes 正面交鋒的道路上走下去蓄氧。
而對于我們這些初學者來說,Docker 方便易用槐脏,具有完善的工具鏈和友好的交互界面喉童,市面上很難找到能夠與它媲美的軟件了,應該說是入門學習容器技術和云原生的“不二之選”顿天。
至于 Kubernetes 底層用的什么堂氯,我們又何必太過于執(zhí)著和關心呢?
雖然今天的內容是加餐牌废,但我還是給你留個思考題吧咽白,我們可以一起討論一下:
Docker 重構自身,分離出 containerd鸟缕,這是否算是一種“自掘墳墓”的行為呢晶框?
如果沒有 containerd,那現(xiàn)在的情形會是怎么樣的呢懂从?
怎么理解 runc是一個按OCI規(guī)范運行容器的cli工具授段,如運行時containerd默認使用runc?
runc是OCI規(guī)范中的容器底層部分番甩,也就是原來docker的libcontainer侵贵,它調用namespace、cgroup等系統(tǒng)接口創(chuàng)建容器缘薛。
運行時這個概念比較模糊窍育,從Kubernetes角度來看containerd就是運行時,再往下runc是運行時掩宜。
Kubernetes現(xiàn)在沒有默認綁定在containerd上蔫骂,cri接口通用,換哪個都可以牺汤。
容器運行時只是對Linux的namespace, cgroup, rootfs進行了產品化打包的一種技術辽旋,原則上來講,如果Docker不把運行時containerd分離標準化CRI出來,Kubernetes其實也可以造出新輪子补胚,畢竟現(xiàn)在的容器運行時也有很多码耐,像cri-o, kata, mcr等。
containerd就是原來的docker daemon溶其,是用來管理調度容器的骚腥,不是憑空出現(xiàn)。
它是Kubernetes的必備組件container-runtime瓶逃,不過也可以替換成CRI-O束铭,因為都符合cri。
Kubernetes定義CRI就是要和下層的容器運行時解耦厢绝,所以不會對哪個產品有偏向契沫,只是containerd源自Docker,用戶的習慣影響會更大一些昔汉。
現(xiàn)在Kubernetes沒有默認的容器運行時懈万,必須用戶自己安裝。