Containerd 必须启用 CRI 插件才能被 Kubelet 使用,因为 Kubelet 仅通过 CRI gRPC 接口通信,而 CRI 功能由 containerd 的 cri 插件提供,非主服务本身;需确认 plugins."io.containerd.grpc.v1.cri".enabled = true,socket 路径匹配,且重启顺序为先 containerd 后 kubelet。

Containerd 为什么必须启用 CRI 插件才能被 Kubelet 使用
Kubelet 不直接调用 containerd,它只认 CRI 接口。哪怕你装了 containerd,若没启用它的 CRI 插件(即 cri 插件),Kubelet 就会报错 connection refused 或卡在 NotReady 状态——因为 Kubelet 默认连的是 /run/containerd/containerd.sock,但真正提供 CRI 服务的其实是 /run/containerd/containerd.sock 上的 cri 插件子服务,不是 containerd 主服务本身。
实操要点:
- 确认 containerd 配置中已启用
cri插件:plugins."io.containerd.grpc.v1.cri"的enabled = true - 检查 socket 路径是否匹配:Kubelet 启动参数必须设为
--container-runtime-endpoint=unix:///run/containerd/containerd.sock(注意不是/var/run/...) - 重启顺序不能错:先
systemctl restart containerd,再systemctl restart kubelet;否则 kubelet 会因连接不上而反复重试
docker shim 已移除,K8s v1.24+ 还能用 Docker 吗
不能原生用了。K8s v1.24 正式移除了内建的 dockershim,它不再是 Kubelet 的一部分。这意味着即使你装了 Docker Engine,Kubelet 也完全不认识它——没有中间翻译层,CRI 请求发过去就是“听不懂”。
真实场景下只有两条路:
- 降级回 v1.23(不推荐,已 EOL)
- 改用
containerd+nerdctl+buildkit组合替代 Docker CLI 功能;镜像构建走nerdctl build,而非docker build - 极少数生产环境用
cri-dockerd(第三方 shim),但它只是兼容补丁,不是 Kubernetes 官方支持路径,且需额外维护 systemd 单元和 socket 激活
常见错误现象:kubelet 日志里反复出现 failed to run Kubelet: unable to load client CA file 或 failed to run Kubelet: invalid configuration: cgroupDriver: "systemd" is not supported——这往往是因为误以为装了 Docker 就万事大吉,却没配好 runtime endpoint 或 cgroup driver 对齐。
CRI 接口实际通信靠哪两个 gRPC 服务
Kubelet 和 CRI 插件之间是纯 gRPC 通信,只依赖两个核心 service:
-
RuntimeService:管容器生命周期,比如CreatePodSandbox、StartContainer、ExecSync -
ImageService:管镜像,比如PullImage、ListImages、RemoveImage
它们共用一个 socket(如 /run/containerd/containerd.sock),但通过不同 service 名字路由。Kubelet 启动时用 --container-runtime-endpoint 指定 runtime,用 --image-service-endpoint 指定 image service——两者可相同,也可分离(例如 image service 单独跑在另一台 registry proxy 上)。
性能影响点:如果把 ImageService 指向远端,每次拉镜像都会跨网络;本地集群务必保持二者指向同一本地 socket。
为什么 containerd-shim 是必要设计,而不是直接让 containerd fork 容器进程
因为容器主进程(PID 1)需要稳定父进程来接管僵尸进程、转发信号、维持 stdio fd。如果 containerd 自己 fork 容器,那它一升级或崩溃,所有容器就全挂——没有父进程收尸,子进程变孤儿,init(即容器内 PID 1)收不到 SIGTERM,也无法清理资源。
containerd-shim 解决了这个问题:
- 它是轻量级守护进程,每个 Pod 一个 shim,与 containerd 是平级关系(非父子)
- shim 启动后立刻
setsid并fork/exec出runc,自己退为后台父进程 - shim 持有容器 rootfs mount、namespace、cgroup handle,并监听 runc 的 exit 状态
- 当容器退出,shim 清理 cgroup、umount、上报状态给 containerd;然后自己退出
容易被忽略的一点:如果你用 ctr 手动运行容器(绕过 CRI),containerd-shim 依然存在;但用 kubectl 创建 Pod 时,shim 的命名会带 sandbox 和 container 标识,可通过 ps aux | grep shim 观察——这是调试容器启停卡顿的第一现场。










