阿里、蚂蚁、晟腾、中科加禾精彩分享 AI 基础设施洞见,现购票可享受 9 折优惠 |AICon 了解详情
写点什么

为什么 Kubernetes 要替换 Docker

  • 2021-03-23
  • 本文字数:3699 字

    阅读完需:约 12 分钟

为什么 Kubernetes 要替换 Docker

为什么这么设计(Why’s THE Design)是一系列关于计算机领域中程序设计决策的文章,我们在这个系列的每一篇文章中都会提出一个具体的问题并从不同的角度讨论这种设计的优缺点、对具体实现造成的影响。如果你有想要了解的问题,可以在文章下面留言。


Kubernetes 是今天容器编排领域的事实标准,而 Docker 从诞生之日到今天都在容器中扮演着举足轻重的地位,也都是 Kubernetes 中的默认容器引擎。然而在 2020 年 12 月,Kubernetes 社区决定着手移除仓库中 Dockershim 相关代码1,这对于 Kubernetes 和 Docker 两个社区来说都意义重大。



图 1 - Dockershim

相信大多数的开发者都听说过 Kubernetes 和 Docker,也知道我们可以使用 Kubernetes 管理 Docker 容器,但是可能没有听说过 Dockershim,即 Docker 垫片。如上图所示,Kubernetes 中的节点代理 Kubelet 为了访问 Docker 提供的服务需要先经过社区维护的 Dockershim,Dockershim 会将请求转发给管理容器的 Docker 服务。


其实从上面的架构图中,我们就能猜测出 Kubernetes 社区从代码仓库移除 Dockershim 的原因:


  • Kubernetes 引入容器运行时接口(Container Runtime Interface、CRI)隔离不同容器运行时的实现机制,容器编排系统不应该依赖于某个具体的运行时实现;

  • Docker 没有支持也不打算支持 Kubernetes 的 CRI 接口,需要 Kubernetes 社区在仓库中维护 Dockershim;

可扩展性

Kubernetes 通过引入新的容器运行时接口将容器管理与具体的运行时解耦,不再依赖于某个具体的运行时实现。很多开源项目在早期为了降低用户的使用成本,都会提供开箱即用的体验,而随着用户群体的扩大,为了满足更多定制化的需求、提供更强的可扩展性,会引入更多的接口。Kubernetes 通过下面的一系列接口为不同模块提供了扩展性:



图 2 - Kubernetes 接口和可扩展性

Kubernetes 在较早期的版本中就引入了 CRD、CNI、CRI 和 CSI 等接口,只有用于扩展调度器的调度框架是 Kubernetes 中比较新的特性。我们在这里就不展开分析其他的接口和扩展了,简单介绍一下容器运行时接口。


Kubernetes 早在 1.3 就在代码仓库中同时支持了 rkt 和 Docker 两种运行时,但是这些代码为 Kubelet 组件的维护带来了很大的困难,不仅需要维护不同的运行时,接入新的运行时也很困难;容器运行时接口(Container Runtime Interface、CRI)是 Kubernetes 在 1.5 中引入的新接口,Kubelet 可以通过这个新接口使用各种各样的容器运行时。其实 CRI 的发布就意味着 Kubernetes 一定会将 Dockershim 的代码从仓库中移除。


CRI 是一系列用于管理容器运行时和镜像的 gRPC 接口,我们能在它的定义中找到 RuntimeService 和 ImageService 两个服务2,它们的名字很好地解释了各自的作用:


service RuntimeService {    rpc Version(VersionRequest) returns (VersionResponse) {}
rpc RunPodSandbox(RunPodSandboxRequest) returns (RunPodSandboxResponse) {} rpc StopPodSandbox(StopPodSandboxRequest) returns (StopPodSandboxResponse) {} rpc RemovePodSandbox(RemovePodSandboxRequest) returns (RemovePodSandboxResponse) {} rpc PodSandboxStatus(PodSandboxStatusRequest) returns (PodSandboxStatusResponse) {} rpc ListPodSandbox(ListPodSandboxRequest) returns (ListPodSandboxResponse) {}
rpc CreateContainer(CreateContainerRequest) returns (CreateContainerResponse) {} rpc StartContainer(StartContainerRequest) returns (StartContainerResponse) {} rpc StopContainer(StopContainerRequest) returns (StopContainerResponse) {} rpc RemoveContainer(RemoveContainerRequest) returns (RemoveContainerResponse) {} rpc ListContainers(ListContainersRequest) returns (ListContainersResponse) {} rpc ContainerStatus(ContainerStatusRequest) returns (ContainerStatusResponse) {} rpc UpdateContainerResources(UpdateContainerResourcesRequest) returns (UpdateContainerResourcesResponse) {} rpc ReopenContainerLog(ReopenContainerLogRequest) returns (ReopenContainerLogResponse) {}
...}
service ImageService { rpc ListImages(ListImagesRequest) returns (ListImagesResponse) {} rpc ImageStatus(ImageStatusRequest) returns (ImageStatusResponse) {} rpc PullImage(PullImageRequest) returns (PullImageResponse) {} rpc RemoveImage(RemoveImageRequest) returns (RemoveImageResponse) {} rpc ImageFsInfo(ImageFsInfoRequest) returns (ImageFsInfoResponse) {}}
复制代码


对 Kubernetes 稍有了解的人都能从上面的定义中找到一些熟悉的方法,它们都是容器运行时需要暴露给 Kubelet 的接口。Kubernetes 将 CRI 垫片实现成 gRPC 服务器与 Kubelet 中的客户端通信,所有的请求都会被转发给容器运行时处理。



图 3 - Kubernetes 和 CRI

Kubernetes 中的声明式接口非常常见,作为声明式接口的拥趸,CRI 没有使用声明式的接口是一件听起来『非常怪异』的事情3。不过 Kubernetes 社区考虑过让容器运行时重用 Pod 资源,这样容器运行时可以实现不同的控制逻辑来管理容器,能够极大地简化 Kubelet 和容器运行时之间的接口,但是社区出于以下两点考虑,最终没有选择声明式的接口:


  1. 所有的运行时都需要重新实现相同的逻辑支持很多 Pod 级别的功能和机制;

  2. Pod 的定义在 CRI 设计时演进地非常快,初始化容器等功能都需要运行时的配合;

虽然社区最终为 CRI 选择了命令式的接口,但是 Kubelet 仍然会保证 Pod 的状态会不断地向期望状态迁移。

不兼容接口

与容器运行时相比,Docker 更像是一个复杂的开发者工具,它提供了从构建到运行的全套功能。开发者可以很快地上手 Docker 并在本地运行并管理一些 Docker 容器,然而在集群中运行的容器运行时往往不需要这么复杂的功能,Kubernetes 需要的只是 CRI 中定义的那些接口。



图 4 - Docker & CRI

Docker 的官方文档加起来可能有一本书的厚度,相信没有任何开发者可以熟练运用 Docker 提供的全部功能。而作为开发者工具,虽然 Docker 中包含 CRI 需要的所有功能,但是都需要实现一层包装以兼容 CRI。除此之外,社区提出的很多新功能都没有办法在 Dockershim 中实现,例如 cgroups v2 以及用户命名空间。


Kubernetes 作为比较松散的开源社区,每个成员尤其是各个 SIG 的成员都只会在开源社区上花费有限的时间,而维护 Kubelet 的 sig-node 又尤其繁忙,很多新的功能都因为维护者没有足够的精力而被搁置,所以既然 Docker 社区看起来没有打算支持 Kubernetes 的 CRI 接口,维护 Dockershim 又需要花费很多精力,那么我们就能理解为什么 Kubernetes 会移除 Dockershim 了。

总结

今天的 Kubernetes 已经是非常成熟的项目,它的关注点也逐渐从提供更完善的功能转变到提供更好的扩展性,这样才能满足不同场景和不同公司定制化的业务需求。Kubernetes 在过去因为 Docker 的热门而选择 Docker,而在今天又因为高昂的维护成本而放弃 Docker,我们能够从这个过程中体会到容器领域的发展和进步。


移除 Docker 的种子其实从 CRI 发布时就种下了,Dockershim 一直都是 Kubernetes 为了兼容 Docker 获得市场采取的临时决定,对于今天已经统治市场的 Kubernetes 来说,Docker 的支持显得非常鸡肋,移除代码也就顺理成章了。我们在这里重新回顾一下 Kubernetes 在仓库中移除 Docker 支持的两个原因:

  • Kubernetes 在早期版本中引入 CRI 摆脱依赖某个具体的容器运行时依赖,屏蔽底层的诸多实现细节,让 Kubernetes 能够更关注容器的编排;

  • Docker 本身不兼容 CRI 接口,而且官方并没有实现 CRI 的打算,同时也不支持容器的一些新需求,所以 Dockershim 的维护成为了社区的想要摆脱负担;


到最后,我们还是来看一些比较开放的相关问题,有兴趣的读者可以仔细思考一下下面的问题:

  • Kubernetes 中还有哪些模块提供良好的扩展性?

  • 除了文中提到的 CRI-O、Containerd,还有哪些支持 CRI 的容器运行时?


如果对文章中的内容有疑问或者想要了解更多软件工程上一些设计决策背后的原因,可以在博客下面留言,作者会及时回复本文相关的疑问并选择其中合适的主题作为后续的内容。


参考资料


  1. Removing dockershim from kubelet #1985 https://github.com/kubernetes/enhancements/pull/1985 ↩︎

  2. Container Runtime Interface (CRI) – a plugin interface which enables kubelet to use a wide variety of container runtimes. https://github.com/kubernetes/cri-api/blob/master/pkg/apis/runtime/v1/api.proto ↩︎

  3. Introducing Container Runtime Interface (CRI) in Kubernetes https://kubernetes.io/blog/2016/12/container-runtime-interface-cri-in-kubernetes/ ↩︎


本文转载自:Draveness

原文链接:为什么 Kubernetes 要替换 Docker

2021-03-23 08:005083

评论 1 条评论

发布
用户头像
把docker弄死了google就彻底杀死了心头大患了
2021-03-24 09:15
回复
没有更多了
发现更多内容

记一次 MySQL 主从同步异常的排查记录,百转千回

做梦都在改BUG

Java MySQL 主从同步

面试官:如何动态的给一个类增加功能?

做梦都在改BUG

原来XXL-JOB可以这么造

六月的雨在InfoQ

Serverless XXL-JOB SAE 三周年连更

直播预告 | TDengine & Apache SeaTunnel 联合应用最佳实践

TDengine

Apache tdengine 时序数据库

如何解决spring的循环依赖问题?

做梦都在改BUG

Java spring 循环依赖

如何将一个链表分组并对每组进行反转?

Java你猿哥

Java 链表 架构师 SSM框架 链表结构

镭速Raysync v6.6.8.0版本发布

镭速

Github上标星68.5K,阿里巴巴《MySQL成长手册》火了

做梦都在改BUG

Java MySQL 数据库

人工智能会取代人类成为地球的主宰么?| 社区征文

毛小毛

人工智能 ChatGPT 三周年征文

阿里高工纯手写的《分布式架构手册》仅仅一天GitHub就标星128K

Java你猿哥

架构 分布式 分布式架构

从零开始学习 GraphQL:入门指南和教程

Apifox

程序员 gRPC 后端 协议 graphql

数说热点|春暖花开日,露营正当时——当精致露营遇上新能源车

MobTech袤博科技

Spring Cloud微服务网关Zuul过滤链实现的源码解读

做梦都在改BUG

ChatGPT的狂欢背后:短板犹在,启发甚多,2023有这些事可以做……

小红书技术REDtech

人工智能 ChatGPT 活动回顾

Linux 修改系统时间的两种方式

会踢球的程序源

Java Linux

从此告别写SQL!DataLeap帮你零门槛完成“数据探查”

字节跳动数据平台

MySQL 数据分析 数据治理 数据探查 企业号 4 月 PK 榜

对话大咖 | 人工智能+视频编解码能带来新的变革吗

小红书技术REDtech

人工智能 深度学习 活动回顾

阿里耗时1年:用283张图+24问完美诠释“Java并发编程”所有难点

做梦都在改BUG

Java 并发编程

Postman Runner 使用指南

Liam

Java 后端 Postman 接口测试 API 开发

高性能存储SIG月度动态:ANCK ublk完成POC测试,EROFS优化xattr元数据开销

OpenAnolis小助手

操作系统 内核 高性能存储 龙蜥社区 sig

eBPF动手实践系列一:解构内核源码eBPF样例编译过程

阿里云大数据AI技术

运维 代码 企业号 4 月 PK 榜

大咖力荐 |《中国企业软件研发管理白皮书》为什么值得看?

万事ONES

SpringBoot设计了哪些可拓展的机制?

做梦都在改BUG

OpenHarmony3.2release抢先体验

坚果

OpenHarmony 三周年连更

面试了个阿里P7大佬,他让我见识到什么才是“精通高并发与调优”

做梦都在改BUG

Java 高并发 性能调优

RocketMQ 多级存储设计与实现

阿里巴巴云原生

阿里云 RocketMQ 云原生

Databend v1.1 版本发布!

Databend

阿里P8:Java性能调优(独孤版),带你打造淘宝秒杀架构

做梦都在改BUG

Java 架构 性能调优

Seata:连接数据与应用

阿里巴巴云原生

阿里云 云原生 seata

优秀的pdf编辑器:Acrobat Pro DC 中文直装版

真大的脸盆

Mac PDF Mac 软件 PDF格式转换

【经验分享】硬件工程师需要知道的DFM可制造性设计

华秋PCB

工具 电路 PCB PCB设计 可制造性

为什么 Kubernetes 要替换 Docker_语言 & 开发_面向信仰编程_InfoQ精选文章