在写“K8s”系列文章的过程中,很多读者留言询问 K8s 弃用 Docker 的事,担心现在学习 Docker 是否还值得,是不是该切换到 containerd 或其他运行时。
这些怀疑有一定的道理。两年前,K8s 发布“弃用 Docker”的消息时,确实在社区引起了“轩然大波”,影响甚至蔓延到了社区之外,K8s 不得不写了好几篇博客来重复解释原因。
两年过去了,虽然 K8s 1.24 已经实现了“弃用 Docker”的目标,但很多人似乎对这一点还不是很清楚。所以本篇文章就来聊聊这个话题。
要理解 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
andgPRC
来指定kubelet
应该如何调用容器运行时来管理容器和镜像,但这是一组与以前的 Docker 调用完全不兼容的新接口。
显然它不想再和 Docker 绑定,在底层允许访问其他容器技术(如 rkt、kata 等),可以随时“踢开” Docker。
但此时 Docker 已经非常成熟,市场的惯性也非常强。各大云厂商不可能一下子全部替换掉 Docker。
因此,K8s 只能同时提供一种“折中”的方案,在kubelet
和 Docker 之间增加一个“适配器”,将 Docker 的接口转换为 CRI 兼容的接口:
因为这个“适配器”夹在kubelet
Docker 和 Docker 之间,所以形象地称为“shim”,意思是“垫片”。
有了 CRI 和 shim,虽然 K8s 仍然使用 Docker 作为底层运行时,但它也具备了与 Docker 解耦的条件,从而拉开了“弃用 Docker”大戏的帷幕。
面对挑战,Docker 采取了“断臂求生”的策略,推动自身重构,将原有单一架构的 Docker Engine 拆分成多个模块,其中 Docker daemon 部分捐赠给 CNCF,containerd
形成。
作为 CNCF 的托管项目,containerd
必须符合 CRI 标准。但是由于很多原因,Docker 只是 containerd
在 Docker Engine 中调用,对外的接口保持不变,也就是说不兼容 CRI。
由于 Docker 的“固执”,此时 K8s 中有两条调用链:
dockershim
,然后 dockershim
调用 Docker,Docker 再去 containerd
操作容器。containerd
操作容器。显然,因为 containerd
是用来管理容器的,所以这两个调用链的最终效果是完全一样的,但是第二种方法去掉了 dockershim
和 Docker Engine 这两个环节,更加简洁明了,性能也更好。
2018 年 Kubernetes 1.10 发布时,containerd 也更新到 1.1 版本,正式与 Kubernetes 集成,并发表[博文](https://kubernetes.io/blog/2018/05/24/kubernetes-containerd-integration- gos-ga/ "博文")显示一些性能测试数据:
从这些数据可以看出,相比当时的 Docker 18.03,containerd1.1
Pod 启动延迟降低了 20% 左右,CPU 使用率降低了 68%,内存使用率降低了 12%,这样可观的性能提升对云厂商来说是非常有诱惑力的。
2020 年,K8s 1.20 终于正式向 Docker “宣战”:kubelet
将弃用 Docker 支持,并将在未来的版本中完全移除。
但由于 Docker 几乎已经成为容器技术的代名词,而且 K8s 已经使用 Docker 多年,该公告在传播时很快“变味了”,“kubelet
将弃用 Docker 支持”被简化为更吸人眼球的东西 “K8s 将弃用”Docker”。
这自然引起了 IT 界的恐慌,“不明真相的群众”纷纷表示震惊:
用了这么久的 Docker 突然不能用了。
为什么 K8s 会这样对待 Docker?
之前对 Docker 的投资会归零吗?现有的大量镜像怎么办?
其实,如果你了解了上面提到的这两个项目CRI
,containerd
你就会知道,K8s 的这一举动并不奇怪,一切都是“自然”的:其实只是“弃用 dockershim
”,也就是dockershim
搬出kubelet
,并不是“弃用 Docker”的软件产品。
因此,“弃用 Docker”对 K8s 和 Docker 的影响不大,因为它们都已经将底层改为开源containerd
,原有的 Docker 镜像和容器仍然可以正常运行。唯一的变化是K8s绕过了Docker,直接调用Docker内部的containerd
。
然而,还是会有一些影响。如果K8s直接使用containerd
来操作容器,那么它就是一个独立于Docker的工作环境,两者都无法访问对方管理的容器和镜像。换句话说,使用docker ps
命令将不会看到K8s中运行的容器。
这对一些人来说可能需要花一点时间来适应并使用新工具crictl
,但用于查看容器和镜像的子命令仍然是相同的,例如ps
,images
等,不难适应(如果你一直在用kubectl管理K8s,这个没有影响)。
K8s 原本计划用一年时间完成“弃用 Docker”的工作,但它确实低估了 Docker 的基础。1.23版本还是没能移除dockershim
,只好延期半年。最后,1.24版本从kubelet
中删除了dockershim
的代码。
从此,Kubernetes 与 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
的项目,该项目也同样工作,将 Docker Engine 适配为 CRI 接口,这样就kubelet
可以通过它再次操作Docker,就好像它从来没有发生过一样。
总的来说,Docker虽然在容器编排大战中败下阵来,被K8s挤到了墙角,但依然具有很强的生命力。多年积累的众多忠实用户和大量应用形象是其最大的资本和后盾。足以支持它在另一条不与 K8s 正面交锋的道路上。
对于初学者来说,Docker简单易用,工具链完整,界面友好,市面上很难找到与之相媲美的软件。应该说是入门级学习容器技术和云原生的“最佳选择”。
本文译自:K8s — Why Deprecating Docker?[1]作者:Tony
[1]
K8s — Why Deprecating Docker?: https://medium.com/nerd-for-tech/k8s-why-deprecating-docker-5d1eccde0775"