很多团队第一次接触containerd,是在升级Kubernetes或处理节点运行时时看到“Docker被弃用”的说法。这里容易产生误解:不是容器能力消失了,也不是Docker镜像不能用了,而是Kubernetes不再通过旧的dockershim去间接调用Docker Engine。真正负责拉取镜像、管理容器生命周期的组件,逐渐从完整Docker引擎中的一部分,独立为更轻量、更标准的containerd。

先把关系说清楚
Docker面向开发者,提供从构建镜像、推送仓库、运行容器到本地调试的一整套体验。containerd面向系统和平台,更关注镜像拉取、快照管理、容器创建、任务启动、日志与事件。两者不是简单替代关系,而是上下层与场景差异关系:Docker可以把containerd作为底层运行组件,Kubernetes也可以直接通过CRI调用containerd。
可以用三层理解:
- Docker CLI和Docker Engine负责开发者交互、构建体验、网络卷等上层能力。
- containerd负责镜像内容管理、容器元数据、快照、任务生命周期和CRI插件。
- runc等低层运行时负责真正创建符合OCI规范的容器进程。
如果你在本机执行docker run nginx,通常是Docker接收命令后交给containerd,再由containerd调用runc创建容器。如果Kubernetes节点使用containerd,kubelet会通过CRI接口直接请求containerd,链路更短,节点上不需要完整Docker Engine。
Kubernetes为什么更偏向containerd
Kubernetes需要的是稳定、标准、可自动化的节点运行时,而不是面向人机交互的完整开发工具。早期Kubernetes通过dockershim适配Docker,但这增加了一层非标准桥接。随着CRI成熟,kubelet可以用统一接口对接containerd、CRI-O等运行时,减少维护负担,也让节点组件边界更清楚。
这对业务团队的影响主要有三类。第一,镜像格式仍然兼容,Docker构建出的OCI或Docker v2镜像依然可以在containerd节点运行。第二,节点排障命令会变化,过去依赖docker ps、docker logs,现在更常用crictl ps、crictl logs、ctr。第三,部分依赖Docker socket的运维脚本需要改造,不能默认节点上存在/var/run/docker.sock。
CRI调用链如何工作

当一个Pod被调度到节点后,kubelet会读取PodSpec,并通过CRI向运行时发起请求。以containerd为例,大致流程是:拉取镜像、创建Pod沙箱、创建容器、启动容器、持续上报状态。Pod沙箱通常负责网络命名空间等基础环境,业务容器再加入这个环境。
常见检查命令如下:
crictl info
crictl images
crictl ps -a
crictl inspect <container_id>
crictl logs <container_id>
ctr更接近containerd自身的管理工具,适合确认底层镜像和任务状态,但它不完全等同于CRI视角。平台运维排障时,应优先使用crictl观察kubelet看到的容器状态,再用ctr深入验证containerd内部状态。
Docker运行时变化会不会影响开发流程
大多数开发者的日常构建不会受影响。团队仍然可以使用Docker Desktop、Docker Engine或BuildKit构建镜像,并推送到镜像仓库。变化主要发生在生产Kubernetes节点:节点不一定安装Docker,部署和排障脚本也不应依赖Docker命令。
如果CI流程中只是构建镜像并推送仓库,继续使用Docker通常没有问题。如果CI任务需要在Kubernetes节点上“进入宿主机后执行docker命令”,就要重新评估。更推荐的方式是把镜像构建、镜像扫描、运行时排障分别拆开,在平台侧提供标准命令和权限边界。关于容器基础知识,也可以结合Docker与容器基础理解镜像、容器和运行时的职责拆分。
选型和迁移时看哪些点

选择containerd并不意味着排斥Docker,而是在生产节点上采用更直接的运行时架构。评估时建议看四个维度:
| 维度 | 关注点 | 建议 |
|---|---|---|
| — | — | — |
| Kubernetes版本 | 是否仍依赖dockershim | 新集群优先使用containerd |
| 运维脚本 | 是否调用docker命令或Docker socket |
改为crictl或平台API |
| 镜像规范 | 镜像是否符合OCI或主流仓库规范 | 保持标准构建与扫描流程 |
| 安全边界 | 是否把宿主机Docker socket挂入容器 | 避免高权限直连宿主机运行时 |
迁移前可以抽样检查节点脚本、监控采集器、安全扫描器和日志组件。尤其要关注以“容器内构建镜像”为目标的流水线,它们可能依赖Docker daemon。可替代方案包括BuildKit、Kaniko、Buildah或平台提供的镜像构建服务,具体选择取决于隔离要求和缓存策略。
排障时如何判断问题层级
遇到Pod启动异常,不要一开始就把问题归因于containerd。更有效的顺序是先看Kubernetes事件,再看CRI状态,最后看底层运行时日志。比如镜像拉取失败多半与仓库地址、凭据、网络、证书有关;容器启动后立即退出,多半与命令、依赖、配置或健康检查有关;只有出现运行时响应慢、容器状态不同步、任务残留时,才需要深入containerd服务和快照层。
kubectl describe pod <pod_name> -n <namespace>
crictl ps -a | grep <pod_name>
crictl logs <container_id>
systemctl status containerd
journalctl -u containerd --since "30 min ago"
如果团队已经建设统一容器平台,应把这些检查沉淀为标准化诊断能力,而不是让每个业务团队登录节点手工操作。更多运行时与容器技术主题可参考容器技术专题。
常见问题
containerd和Docker是不是只能二选一?
不是。Docker在开发和镜像构建侧仍然很常见,containerd在生产Kubernetes节点侧更常见。两者可以处在同一供应链的不同阶段:开发者使用Docker构建和验证镜像,CI把镜像推送到仓库,Kubernetes节点通过containerd拉取并运行镜像。真正需要避免的是把生产节点管理逻辑绑定到Docker socket,因为这会增加安全风险和迁移成本。
Kubernetes使用containerd后,Docker镜像还能运行吗?
通常可以。关键不在于镜像是不是用Docker命令构建,而在于镜像格式、架构、入口命令、依赖文件和仓库访问是否符合运行环境要求。只要镜像能被仓库正确分发,且符合目标节点CPU架构和OCI兼容要求,containerd就可以拉取和启动。若出现异常,应优先检查镜像平台、tag、认证、证书和启动命令,而不是简单判断“Docker镜像不能跑”。
生产环境还需要安装Docker吗?
新建Kubernetes集群通常不建议为了运行Pod而安装完整Docker Engine。节点运行时使用containerd即可满足Pod生命周期管理需求,也能减少组件复杂度。但在专门的构建节点、开发机或兼容旧流水线的环境中,Docker仍可能存在。比较稳妥的做法是把运行节点和构建节点职责分离,并逐步清理依赖Docker命令的运维脚本。
小结
containerd和Docker的关系,核心是“Docker提供完整体验,containerd承担标准运行时能力”。理解这层关系后,Kubernetes从Docker迁移到containerd就不再是概念冲突,而是生产节点运行时架构的简化。对企业团队而言,真正要做的是统一镜像规范、调整排障工具、减少Docker socket依赖,并把运行时诊断能力纳入平台治理。
转载请注明出处:https://www.cloudnative-tech.com/p/7423/