Docker、containerd、CRI、shim 之间的关系

Docker、docker-ce、containerd、CRI、CRI-O、shim 是啥关系?顺便把"以前那条链路"也讲透

聊容器运行时,最常见的困惑就是:

"我以前装个 Docker 就能跑容器,怎么现在 Kubernetes 又说 containerd,又说 CRI,还冒出个 shim?Docker 不是运行时吗?那 docker-ce 又是什么?CRI-O 又是哪路神仙?"

说白了,这不是谁取代谁,而是 K8s 把容器这摊事拆得更清楚了:上层只管提需求,中间用标准接口对接,底层专心把进程跑起来,再用 shim 把运行过程稳住。

你把"现在的链路"和"以前的链路"放一起看,就不会乱。

先把几条链路摆出来:一眼就懂它们在换什么

以前(K8s 早期常见,强绑定 Docker):
kubelet → dockershim → Docker Engine(dockerd) → containerd → runc → 容器进程

现在(K8s 主流之一:containerd 直接对接):
kubelet → CRI → containerd → shim → runc → 容器进程

现在(另一条主流:CRI-O 对接):
kubelet → CRI → CRI-O →(shim/OCI runtime)→ runc → 容器进程

看出来了吗?底层终点几乎都一样,都是 runc + 容器进程。变化主要在上层两件事:

  • 以前 kubelet 需要靠 dockershim 去"翻译"Docker 的接口。

  • 现在 kubelet 只认 CRI,至于底层是 containerd 还是 CRI-O,随你选(只要实现 CRI)。

Docker 和 docker-ce:一个是"概念/产品体系",一个是"你安装的发行版"

很多人把 Docker 当成一个单一组件,其实它更像一个产品集合:CLI、镜像构建、镜像分发、运行容器、网络、卷、日志、API......早期是一条龙,确实省心。

那 docker-ce 是什么?简单讲就是:

Docker 的社区版发行方式/安装包。

你在 Linux 上 yum install docker-ce / apt install docker-ce 装的那套,通常就包含:

  • docker CLI(你敲命令用的)

  • dockerd(Docker Engine)

  • 以及它依赖的一些底层组件(例如 containerd、runc 等)

所以 docker-ce 不是另一种运行时,而是 Docker 这个体系在社区版里怎么被打包交付。

Docker:以前像"一条龙",现在更像"上层工具箱"

Docker 最早为什么火?因为它把一堆事做成了一条龙:

  • build 镜像、pull/push 镜像

  • run 容器

  • 网络、卷、日志、API 都一起包好

体验当然好,但问题也很现实:太大、太全、太像一个产品。

Kubernetes 需要的是一个更"专注跑容器"的底层能力,而不是一个把所有功能都打包的巨无霸。

所以今天很多场景里 Docker 的定位更像:

开发和运维常用的容器工具箱。

在集群里真正负责跑容器的核心,往往不是"Docker 的大引擎",而是它下面的运行时组件(containerd/runc)。

containerd:专职"管理容器生命周期"的运行时核心

containerd 可以理解成:专心把容器跑起来,别的尽量少管。它负责的都是硬活:

  • 拉镜像、解压、管理 snapshot

  • 创建/启动/停止/删除容器

  • 调用更底层 runtime(比如 runc)把容器进程真正拉起来

  • 管理容器 I/O、状态等

所以你现在看到 Kubernetes 默认倾向 containerd,并不奇怪:它更轻、更专注、更符合"运行时组件"该有的样子。

CRI:Kubernetes 的"标准插槽",不是运行时

CRI(Container Runtime Interface)最容易被误解成某个程序,其实它是接口标准。

kubelet 为什么要 CRI?一句话:不想跟某一个运行时绑死。

如果 kubelet 直接适配 Docker、适配 containerd、适配 CRI-O......那 kubelet 的代码得多乱?维护成本得多高?

所以 Kubernetes 的做法是:

我 kubelet 只会说 CRI 这门语言,你们运行时自己来实现。

于是现在链路变得很干净:
kubelet → CRI →(containerd / CRI-O / 其他 runtime)

CRI-O:为 Kubernetes 而生的运行时实现(更"纯粹")

CRI-O 你可以理解成:

专门为 Kubernetes 设计的 CRI 运行时。

它不像 Docker 那样做镜像构建体验,也不像 Docker 那样带一堆平台功能。CRI-O 的目标非常明确:

  • 实现 CRI,让 kubelet 能用标准接口管理容器

  • 遵循 OCI,调用 runc 等 runtime 拉起容器进程

  • 尽量保持轻量、专注、贴合 K8s

"containerd 和 CRI-O 有啥区别?"

两者都能当 K8s 运行时,但 CRI-O 更像"为了 K8s 而定制的后端",而 containerd 更像"通用型运行时管理器"。

shim:为什么要有"垫片"?为了稳,不是为了多一层

shim 听起来像"中间商",但它解决的都是生产里非常实在的问题。

以 containerd 为例,一个容器通常会对应一个 shim(例如 containerd-shim-runc-v2)。它的价值主要在三点:

让容器不依赖 containerd 的生命周期

你真的希望 containerd 重启一下,所有容器跟着死吗?当然不希望。

shim 让容器进程可以继续跑,containerd 重启后再重新接管。

处理容器 I/O 和退出状态

stdout/stderr 谁接?容器退出后 exit code 谁收?状态怎么回报?

这些都需要一个"离容器很近、一直在线"的管家,shim 就干这个。

隔离底层 runtime 的细节

containerd 不必直接黏着 runc 的各种细节,通过 shim 隔离开,系统更稳,演进也更容易。

所以 shim 的存在,核心就是两个字:稳定。

以前那条链路:dockershim 到底是干嘛的?为什么后来不用了?

早期 Kubernetes 很依赖 Docker 生态,但 kubelet 需要一种方式驱动 Docker,于是有了 dockershim。

你可以把 dockershim 理解成:

kubelet 和 Docker Engine(dockerd) 之间的翻译官/胶水层。

它把 kubelet 的需求转成 Docker 能理解的调用方式,让 Kubernetes 能"用 Docker 跑 Pod"。

所以以前链路是:
kubelet → dockershim → dockerd → containerd → runc → 容器进程

有趣的是:很多人以为 Docker 直接跑容器,但 Docker Engine 下面早就有 containerd/runc。底层能力一直在那里,只是以前你不需要关心。

那为什么 dockershim 后来逐步退场?也很现实:

  • Kubernetes 不想和 Docker 的实现细节强绑定

  • 运行时生态更丰富了(containerd、CRI-O 等)

  • 需要统一标准(CRI)来降低维护成本、提升一致性

于是大家改走 CRI 标准化路线。

现在的链路:更标准、更可插拔,也更清爽

现在主流链路就是:

  • kubelet → CRI → containerd → shim → runc → 容器进程

  • kubelet → CRI → CRI-O →(shim/OCI runtime)→ runc → 容器进程

它带来的变化很直接:

  • kubelet 只对 CRI 负责,运行时更可插拔

  • containerd/CRI-O 专注运行时职责,边界更清晰

  • shim 把稳定性兜住,容器不怕 runtime 重启

  • 分层更清楚,排障也更容易定位

总而言之:把它们当成"分工明确的一条流水线"

  • Docker:容器工具箱/平台体验(开发运维常用)

  • docker-ce:Docker 的社区版发行包,你装的 Docker 通常就是它

  • CRI:kubelet 和运行时之间的标准接口(kubelet 只认它)

  • containerd:通用型运行时管理器(K8s 常用)

  • CRI-O:为 Kubernetes 定制的 CRI 运行时实现(更专注、更原生)

  • shim:容器的管家/垫片,让容器更稳、更独立

  • 最终都落到 runc + 容器进程:你的应用真正跑在这里

以前是 kubelet 通过 dockershim "绑"着 Docker 跑;现在是 kubelet 通过 CRI "插"上 containerd 或 CRI-O 跑。Docker/docker-ce 负责好用,containerd/CRI-O 负责执行,shim 负责稳定。

相关推荐
杨浦老苏2 小时前
在线视频播放器YT-DLP Web Player
docker·工具·群晖·下载·多媒体
ALex_zry2 小时前
Docker Compose 配置文件完全指南:从基础到生产级安全实践
服务器·安全·docker
idolao3 小时前
CentOS 7 安装 nginx-1.3.15.tar.gz 详细步骤(从源码编译到启动配置)
linux·运维·数据库
CDN3603 小时前
低成本游戏防护:360 SDK 游戏盾使用总结
运维·游戏·网络安全
万象.4 小时前
docker镜像仓库
运维·docker·容器
rosmis4 小时前
自动化文献检索与下载工作流:基于 Playwright 的 RPA 实践方案
运维·自动化·rpa
亚林瓜子4 小时前
linux账号强制密码过期导致私钥文件登录异常问题——(current) UNIX password:
linux·运维·服务器·ssh·aws·ec2·chage
姚不倒4 小时前
Docker 核心原理与运维实战:从入门到生产级理解
运维·docker·架构
2401_891655814 小时前
Git + 云原生:如何管理K8s配置版本?
git·云原生·kubernetes