点击上方“icloud布道师”,“星标或置顶公众号”


前段时间关于不能使用Docker的消息一直满天飞,那么是不是狼来的故事呢?
接下来,我将尝试着去剖析Docker与Kubernetes的往事风云
Docker被K8S弃用,是真的!
首先,要接受一个既定事实:
Kubernetes在ReleaseNote-1.20[1]明确声明了将丢弃Dockershim

Kubernetes v1.20Release声明
其次,不要过分解读Deprecation
注意:
Kubernetes的这次release声明用的词是deprecation,它不意味着立即停止支持!
Deprecation是不鼓励使用某些术语、特性、设计或实践,通常是因为它已被取代或不再被认为是有效或安全的,而没有完全删除它或禁止使用它。通常,不会完全删除已弃用的材料以确保旧的兼容性或备份实践,以防新方法在奇怪的情况下不起作用。
也就是说,Kuberenetes将来会在某个版本删除或完全停用Dockershim这个功能/设计。
Dockershim是个啥?
要想知道Dockershim是什么意思,首先先来简单了解下Kuberenetes
组织架构

Kubernetes架构
先不谈部署到Google/AWS
等公有云的条件下,我们粗颗粒度的将K8S
集群看成Master
节点和Node
节点。
Master
节点作为控制中心负责下发指令,Node
节点收到指令负责去启动这个Pod
(容器),其实真正去执行这个操作的组件就是Kubelet
,每个节点上都有这个组件。
Kubelet
负责容器的创建、镜像的拉取、容器的运行、停止容器等操作,其实kubelet
也不是真正去执行这个操作的组件。

Kubernetes高级架构
从上图可以看到Kubelet
下面还有一层容器运行时是作为真正和OS
去交互的,这个容器运行时是真正的去管理容器的整个生命周期的以及拉取镜像等操作的。这里面还有一个CRI
的概念需要去了解下:
Container Runtime Interface (CRI)
CRI接口
Kubernetes 节点的最底层是软件实现的,这个软件负责容器的启动和停止。我们称之为“容器运行时”。
最广为人知的容器运行时就是 Docker,但它在这个领域Docker并不是唯一的选手。事实上,容器运行时这个技术领域一直在快速发展。
按照Unix哲学,为了使 Kubernetes 更具可扩展性,我们一直在为 Kubernetes 中的容器运行时开发一个新的插件 API,称为“CRI”。
为什么要引入CRI
目前比较主流的容器运行时有Containerd
、CRI-O
以及PodMan
等,每个容器运行时都有其自身的特性和优势,所以要引入CRI
这个概念以用来支持更多的容器运行时工具,从而实现减少对某一个产品或某一个公司的依赖。
是不是为了防止Docker一家独大呢?
有哪些支持CRI接口的容器运行时
简单的了解下其他的容器运行时工具

目前所有的容器运行时引擎
Containerd
:Docker
引擎分解后的一部分功能组件,Docker
公司并将Containerd
贡献给了CNCF
基金会CRI-O
:Red Hat
联合IBM
等公司共同推出的一个产品,PodMan
就是Red Hat
基于CRI-O
实现的一个替代Docker
的产品
简单了解Docker的发展史
Docker
曾经是一个整体工具包,包含管理容器以及无数开发工具的能力。它包含 CLI、日志记录、存储管理、网络、构建工具以及创建容器的核心能力之外的许多其他功能。
然而,在 Unix
哲学的指导下(OCI规范),Docker 最终分解了这些组件,并将高级容器运行时组件 containerd
贡献给了CNCF[2]。

Docker架构
Dockershim登场
前面我们也说了CRI
的出现以及原因。由于Docker
是早于Kubernetes
出现的(也就是先有的Docker
,后有的Kubernetes
,毋庸置疑),所以Docker是肯定不符合CRI
标准的。
由于当时Docker是容器技术最主流也是最权威的存在,Kuberentes虽然提出了CRI接口规范,但仍然需要去适配CRI与Docker的对接。
因此它需要一个中间层或 shim
来对接Kubelet
和Docker
容器运行时。其实在Kubernetes
提出CRI
这个标准的时候,也就是K8SV1.5
版本的推出的时候自己就实现了Docker CRI shim
,这个时候,容器的启动流程如下:

Docker架构的容器启动流程
需要注意的是:在这个阶段,Kubelet
的代码和dockershim
都是放在一个Repo
的,这也就意味着,Dockershim是由K8S组织进行开发和维护的!
这也为K8S的1.20版本丢弃dockershim埋下伏笔,因为Docker公司的版本发布,K8S组织是无法控制和管理的,所以每次Docker发布新的Release,K8S组织都要集中精力去快速的更新维护Dockershim,否则就无法支持Docker的新版本。
Docker备胎计划推出
前面我们已经说明了Dockershim
的出现以及Dockershim
的困境,在Kubernetes1.5
版本推出不久之后,容器技术领域正在发生着两件事情:
一、Containerd演进
为了减少Kubernetes与Docker容器运行时的调用链开销,Kubernetes
和 containerd
的维护者为 kubelets
添加了一个 CRI shim
,以便直接与 containerd
对话。这使得 Kubernetes
可以去掉 Docker
并直接使用 containerd。当然这在技术上会减少一些docker容器的功能,但这些功能对Kubernetes
来说是毫无意义的。例如,Kubernetes
管理的容器不需要 SSH
访问。
CRI shim这个功能在Containerd1.0
的版本的时候被推出,这个时候,容器的启动流程如下:

Containerd1.0架构的容器启动流程
但是,CRI shim
(CRI-Containerd) 仍然增加了调用链复杂性并降低了容器的安全性。因此开发人员最终将 CRI
作为插件原生添加到 containerd
中,这个时候,容器的启动流程如下:

Containerd1.1架构的容器启动流程
最终的结果就是:Kubernetes创建Pod的启动时长降低了很多,同时在资源利用率上得到了一定的改善。
但是这还不是终点......
二、CRI-O登场
2016 年,CRI-O
被开发为 Docker
的替代品。它领先于 containerd
的发展,从一开始就包含本地 CRI
。这样,kubelet
通过 CRI
直接与 CRI-O
对话以拉取镜像并启动低级容器运行时(例如,runc
),后者依次设置命名空间、cgroup
、根文件系统、存储、几个Linux
安全模块和 conmon
(一种 CRI-O
特定的监控工具)。CRI-O
和 containerd
之间的一个重要区别是删除了一些 Linux
功能,这些功能往往都是Docker
需要的,而不是Kubernetes
所需要的。
Container Runtime总结
在这里,我们简单总结下不同的容器运行时:
从左到右依次是Dockershim
、Containerd1.0
、Containerd1.1
和 CRI-O

Container Runtime对比总结
OCI扩展
OCI
(Open Container Initiative,开放工业标准)的容器运行时规范设定的标准定义了容器运行状态的描述,以及运行时需要提供的容器管理功能, 例如创建、删除和查看等操作。容器运行时规范不受上层结构绑定,不受限于任何特定操作系统、硬件、CPU
架构或公有云等,从而允许任何人遵循该标准开发应用容器技术。
OCI
项目启动后,Docker
公司将2014年开源的libcontainer
项目移交至OCI
组织并进化为runC
项目,成为第一个且目前接受度最广泛的遵循OCI
规范的容器运行时实现。
Docker、CRI-O 和 Containerd 的安全性比较
事实上,对于普通用户来说,切换到新的运行时会产生显着的好处。
目前最主流的三个容器运行时Docker
、CRI-O
和 Containerd
,他们的容器启动过程都是拉取一个镜像,然后启动一个较低级别的运行时(runC
)来配置和启动容器的组件和进程。这些过程中的风险点有:
拉取恶意镜像或过期镜像
在镜像中保留了密码等重要信息
给容器赋予过多权限,例如共享主机命名空间、主机网络或应用特权标志(
--privileged
)通过增加
CPU
、RAM
、网络、IOPS
或磁盘使用量来干扰相邻容器,从而成为“嘈杂的邻居”利用内核漏洞
由于这些运行时的架构差异,每个容器运行时都有一些独特的安全薄弱点。
Docker
是三者中最臃肿的。例如,它包含一个 CLI
和 SSH
守护进程,为攻击者提供了更多访问容器的方法。
Containerd
删除了许多这些功能并显着减少了代码库。通常情况下我们都是通过 Kubernetes
的控制平面进行交互,因此不会错过这些功能。但是,Containerd
仍然有一些不必要的 Linux
功能,例如 audit_write
、mknod
、net_raw
和 sys_chroot
。这些字段或功能是由 Docker
维护者在开发早期定义的,在没有搞清楚这些字段被什么功能依赖前,是不能随意去删除这些功能的。
所以说尽管其攻击面有所减少,但多年来,Containerd
容易受到多次攻击,例如从注册表中提取的镜像中毒[3]和主机网络容器的容器逃逸[4],以及其他攻击媒介。
相比之下,CRI-O
删除了这些 Linux 功能[5]以减少攻击面。尽管有这种额外的保护,CRI-O
并非没有缺点。例如,conmon
是一个有用的监控工具,但也一直是导致容器逃逸漏洞[6]的原因。并且以前的版本没有将TLS 与注册表一起[7]使用,这为中间人攻击提供了机会。
无论选择哪种容器运行时工具,都要提高我们的安全防患意识:
对于大多数 Kubernetes
用户,最好的建议是迁移到 containerd
和 CRI-O
等更加轻量的容器运行时。它们较小的攻击面将更容易保护。无论选择哪一种,请确保:
尽可能频繁地更新。如果您使用的是托管
Kubernetes
服务(Google platform
、AWS
容器服务等),请升级到最新版本。为您的容器使用最小权限模型——避免以
root
身份运行容器,并去除不必要的Linux
功能。确保从受信任的镜像仓库拉取安全的镜像。
不要假设镜像是安全的,因为它是开源的。
确保镜像内的密码信息被妥善处理。
对主机进行安全加固,免受容器突破等攻击。
备胎转正带来的问题和影响
为什么放弃 dockershim?
前面我们已经铺垫或叙述了一些Docker
容器和Kubelet
在CRI
对接上的一些问题。在这里我们再引用Kubernetes
组织官方的的口径进行统一解释:
维护 dockershim
已经成为 Kubernetes
维护者的沉重负担。创建 CRI
标准是为了减轻这种负担并允许不同容器运行时的平滑互操作性。Docker
本身目前没有实现CRI,因此是问题所在。
Dockershim
一直旨在成为一个临时解决方案(因此得名:shim
)。您可以在Dockershim Removal Kubernetes Enhancement Proposal 中[8]阅读有关社区讨论和规划的更多信息 。
此外,在这些较新的 CRI
运行时中有一些正在试验的新特性,这些新特性与 dockershim
在很大程度上是不兼容的,例如 cgroups v2
和用户命名空间。
取消对 dockershim 的支持将允许在这些领域进一步发展。
我还能在 Kubernetes 1.20 中使用 Docker 吗?
是的, 如果使用 Docker
作为运行时,1.20
中唯一改变的是在kubelet[9]启动时打印的单个警告日志。
dockershim 什么时候会被移除?
鉴于此更改的影响,Kubernetes
团队正在使用延长的弃用时间表。它不会在 Kubernetes 1.22
之前被删除,这意味着没有 dockershim
的最早版本将是 2021 年末的 1.23
版本。
同时K8S组织也将和各种云厂商和其他组织开展密切合作,以确保顺利过渡,并将随着情况的发展而进行新的评估:
这里我们简单看下K8S这个发版狂魔的发版速度:

Kubernetes发版记录
所以说留给我们的时间不多了,因为如果你想去用Containerd
或者CRI-O
来替换Docker
引擎,不仅仅要做风险评估,还要富裕出一些时间进行新工具的学习。
但是这也没必要引起恐慌,如果没有十分充足的准备去替换Container Runtime
,我们可以选择另一条路:不升级
今天有没有人在生产中使用其他运行时的例子?
许多托管 Kubernetes
产品已切换到其他容器运行时。例如,OpenShift
于 2019 年 6 月在 OpenShift 4
中切换到CRI-O 作为其默认运行时[10],Azure Kubernetes
服务在 2020 年 1 月将其默认运行时设置为 containerd[11]。
从 Kubernetes 中删除后,我还可以使用 dockershim 吗?
Mirantis
和 Docker
已承诺[12]在从 Kubernetes
中删除 dockershim
后对其进行维护。
我现有的 Docker 镜像还能用吗?
是的,docker build
生成的镜像适用于所有 CRI
容器运行时。
无论你是通过手工编译代码打包构建镜像,还是通过预设的CI流水线完成docker build
这个操作,得到的镜像和你的高级容器运行时(dockershim
、containerd
、cri-o
等)是没有任何关系的。前面我们提到过低级容器运行时runC
,它是目前符合OCI
标准的最流行的低级容器运行时,镜像的生成、上传和下载等操作都是靠runC
去控制完成的,显然Docker
是完全满足OCI
标准的,因为runC
就是Docker
分化出的一个独立项目。所以我们构建出的镜像是完全没有影响的。
Docker 和容器是一回事吗?
Docker
普及了Linux
容器模式,并在开发底层技术方面发挥了重要作用,但 Linux
中的容器已经存在很长时间了。容器生态系统已经发展到比 Docker
更广泛的范围。OCI
和 CRI
等标准帮助许多工具在我们的生态系统中发展壮大,其中一些取代了 Docker
的某些方面,而另一些则增强了现有功能。
对我们的实际工作有什么影响?
实际上,dockershim
的废弃,对不同的IT角色来说影响也是大不一样的!
对开发人员的影响
如果你是一个使用Kubernetes
这个PaaS
平台的开发工程师,或者你是一个DevOps
工程师,亦或是其他间接或直接使用Kubernetes
这个PaaS
平台的IT用户来说,dockershim
的废弃对你来说是没有任何影响的。
对于上述人员来说,涉及到容器和Kubernetes
的工作主要就是镜像打包和容器部署,其中镜像打包是在前面以及提到过了,因为Docker
的底层容器运行时runC
是遵循OCI
规范的,以前构建出来的镜像和将来要构建的镜像都是能兼容其他高级容器运行时的;再说部署部分,通常我们都是通过Kubernetes
的控制面板或yaml
文件(Kubectl CLI
)和Kubernetes
的API
进行通信,所以我们丝毫感觉不到任何影响
对Kubernetes运维人员的影响
如果你是基于AWS
、Alibaba
、Google
等云计算运营商提供的Kubernetes
服务构建出你的集群和应用,那么对你来说也是无感知的,因为底层的容器运行时通常都是这些运营商去提供和维护的。
如果你像我一样,通过Kubeadm
或minikube
等工具在服务器上自建Kubernetes
集群的话,我们要在这些高级容器运行时工具中选择一款,早做打算!
通过我们这篇文章的讲解,我们需要清楚的认识到:
Kubernetes放弃对Docker的支持并不是一个特别突然的计划,这是符合事务发展规律的必然结果!

还需要学习Docker
吗?
References
[1]
ReleaseNote-1.20: https://github.com/kubernetes/kubernetes/blob/bdbf2101eace4a0c8ceb58dcddcca62a1de443de/CHANGELOG/CHANGELOG-1.20.md[2]
CNCF: https://www.cncf.io/projects/[3]
镜像中毒: https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-15157[4]
主机网络容器的容器逃逸: https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-15257[5]
删除了这些 Linux 功能: https://www.youtube.com/watch?v=RoiIx8mcECY[6]
容器逃逸漏洞: https://access.redhat.com/security/cve/cve-2019-14891[7]
TLS 与注册表一起: https://access.redhat.com/security/cve/CVE-2019-10214[8]
Dockershim Removal Kubernetes Enhancement Proposal 中: https://github.com/kubernetes/enhancements/tree/master/keps/sig-node/1985-remove-dockershim[9]
kubelet: https://kubernetes.io/docs/reference/command-line-tools-reference/kubelet/[10]
CRI-O 作为其默认运行时: https://www.redhat.com/en/blog/red-hat-openshift-container-platform-4-now-defaults-cri-o-underlying-container-engine[11]
将其默认运行时设置为 containerd: https://docs.microsoft.com/en-us/azure/aks/cluster-configuration[12]
承诺: https://www.mirantis.com/blog/mirantis-to-take-over-support-of-kubernetes-dockershim-2/
参考资料:
https://www.youtube.com/watch?v=7KUdmFyefSA
https://kubernetes.io/blog/2016/12/container-runtime-interface-cri-in-kubernetes/
https://thenewstack.io/a-security-comparison-of-docker-cri-o-and-containerd/
https://kubernetes.io/blog/2020/12/02/dont-panic-kubernetes-and-docker/
https://kubernetes.io/blog/2020/12/02/dockershim-faq/





