3. containerd 架构与核心组件
containerd 是 Kubernetes 最主流的容器运行时,其架构设计遵循“单一职责、可插拔、轻量级”原则。从控制面故障排查视角看,理解 containerd 守护进程、shim 进程、snapshotter、metadata 存储和 gRPC API 这五大核心组件,是定位容器启动失败、镜像拉取超时、节点 NotReady 等问题的前提。
3.1 整体架构总览
下图展示了 containerd 的核心组件及其交互关系。控制面故障排查时,重点关注 gRPC API 层(kubelet 调用入口)、shim 进程(容器生命周期隔离单元)和 snapshotter(镜像层挂载引擎)。
3.2 containerd 守护进程(containerd)
containerd 守护进程是系统的核心协调者,以 systemd 服务形式运行(systemctl status containerd)。它负责:
- gRPC 服务注册:监听
/run/containerd/containerd.sock(默认),暴露 CRI、Namespace、Tasks、Images 等服务。 - 任务调度:将容器创建请求转发给 shim 进程,并跟踪容器状态。
- 镜像管理:拉取、解压、索引镜像层,委托 snapshotter 完成层挂载。
- 元数据协调:读写 metadata 存储,维护容器、镜像、快照的关联关系。
| 配置项 | 默认值 | 故障排查关注点 |
|---|---|---|
root |
/var/lib/containerd |
磁盘空间不足 → 镜像拉取/容器启动失败 |
state |
/run/containerd |
socket 文件丢失 → kubelet 无法连接 |
grpc.address |
/run/containerd/containerd.sock |
权限错误 → kubelet 报 permission denied |
oom_score |
-999 |
OOM 导致守护进程被杀 → 节点 NotReady |
ctr version 和 ctr plugins list 快速验证 containerd 守护进程是否存活、插件是否正常加载。若 ctr 命令无响应,大概率是守护进程 hang 或 socket 文件损坏。
3.3 shim 进程
shim 进程是 containerd 架构中“容器运行时隔离”的关键设计。每个容器(或 Pod sandbox)对应一个独立的 shim 进程,其核心职责包括:
- 生命周期代理:containerd 守护进程通过 shim 间接管理容器,即使 containerd 重启,shim 进程仍保持容器运行。
- 标准 I/O 转发:将容器 stdout/stderr 通过 FIFO 或管道转发给 containerd。
- 退出状态收集:容器退出后,shim 负责收集退出码并通知 containerd。
- 运行时适配:不同的 shim 实现(如
containerd-shim-runc-v2)对接不同的底层运行时(runc、kata、gvisor)。
控制面故障中,shim 进程异常是最常见的“容器卡在 Terminating”或“Sandbox 状态不一致”的根因。
| 故障现象 | 可能原因 | 诊断命令 |
|---|---|---|
| 容器状态为 Unknown | shim 进程崩溃或 OOM | ps aux | grep containerd-shim |
| Pod 一直 Terminating | shim 未响应 SIGTERM | ctr tasks kill <task-id> |
| kubelet 报 "failed to get sandbox status" | shim socket 文件丢失 | ls /run/containerd/io.containerd.runtime.v2.task/ |
| 容器启动超时 | shim 创建失败(资源不足) | journalctl -u containerd --no-pager | grep shim |
ctr tasks rm --force 或重启 containerd 服务来触发 shim 清理。
3.4 snapshotter
snapshotter 是 containerd 的“镜像层挂载引擎”,负责将镜像的每一层(layer)挂载为容器的根文件系统。默认使用 overlayfs snapshotter,其他选项包括 native、devmapper、nydus(加速镜像)。
从控制面故障排查角度看,snapshotter 问题通常表现为:
- 镜像拉取成功但容器启动失败:snapshotter 挂载时报错,如
mount: no such device(overlay 内核模块未加载)。 - 磁盘空间泄漏:未正确清理的 snapshots 占用大量磁盘,导致节点磁盘压力。
- 快照层损坏:异常断电或并发写入导致 metadata 不一致。
ctr snapshots list — 列出所有快照ctr snapshots info <snapshot-id> — 查看快照详情mount | grep overlay — 检查 overlay 挂载是否正常df -h /var/lib/containerd — 检查 snapshotter 数据目录磁盘使用率
3.5 metadata 存储
containerd 使用 BoltDB(嵌入式键值数据库)作为 metadata 存储,文件位于 /var/lib/containerd/containerd.db。它维护以下核心元数据:
- 容器元数据:ID、状态、标签、创建时间、shim 进程 PID。
- 镜像元数据:镜像名称、摘要、层索引、大小。
- 快照元数据:快照链关系、父快照引用、挂载点。
- 命名空间:Kubernetes 使用
k8s.io命名空间隔离 Pod 容器。
metadata 存储损坏是控制面故障中的“隐形杀手”——containerd 守护进程可能仍在运行,但所有容器操作都返回错误。
| 故障表现 | 日志特征 | 恢复操作 |
|---|---|---|
| containerd 启动失败 | bolt: database is locked |
检查是否有残留 containerd 进程;删除 containerd.db 锁文件 |
| 容器列表为空但实际有容器运行 | metadata: bucket not found |
使用 containerd.db 备份恢复;或重建数据库(会丢失状态) |
| 镜像拉取后无法引用 | metadata: key already exists |
清理镜像缓存:ctr images rm 后重新拉取 |
| 快照链断裂 | snapshot: parent snapshot not found |
删除容器后清理快照:ctr snapshots rm |
containerd.db 文件——这会丢失所有容器和镜像元数据,导致 kubelet 无法管理已有 Pod。优先尝试从备份恢复,或使用 containerd --config 指定只读模式导出数据。
3.6 gRPC API
containerd 通过 gRPC API 对外提供服务,kubelet 通过 CRI(Container Runtime Interface)调用这些 API。gRPC API 是控制面故障排查的“第一入口”——通信中断会直接导致节点 NotReady。
核心 gRPC 服务包括:
- RuntimeService(CRI):
RunPodSandbox、CreateContainer、StartContainer、Status。 - ImageService(CRI):
PullImage、ListImages、ImageStatus。 - TasksService(containerd 原生):
Create、Start、Kill、Delete。 - SnapshotsService(containerd 原生):
Prepare、Mount、Remove。
gRPC 故障通常表现为 kubelet 日志中的 rpc error: code = Unavailable 或 code = DeadlineExceeded。
crictl info — 验证 containerd CRI 是否响应crictl ps -a — 列出所有容器(包括已退出)grpcurl -plaintext -unix /run/containerd/containerd.sock list — 枚举所有 gRPC 服务journalctl -u kubelet --no-pager | grep "rpc error" — 快速定位 gRPC 通信错误
3.7 组件协作故障排查路径
当控制面出现容器运行时相关故障时,建议按以下路径逐层排查:
- 检查 containerd 守护进程:
systemctl status containerd、ctr version。 - 检查 gRPC 通信:
crictl info、ls -l /run/containerd/containerd.sock。 - 检查 shim 进程:
ps aux | grep shim、ctr tasks list。 - 检查 snapshotter:
mount | grep overlay、ctr snapshots list。 - 检查 metadata 存储:
ls -lh /var/lib/containerd/containerd.db、dmesg | grep containerd。