从 Docker 到 containerd:Kubernetes 生产环境为什么必须理解 CRI?
从 Docker 到 containerd:Kubernetes 生产环境为什么必须理解 CRI?

很多人第一次排查 Kubernetes 节点问题时,第一反应还是:
docker ps
docker logs
docker exec
但到了新的 Kubernetes 集群里,你很可能会发现:节点上根本没有 Docker daemon,或者 Docker 命令已经不是你真正应该看的排障入口。
这不是 Kubernetes “变复杂了”,而是我们需要把几个概念拆开:Docker、containerd、CRI、kubelet。只要这层关系理顺,后面排查 ContainerCreating、镜像拉取失败、kubelet 连不上运行时、升级 containerd 等问题,都会清楚很多。
这篇文章先做铺垫。明天再进入更重的实战:Kubernetes 1.36 + containerd 2.3 生产升级检查清单。
1. 先说结论:Kubernetes 真正关心的是 CRI
Docker 对开发者很友好,它提供了镜像构建、容器运行、日志查看、网络、命令行等一整套体验。
但 Kubernetes 在节点上真正需要的,不是完整 Docker 工具链,而是一个能被 kubelet 调用的容器运行时接口。这个接口就是 CRI,Container Runtime Interface。
简单说:
kubelet负责管理节点上的 Pod 生命周期;CRI是 kubelet 和容器运行时之间的标准接口;containerd是 Kubernetes 常用的容器运行时;runc是更底层真正创建 Linux 容器的组件;crictl是面向 CRI 的节点排障工具。

所以,生产环境里如果 Pod 启动失败,不要只想着“Docker 怎么了”。更准确的问题应该是:
kubelet 通过 CRI 调用 containerd 的过程中,哪一步失败了?
2. Docker、containerd、CRI、kubelet 分别是什么?
Docker:开发者熟悉的一整套容器工具链
Docker 最早让容器真正流行起来,核心原因是体验好。开发者可以用一组统一命令完成镜像构建、容器启动、日志查看和调试。
但在 Kubernetes 节点上,Docker 的完整能力并不是都必须存在。Kubernetes 不一定需要 Docker CLI,也不一定需要 Docker daemon 的所有功能。
containerd:更贴近运行时本身
containerd 更靠近容器运行时的核心职责。它主要负责:
- 拉取镜像;
- 管理镜像;
- 创建容器;
- 管理容器生命周期;
- 管理 Pod sandbox;
- 与底层 runtime 交互。
如果把 Docker 理解成“完整工具箱”,containerd 更像是 Kubernetes 节点上真正干活的“运行时底座”。
CRI:kubelet 和运行时之间的接口
CRI 是 Kubernetes 定义的一套容器运行时接口。kubelet 不需要理解每个运行时的内部实现,只要运行时实现 CRI,kubelet 就可以通过统一接口调用它。
这也是为什么 Kubernetes 可以对接 containerd、CRI-O 等不同运行时。
kubelet:节点上的 Pod 管家
kubelet 运行在每个 Kubernetes 节点上。它会接收控制面的调度结果,然后在本机创建、检查、重启和汇报 Pod 状态。
但 kubelet 并不自己拉镜像、创建容器。它通过 CRI 请求 containerd 这类运行时完成底层动作。
3. 为什么 Kubernetes 逐步淡化 Docker?
很多人听过一句话:“Kubernetes 弃用了 Docker。”
这句话容易误解。更准确地说,是 Kubernetes 移除了 kubelet 里内置的 dockershim。Kubernetes 官方在 dockershim FAQ 里说明,dockershim 在 v1.24 从 kubelet 中移除。它移除的是 kubelet 直接适配 Docker Engine 的那层兼容逻辑,不是说 Docker 镜像不能用了。
今天你用 Dockerfile 构建的镜像,仍然可以在 Kubernetes 里运行。因为镜像格式遵循 OCI 规范,containerd 同样可以拉取和运行这些镜像。
真正发生变化的是节点排障方式:
- 以前你可能习惯看 Docker daemon;
- 现在你更应该看 kubelet、CRI、containerd;
- 以前你习惯
docker ps; - 现在节点侧更常用
crictl ps -a; - 以前你查 Docker 日志;
- 现在你要查 kubelet 和 containerd 日志。

到这里可以先建立一个判断原则:不要先问“Docker 在不在”,而要先问“kubelet 能不能通过 CRI 正常调用运行时”。
如果这个调用链是通的,再继续看镜像、网络、资源和业务容器;如果这个调用链本身不通,后面看多少业务日志都容易跑偏。
4. containerd 在 Kubernetes 节点里到底做什么?
一个 Pod 被调度到节点上,大致会经历这样的过程:
- 控制面决定把 Pod 调度到某个节点;
- 该节点上的 kubelet 看到这个 Pod;
- kubelet 通过 CRI 调用 containerd;
- containerd 准备 Pod sandbox;
- containerd 拉取业务镜像;
- containerd 调用底层 runtime 创建容器;
- kubelet 持续检查 Pod 状态并向 API Server 汇报。
这里面有几个特别容易出问题的点。
第一,Pod sandbox 创建失败
Pod 不是一个单独容器。Kubernetes 会先创建 Pod sandbox,用来承载 Pod 的网络命名空间等基础环境。这个过程通常依赖 pause 镜像。
如果 pause 镜像拉不下来,Pod 就可能一直卡在 ContainerCreating。
第二,业务镜像拉取失败
企业环境里经常会用 Harbor 或其他私有镜像仓库。如果 containerd 的 registry、mirror、TLS 或认证配置有问题,kubelet 看到的结果就是 Pod 拉镜像失败。
第三,kubelet 连不上 containerd
如果 CRI socket 配错、containerd 没启动、CRI 插件异常,kubelet 就无法通过 CRI 管理容器。
这类问题很容易被误判成“Pod 有问题”,但真正的问题在节点运行时。
5. 生产环境为什么必须理解 CRI?
因为生产环境的很多故障,表面看是 Pod 异常,实际入口在 CRI。
场景一:Pod 一直 ContainerCreating
你看到的可能只是:
kubectl get pod
状态一直停在 ContainerCreating。
但原因可能是:
- pause 镜像拉不下来;
- 业务镜像拉不下来;
- containerd 无法创建 sandbox;
- CNI 网络初始化失败;
- kubelet 调用 CRI 失败。
这时候要从 kubectl describe pod 往下追,而不是立刻去猜业务代码问题。
场景二:节点 NotReady
节点 NotReady 不一定是机器宕机,也可能是 kubelet 无法正常汇报状态,或者 kubelet 与 containerd 通信异常。
你需要检查:
systemctl status kubelet
systemctl status containerd
journalctl -u kubelet -f
journalctl -u containerd -f
场景三:升级 Kubernetes 或 containerd
升级前必须确认:
- kubelet 版本:
kubelet --version,并确认与目标 Kubernetes 小版本兼容; - containerd 版本:
containerd --version,重点确认是否启用了 CRI 插件; - CRI endpoint:检查 kubelet 启动参数里的
--container-runtime-endpoint,以及crictl info能否连通; - cgroup driver:检查 kubelet 配置里的
cgroupDriver,以及 containerd 配置里的SystemdCgroup; - pause 镜像:检查 containerd 的
sandbox_image,确认镜像仓库在当前网络环境可以拉取; - 私有仓库配置:检查
/etc/containerd/certs.d/或config.toml里的 registry mirror、TLS、认证配置; - 节点系统和内核配置:至少确认内核版本、cgroup v1/v2、swap、系统服务状态和磁盘压力。
否则升级后最常见的问题就是:节点 Ready,但 Pod 起不来;或者部分 Pod 起得来,部分 Pod 卡住。

6. 常用排查命令
下面这组命令建议直接收藏。它们不是所有问题的最终答案,但能帮你快速判断问题在 Kubernetes 层、kubelet 层,还是 containerd/CRI 层。
查看节点运行时
kubectl get nodes -o wide
关注 CONTAINER-RUNTIME 字段,确认当前节点使用的是 containerd、CRI-O 还是其他运行时。
查看节点详情
kubectl describe node <node-name>
重点看:
- Conditions;
- Allocated resources;
- Events;
- Container Runtime Version。
查看 Pod 事件
kubectl describe pod <pod-name> -n <namespace>
重点看 Events。镜像拉取失败、sandbox 创建失败、调度失败,通常都会在这里留下第一手线索。
查看 CRI 信息
crictl info
如果这里都连不上,说明你要先检查 crictl 的 endpoint 配置,以及 containerd 的 CRI 插件状态。
重点看三件事:
runtimeType或运行时名称是否符合预期;config.containerd.runtimes里是否有默认 runtime;- 输出里是否出现连接 endpoint 失败、CRI service 不可用等错误。
如果 crictl info 报错,可以继续检查:
cat /etc/crictl.yaml
ls -l /run/containerd/containerd.sock
常见目标是确认 runtime-endpoint 是否指向 unix:///run/containerd/containerd.sock。
查看节点上的容器
crictl ps -a
这相当于以前很多人习惯用的 docker ps -a,但它面向的是 CRI。
查看 containerd 状态
systemctl status containerd
如果 containerd 没正常运行,Pod 生命周期管理就会受影响。
如果服务是运行中的,还要继续看配置是否生效:
containerd config dump
重点查:
- CRI 插件是否存在;
sandbox_image是否符合当前环境;- registry mirror 是否指向企业内可访问地址;
- default runtime 是否是预期的
runc或其他运行时。
查看 kubelet 日志
journalctl -u kubelet -f
kubelet 日志通常能看到它调用 CRI 时的错误,比如 runtime endpoint 不可用、sandbox 创建失败等。
7. 一个简单的排障顺序
遇到 Pod 启动异常时,我建议先按这个顺序来:
| 步骤 | 命令 | 目的 |
|---|---|---|
| 1 | kubectl get pod -o wide | 看 Pod 在哪个节点、当前状态是什么 |
| 2 | kubectl describe pod | 看 Events,判断是调度、镜像、sandbox 还是运行时问题 |
| 3 | kubectl describe node | 看节点状态、资源压力和运行时版本 |
| 4 | systemctl status containerd | 看 containerd 是否正常 |
| 5 | crictl info | 看 CRI 是否可用,endpoint 是否正确 |
| 6 | journalctl -u kubelet -f | 看 kubelet 调用运行时的错误 |
| 7 | journalctl -u containerd -f | 看 containerd 侧错误 |
这套顺序的好处是:先看 Kubernetes 暴露出来的信息,再逐层下钻到节点运行时。
如果只能记一条路径,就记这个:
Pod 状态 -> Pod Events -> Node 状态 -> kubelet 日志 -> CRI 连通性 -> containerd 配置与日志
8. 常见误区
误区一:Docker 不能用了,所以 Docker 镜像也不能用了
不对。Docker 构建出来的 OCI 镜像仍然可以被 containerd 拉取和运行。
真正变化的是节点运行时和排障入口。
误区二:containerd 就是 Docker 的完整替代品
不完全是。containerd 是运行时底座,不是 Docker Desktop 或 Docker CLI 的完整体验替代。
如果你想在本地获得接近 Docker 的命令体验,可以了解 nerdctl。但在 Kubernetes 节点排障时,更应该熟悉 crictl。
误区三:Pod 起不来就是业务镜像有问题
不一定。Pod 起不来可能卡在:
- 调度;
- 镜像拉取;
- Pod sandbox;
- CNI;
- CRI;
- containerd;
- kubelet。
所以排障时不要从业务代码开始猜,要先看 Events 和节点运行时。
9. 小结:先理解 CRI,再谈生产升级
如果你只记住一句话,那就是:
Kubernetes 节点上的容器问题,不要只用 Docker 思路排查;先看 kubelet 通过 CRI 调用 containerd 的链路。
理解这条链路后,很多问题会变得清楚:
- 为什么没有 Docker 命令也能跑容器;
- 为什么要用
crictl; - 为什么 Pod sandbox 会影响 Pod 启动;
- 为什么 containerd 配置会影响镜像拉取;
- 为什么升级 Kubernetes 前必须检查运行时版本和 CRI socket。
下一篇我会进入真正的生产实战:
Kubernetes 1.36 + containerd 2.3 生产升级实战:CRI、版本兼容与避坑清单。
那篇会重点给出升级前检查项、命令清单、常见故障和回滚方案。
参考资料
- Kubernetes 官方文档:Container Runtimes
Container Runtimes | Kubernetes - Kubernetes 官方博客:Dockershim Removal FAQ
Updated: Dockershim Removal FAQ | Kubernetes - Kubernetes 官方文档:Container Runtime Interface
Container Runtime Interface (CRI) | Kubernetes - containerd 官方站点
containerd – An industry-standard container runtime with an emphasis on simplicity, robustness and portability
更多推荐

所有评论(0)