K8s -- 为什么弃用 docker?什么是 cri ? 什么是 shim
在 K8s 1.24 弃用了 docker ,为什么?
CRI(容器运行时接口)
要理解 K8s 为什么“弃用 Docker”,我们还得回顾一下 K8s 的发展历史。
2014年,Docker如日中天,K8s刚刚诞生。虽然它得到了 Google 和 Borg 的支持,但它仍然相对较新并且没有很大的社区。
因此,K8s 自然而然地选择运行在 Docker 上。毕竟“大树背阴好”,同时也可以借机“养精蓄锐”,逐渐发展壮大自己。
时间快进到2016年,CNCF已经成立一年了,K8s也发布了1.0版本,可以正式用于生产环境。这些都预示着K8s已经成长起来了。
于是宣布加入CNCF,成为第一个CNCF托管项目。它想借助基金会的力量联合其他厂商“打倒”Docker。
在2016年底的1.5版本中,K8s引入了新的接口标准:CRI:Container Runtime Interface。
CRI 使用 ProtoBuffer 和 gPRC 来指定 kubelet 应该如何调用容器运行时来管理容器和 image,但这是一组新的接口,与以前的 Docker 调用完全不兼容。
显然它不想再被Docker束缚,底层允许接入其他容器技术(如rkt、kata等),随时可以“踢开”Docker。
但是此时的Docker已经很成熟了,市场的惯性也很强。各大云厂商不可能一下子全部取代Docker。
因此,K8s只能同时提供一个“折衷”的解决方案,在kubelet和Docker之间增加一个“适配器”,将Docker的接口转换为符合CRI的接口:
因为这个“适配器”夹在 kubelet 和 Docker之间,所以被形象地称为“shim”,意思是“垫片”。
有了 CRI 和 shim,虽然 K8s 仍然使用 Docker 作为底层运行时,但也具备了与 Docker 解耦的条件,拉开了“弃用 Docker”大戏的序幕。
Containerd
面对挑战,Docker 采取了“断臂求生”的策略,将原来单一架构的 Docker Engine拆分为多个模块,其中 Docker daemon 部分捐赠给 CNCF并组建 containerd。
作为CNCF的托管项目,containerd必须遵守CRI标准。但是由于很多原因,Docker 只是在 Docker Engine 中调用了 containerd,对外接口保持不变,也就是说不兼容CRI。
由于Docker的“固执”,此时 K8s 中有两条调用链:
使用 CRI 接口调用 dockershim,然后 dockershim 调用 Docker,Docker 再去 containerd 操作容器。
使用 CRI 接口直接调用 containerd 操作容器。
显然,因为 containerd 是用来管理容器的,所以这两个调用链的最终效果是完全一样的,但是第二种方法去掉了 dockershim 和 Docker Engine 的两个环节,更简洁明了,性能更好。
2018 年 Kubernetes 1.10 发布时,containerd 也更新到1.1 版本,正式与 Kubernetes 集成,并发布博文(https://kubernetes.io/blog/2018/05/24/kubernetes-containerd-integration- goes-ga/ ),展示一些性能测试数据:
从这些数据可以看出,相比当时的 Docker 18.03,containerd1.1 降低了 Pod启动延迟20%左右,CPU占用率降低了68%,内存占用率降低了12%,这是一个相当可观的性能提升,对于云供应商来说是非常有诱惑力的。
官方 Docker 弃用
两年后的 2020 年,K8s 1.20 终于正式向 Docker “宣战”:将kubelet弃用 Docker 支持,并在未来的版本中彻底移除。
但由于 Docker 几乎已经成为容器技术的代名词,而 K8s 已经使用 Docker 多年,所以随着公告的传播“kubelet will deprecate Docker support”被简化为更吸引眼球的“K8s will deprecate docker”。
这自然引起了IT界的恐慌,“不明真相的群众”纷纷表示震惊:用了这么久的 Docker 突然不能用了。为什么 K8s 会这样对待 Docker?之前对 Docker 的投资会归零吗?现有的大量镜像怎么办?
其实,如果你了解了上面提到的这两个项目 CRI,containerd 就会知道K8s的这一举动并不奇怪,一切都是“自然而然”的:它其实只是“弃用了dockershim ”,即移除 dockershim,kubelet 并不是一个“弃用 Docker”的软件产品。
因此,“弃用Docker”对 K8s 和 Docker 不会有太大影响,因为它们的底层都是开源的 containerd,原有的 Docker 镜像和容器依然会正常运行。唯一的变化是K8s绕过了Docker,直接containerd 而不是借用 Docker 调用。
但是,也会有一些影响。如果 K8s 直接使用 containerd 来操作容器,那么它是一个独立于 Docker 的工作环境,两者都不能访问对方管理的容器和镜像。也就是说,使用命令 docker ps是看不到K8s中运行的容器的。
这对于某些人来说可能需要一点时间来适应并使用新工具 crictl,但是用于查看容器和 image 的子命令仍然是相同的,例如:ps,images等,适应起来并不困难(但如果我们如果你一直用kubectl管理k8s,这个没有效果)。
K8s 原本计划用一年的时间来完成“弃用 Docker”的工作,但它确实低估了 Docker 的根基。1.23版本还是没能去掉dockershim,只好推迟了半年。最后,今年 5 月发布的 1.24 版本将代码dockershim从kubelet.
自此,Kubernetes 与 Docker 彻底“分道扬镳”。
Docker的未来
那么,Docker 的未来会怎样?云原生时代就没有立足之地了吗?这个问题的答案显然是否定的。
作为容器技术的奠基者,没有人可以质疑Docker的历史地位。虽然 K8s 默认不再绑定 Docker,但 Docker 仍然可以通过其他形式与 K8s 共存。
首先,由于容器镜像格式已经标准化(OCI规范,Open Container Initiative),Docker 镜像在 K8s 中仍然可以正常使用,原有的开发测试和CI/CD流程不需要改变。我们仍然可以拉取 Docker Hub ,或者写一个 Dockerfile 来打包应用程序。
其次,Docker是一个完整的软件产品线,不仅包含 containerd,它还包含了镜像构建、分发、测试等诸多服务,甚至K8s都内置在了Docker Desktop中。
就容器开发的便利性而言,Docker暂时还很难被取代。广大云原生开发者可以继续在这个熟悉的环境中工作,使用Docker开发运行在K8s上的应用。
同样,虽然K8s不再包含dockershim,但Docker已经接管了这部分代码并构建了一个名为 cri-dockerd(https://github.com/mirantis/cri-dockerd)的项目,该项目也同样有效,将 Docker Engine 适配为 CRI 接口,这样 kubelet 就可以通过它再次操作 Docker,就好像什么都没发生过一样。
整体来看,虽然Docker在容器编排大战中败北,被K8s挤到墙角,但依然拥有强大的生命力。多年积累的众多忠实用户和大量应用镜像是其最大的资本和后盾。足以支持它走另一条不与 K8s 正面交锋的道路。
对于初学者来说,Docker简单易用,拥有完整的工具链和友好的界面,市面上很难找到与之相媲美的软件。应该说是入门学习容器技术和云原生的“最佳选择”。
翻译自:
共 0 条评论