在理解CRI之前我们回顾一下docker的运行流程,才能明白为什么k8s要引入CRI。
Docker
我们可以发现其实docker就是通过containerd创建容器的,当执行 docker run 时,Docker 守护进程并不会直接创建容器,而是通过 containerd 来管理容器的生命周期。而 containerd 本身也不会直接创建容器,而是通过其内置组件 containerd-shim 调用 runc 来实际创建和运行容器。为什么要搞这么复杂呢?这种设计是为了解决实际问题和满足现代容器化技术的需求。首先将各个部分分离出来,模块化设计,每一个模块仅需专注自己的事情,如docker只需要专注于与用户交互,实现其高级功能,containerd只需要负责负责容器的生命周期管理,而containerd-shim则是将containerd与runC容器运行时隔离起来,确保即使容器运行时崩溃,也不会影响 containerd 的稳定性,同时增加可拓展性,因为可以通过更换containerd-shim和runC切换不同的容器运行时,如谷歌的gVisor或者蚂蚁的kata container。
CRI
了解了docker后再来看看什么是CRI, Kubernetes 在 2016 年标准化接口CRI,k8s1.5之前k8s直接调用docker daemon创建容器,Kubernetes 的代码与 Docker 紧密耦合,难以支持其他容器运行时。在这之后只要你的容器实现了CRI就可以与k8s交互,就可以被k8s调度。但是理想很丰满现实很骨感,那时docker如日中天,k8s还相对没那么出名,所以k8s官方自己维护了一个叫做docker-shim的组件,来实现docker与k8s的交互。时过境迁后来k8s也爆火了,所以在2020年k8s就提出停止维护dokcer-shim,所以在1.20.x版本后就默认没有docker-shim,在1.21.x~1.23.x需要你手动安装docker-shim,从1.24.x起完全移除docker-shim。那是不是在这之后不能再用doker呢?并不是的docker的母公司为适配k8s,自行维护了一个插件cri-dockerd(https://github.com/Mirantis/cri-dockerd.git)如果你想用docker还是可以的。那为什么k8s官方还是推荐使用containerd作为容器呢?
1.20.x之前(包括1.20.x) | 自带docker-shim |
1.21.x~1.23.x | 需手动安装docker-shim |
1.24.x后 | 需手动安装cri-dockerd |
containerd
接下来让我们再看看containerd,其实containerd就是docker团队开发的,并于2017年开源同年又捐给云原生基金会CNCF(有人说是docker发现自家的容器编排工具swarm打不过k8s,为扩大影响力而断臂求生,与k8s同属一个基金会)。上文搞清楚docker运行流程后,我们发现其实k8s直接管理containerd不就好了吗,根本不需要多此一举的管理docker,所以2017年cri-containerd就诞生了,他就是联系CRI和containerd的桥梁,在诞生之初它是作为一个项目单独维护,而在2019年containerd1.2.x版本后containerd原生支持cri,被当作一个插件直接内置到containerd主进程中。
那么我们现在搞清楚了dokcer和containerd以及CRI间的关系,如下:
cri-o
但是这个流程还可以简化如上图出现的cri-o,因为即使新版本的containerd内置插件来支持cri但是还是不够轻量,所以cri-c孕育而生。它是由Red Hat主导开发专门为k8s的cri设计完全符合cri 规范的容器运行时且符合OCI,它更加轻量级减少了不必要的开销。以下是chatgpt给出的对比
总结
-
CRI 的作用:解耦 Kubernetes 和容器运行时,支持多种容器运行时。
-
containerd
:由 Docker 团队开发,原生支持 CRI,功能全面。 -
CRI-O
:专为 Kubernetes 设计,更加轻量级,符合 CRI 和 OCI 规范。 -
dockershim
的演变:Kubernetes 逐渐移除对 Docker 的直接支持,推荐使用containerd
或CRI-O
。