Kubernetes“弃用Docker”是怎么回事?

什么是CRI

在2016年底的1.5版里,Kubernetes引入了一个新的接口标准:CRI,Container Runtime Interface。

CRI采用了ProtoBuffer和gPRC,规定kubelet该如何调用容器运行时去管理容器和镜像,但这是一套全新的接口,和之前的Docker调用完全不兼容。

Kubernetes意思很明显,就是不想再绑定在Docker上了,允许在底层接入其他容器技术(比如rkt、kata等),随时可以把Docker“踢开”。

但是这个时候Docker已经非常成熟,而且市场的惯性也非常强大,各大云厂商不可能一下子就把Docker全部替换掉。所以Kubernetes也只能同时提供一个“折中”方案,在kubelet和Docker中间加入一个“适配器”,把Docker的接口转换成符合CRI标准的接口:

因为这个“适配器”夹在kubelet和Docker之间,所以就被形象地称为是“shim”,也就是“垫片”的意思。

有了CRI和shim,虽然Kubernetes还使用Docker作为底层运行时,但也具备了和Docker解耦的条件,从此就拉开了“弃用Docker”这场大戏的帷幕。

什么是containerd

Docker没有“坐以待毙”,而是采取了“断臂求生”的策略,推动自身的重构,把原本单体架构的Docker Engine拆分成了多个模块,其中的Docker daemon部分就捐献给了CNCF,形成了containerd

containerd作为CNCF的托管项目,自然是要符合CRI标准的。但Docker出于自己诸多原因的考虑,它只是在Docker Engine里调用了containerd,外部的接口仍然保持不变,也就是说还不与CRI兼容。

由于Docker的“固执己见”,这时Kubernetes里就出现了两种调用链:

  • 第一种是用CRI接口调用docker shim,然后docker shim调用Docker,Docker再走containerd去操作容器。
  • 第二种是用CRI接口直接调用containerd去操作容器。

显然,由于都是用containerd来管理容器,所以这两种调用链的最终效果是完全一样的,但是第二种方式省去了docker shim和Docker Engine两个环节,更加简洁明了,损耗更少,性能也会提升一些。

正式“弃用Docker”

如果你理解了前面讲的CRI和containerd这两个项目,就会知道Kubernetes实际上只是“弃用了docker shim”这个小组件,也就是说把docker shim移出了kubelet,并不是“弃用了Docker”这个软件产品。

所以,“弃用Docker”对Kubernetes和Docker来说都不会有什么太大的影响,因为他们两个都早已经把下层都改成了开源的containerd,原来的Docker镜像和容器仍然会正常运行,唯一的变化就是Kubernetes绕过了Docker,直接调用Docker内部的containerd而已。

这个关系你可以参考下面的这张图来理解:

当然,影响也不是完全没有。如果Kubernetes直接使用containerd来操纵容器,那么它就是一个与Docker独立的工作环境,彼此都不能访问对方管理的容器和镜像。换句话说,使用命令docker ps就看不到在Kubernetes里运行的容器了。

这对有的人来说可能需要稍微习惯一下,改用新的工具crictl,不过用来查看容器、镜像的子命令还是一样的,比如ps、images等等,适应起来难度不大(但如果我们一直用kubectl来管理Kubernetes的话,这就是没有任何影响了)。

Kubernetes在1.24版本把docker shim的代码从kubelet里删掉了。

Docker的未来

虽然Kubernetes已经不再包含docker shim,但Docker公司却把这部分代码接管了过来,另建了一个叫cri-docker的项目,作用也是一样的,把Docker Engine适配成CRI接口,这样kubelet就又可以通过它来操作Docker了,就仿佛是一切从未发生过。

0%