在容器平台升级、Kubernetes 节点改造或操作系统换代过程中,团队常会遇到从 Docker 相关运行方式迁移到 containerd 的需求。迁移看似只是替换运行时,实际上还涉及 CRI 接口、镜像存储、cgroup、网络插件、日志路径和运维工具链。若缺少完整检查,可能出现镜像无法拉取、Pod 沙箱创建失败、资源限制失效等问题。本文提供一套可直接落地的迁移与排障思路。🔧
一、先分清 Docker、containerd 与 CRI
Docker Engine 并不等同于单一运行时,它包含镜像构建、网络、存储、API 和容器生命周期管理等能力;containerd 更聚焦镜像管理、快照和容器生命周期。Kubernetes 则通过 CRI 与运行时通信。自 Kubernetes 1.24 起,dockershim 已从项目中移除,但这并不意味着 Docker 构建的镜像不能使用。只要镜像符合 OCI 规范,通常仍可由 containerd 拉取和运行,具体架构可参考 Kubernetes 容器运行时文档。
另一个容易混淆的概念是“Docker 使用 containerd”与“Kubernetes 直接使用 containerd”。前者仍由 dockerd 暴露 Docker API,后者则由 kubelet 通过 CRI 连接 containerd。迁移后,原有 docker 命令、Docker Socket 及依赖 Docker API 的脚本不一定继续有效,需要逐项识别。
二、迁移前建立资产与兼容性清单
正式操作前,应记录 Docker、containerd、runc、Kubernetes、操作系统内核及 CNI 插件版本,并确认目标 Kubernetes 版本所要求的 CRI API。还要检查业务是否依赖 /var/run/docker.sock、Docker 日志驱动、私有仓库证书、镜像加速器、自定义网络或存储驱动。📋
- 镜像:确认关键镜像已推送到可访问的仓库,不要只依赖节点本地缓存。
- 存储:核对 Docker data-root、containerd root 和 state 路径所在分区的容量。
- 网络:记录 CNI 配置、Pod 网段、Service 网段以及内核转发参数。
- 安全:检查私有仓库 CA、认证信息、SELinux、AppArmor 和 seccomp 配置。
- 运维:找出调用 docker ps、docker logs、docker exec 或 Docker SDK 的脚本。
建议先选择非核心节点进行灰度迁移,并准备节点排空、配置回退和重新加入集群的方案。生产集群不要在所有节点上同时替换运行时。
三、推荐的迁移执行顺序
- 备份配置:保存 /etc/docker、/etc/containerd、kubelet 配置、CNI 配置和 systemd 单元覆盖项。
- 排空节点:在 Kubernetes 环境中先执行节点 cordon 和 drain,避免业务容器在切换期间被强制中断。
- 生成配置:通过 containerd config default 生成基础配置,再按发行版和集群要求调整,避免直接复制其他环境的旧配置。
- 确认 CRI:检查配置中 CRI 插件未被禁用,并核对 kubelet 的 containerRuntimeEndpoint 是否指向正确的 containerd.sock。
- 统一 cgroup:使用 systemd 管理主机时,通常应让 kubelet 与 containerd 均采用 systemd cgroup 驱动。
- 重启并验证:依次重启 containerd 和 kubelet,确认节点恢复 Ready,再调度测试负载。
迁移的核心原则是:先验证运行时,再恢复调度;先迁移无状态服务,再处理有状态和高可用要求较高的工作负载。🚦
四、重点排查 containerd 兼容性问题
1. CRI 端点连接失败
如果 kubelet 日志出现无法连接运行时、CRI API 不可用或连接 socket 失败,应先执行 systemctl status containerd 和 journalctl -u containerd,确认服务是否正常,再检查 /run/containerd/containerd.sock 是否存在以及 kubelet 配置中的端点是否一致。使用 crictl 时,也应在配置中明确 runtime-endpoint 和 image-endpoint,避免工具自动探测到错误端点。
2. cgroup 驱动不一致
kubelet 与 containerd 使用不同 cgroup 驱动时,常见表现包括节点启动异常、Pod Sandbox 创建失败、资源限制行为异常,或节点在压力下不稳定。使用 systemd 的 Linux 主机通常推荐统一为 systemd;Kubernetes 官方也强调两端配置必须保持一致,详情可查看 cgroup 驱动配置说明。
3. 镜像拉取失败
出现 ImagePullBackOff 时,不要只检查镜像名称。还应确认私有仓库认证、CA 证书、代理、DNS、镜像架构和仓库限流情况。containerd 的仓库配置方式可能与 Docker 的 daemon.json 不同,因此 Docker 能拉取镜像,并不能证明 containerd 也具备相同的证书和网络配置。可使用 crictl pull 直接复现,减少 kubelet 与调度层的干扰。
4. 本地镜像“消失”
Docker 与 containerd 可能使用不同的内容存储和命名空间,原有 Docker 本地镜像不会自动出现在 Kubernetes 使用的 containerd 命名空间中。迁移前应把必要镜像推送到仓库,或者进行受控导出和导入。使用 ctr 检查 Kubernetes 镜像时,应关注 k8s.io 命名空间;日常 CRI 排障则优先使用 crictl。
5. 网络与 Pod 沙箱异常
若出现 failed to setup network、Pod 一直处于 ContainerCreating,需检查 CNI 二进制目录、/etc/cni/net.d 配置、内核模块、IPv4 转发以及网桥过滤参数。还要确认残留 CNI 配置是否引用已卸载插件。迁移运行时通常不要求更换 CNI,但路径、权限或启动顺序变化可能暴露既有问题。🌐
6. 日志与监控链路中断
迁移后,依赖 docker logs、Docker JSON 日志路径或 Docker API 的采集器可能失效。应确认 kubelet 容器日志目录、CRI 日志格式、日志轮转策略和采集器路径,并验证监控系统能否继续获取容器 CPU、内存、重启次数及运行状态。
五、Docker Engine 使用 containerd 镜像存储时的注意事项
如果不是迁移 Kubernetes 运行时,而是启用 Docker Engine 的 containerd 镜像存储,同样要关注兼容性。切换存储后,旧后端中的镜像和容器可能暂时不可见,但数据未必被删除;切回原存储配置后通常可以重新访问。containerd 还可能同时保存压缩内容和解压后的快照,因此磁盘占用模型与传统存储驱动不同,具体限制与启用方式应以 Docker 官方文档为准。
此外,自定义 Docker 数据目录不会必然同步改变 containerd 的存储目录。迁移前应分别检查相关分区容量、挂载方式与告警阈值,防止根分区被镜像内容填满。若环境使用 userns-remap 等特性,也要提前核对目标版本是否支持。
六、迁移后的验收清单
- containerd 与 kubelet 服务持续稳定,无高频重启或明显错误日志。
- 节点状态为 Ready,系统 Pod、CNI 和存储插件运行正常。
- 私有及公共镜像均可拉取,镜像架构与节点架构匹配。
- 容器创建、停止、重启、探针、资源限制和日志采集均正常。
- 网络访问、DNS、Service、Ingress 和持久卷挂载通过验证。
- 旧 Docker 依赖脚本已替换,监控、告警与应急操作手册已更新。
总结
Docker 运行方式迁移到 containerd,真正的难点不是安装软件包,而是处理 CRI、cgroup、镜像仓库、CNI、日志和运维工具之间的联动。✅ 最稳妥的方法是先盘点依赖,再进行单节点灰度,按照“服务状态—CRI 端点—cgroup—镜像—网络—日志”的顺序排查。只要保留可回退配置、避免依赖本地镜像,并完成业务级验收,迁移风险就能得到有效控制。