主题
02 — 容器运行时深度教材
理解容器运行时的分层架构(OCI → CRI → containerd/CRI-O → runc)是高级运维的基本功。本章从 OCI 规范到生产运维,完整覆盖 containerd 架构、CRI 接口、镜像管理、故障排查。
1. 容器运行时架构分层
┌─────────────────────────────────────────────────────────┐
│ kubelet │
│ (通过 CRI gRPC 接口调用容器运行时) │
├─────────────────────────────────────────────────────────┤
│ CRI (Container Runtime Interface) │
│ gRPC 协议,定义 ImageService + RuntimeService │
├──────────────────────┬──────────────────────────────────┤
│ containerd │ CRI-O │
│ (shim 架构) │ (轻量级,专为 K8s) │
├──────────────────────┴──────────────────────────────────┤
│ runc (OCI runtime) │
│ 创建容器进程:clone() + namespace + cgroup + chroot │
├─────────────────────────────────────────────────────────┤
│ OCI (Open Container Initiative) │
│ 定义镜像格式(image spec)和运行时规范(runtime spec) │
└─────────────────────────────────────────────────────────┘1.1 OCI 规范
OCI 两大规范:
1. Image Specification(镜像格式)
- 定义镜像的 layer、config、manifest
- Docker 镜像格式是 OCI 的超集
2. Runtime Specification(运行时规范)
- 定义容器的创建、运行、销毁
- 规定 config.json 格式(mounts、hooks、linux namespace/cgroup)
OCI 运行时参考实现:runc1.2 containerd vs CRI-O 对比
| 特性 | containerd | CRI-O |
|---|---|---|
| 定位 | 通用容器运行时 | K8s 专用运行时 |
| 支持 Docker CLI | ✅ 通过 ctr/docker | |
| 架构复杂度 | 中等(shim 架构) | 轻量 |
| K8s 默认 | ✅ 1.24+ 默认 | OpenShift 默认 |
| 资源占用 | ~50MB RSS | ~30MB RSS |
| 社区活跃度 | 高(CNCF 毕业项目) | 中(K8s SIG) |
2. containerd 深度运维
2.1 架构与组件
containerd 架构:
┌──────────────┐
│ kubelet │ ← CRI gRPC
├──────────────┤
│ containerd │ ← 主进程(管理容器生命周期)
├──────────────┤
│ containerd-shim-runc-v2 │ ← 每个 Pod 一个 shim 进程
├──────────────┤
│ runc │ ← 创建容器后退出(shim 接管)
└──────────────┘
关键设计:
- shim 进程在 containerd 重启时保持容器运行
- containerd 崩溃重启不影响已运行的容器
- 每个 Pod(含 pause + 业务容器)对应一个 shim2.2 containerd 配置
bash
# 生成默认配置
containerd config default > /etc/containerd/config.toml
# 关键配置项:
# [plugins."io.containerd.grpc.v1.cri"]
# sandbox_image = "registry.k8s.io/pause:3.9" # pause 镜像
# [plugins."io.containerd.grpc.v1.cri".containerd]
# default_runtime_name = "runc"
# [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc]
# runtime_type = "io.containerd.runc.v2"
# [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc.options]
# SystemdCgroup = true # 使用 systemd 管理 cgroup(推荐)
# 镜像仓库配置(私有仓库 + mirror)
# [plugins."io.containerd.grpc.v1.cri".registry]
# [plugins."io.containerd.grpc.v1.cri".registry.mirrors]
# [plugins."io.containerd.grpc.v1.cri".registry.mirrors."docker.io"]
# endpoint = ["https://mirror.ccs.tencentyun.com", "https://registry-1.docker.io"]
# [plugins."io.containerd.grpc.v1.cri".registry.mirrors."harbor.example.com"]
# endpoint = ["https://harbor.example.com"]
# 重启 containerd 使配置生效
systemctl restart containerd2.3 crictl — K8s 容器运行时 CLI
bash
# === 容器管理 ===
crictl ps # 运行中的容器
crictl ps -a # 所有容器
crictl inspect <container-id> # 容器详情
crictl logs <container-id> # 容器日志
crictl logs -f <container-id> # 实时跟踪
crictl exec -it <container-id> /bin/sh # 进入容器
# === Pod 管理 ===
crictl pods # 列出 Pod sandbox
crictl inspectp <pod-id> # Pod 详情
# === 镜像管理 ===
crictl images # 列出镜像
crictl pull <image> # 拉取镜像
crictl rmi <image> # 删除镜像
crictl rmi --prune # 清理未使用镜像
# === 运行时状态 ===
crictl info # containerd 状态
crictl stats # 容器资源使用
# === 调试技巧 ===
# 找到某个 Pod 的所有容器
crictl ps --pod <pod-id>
# 查看容器启动参数
crictl inspect <container-id> | jq .info.config.args
# 查看容器的 cgroup 路径
crictl inspect <container-id> | jq .info.runtimeSpec.linux.cgroupsPath2.4 ctr — containerd 原生 CLI(调试用)
bash
# 注意:ctr 直接操作 containerd,绕过 CRI,生产慎用
ctr -n k8s.io images ls # 列出镜像
ctr -n k8s.io containers ls # 列出容器
ctr -n k8s.io tasks ls # 列出运行中的任务
# 查看镜像的 layer 信息
ctr -n k8s.io images info <image>
# 导出/导入镜像(离线场景)
ctr -n k8s.io images export /tmp/image.tar <image>
ctr -n k8s.io images import /tmp/image.tar3. 镜像管理深度
3.1 镜像层与存储
bash
# 镜像层查看
ctr -n k8s.io images info docker.io/library/nginx:latest | jq .target
# 镜像存储位置
ls /var/lib/containerd/io.containerd.snapshotter.v1.overlayfs/snapshots/
# 查看镜像占用空间
du -sh /var/lib/containerd/
# 镜像垃圾回收
ctr -n k8s.io images gc --help
# 配置自动 GC:
# [plugins."io.containerd.gc.v1.scheduler"]
# deletion_threshold = 0
# pause_threshold = 0.023.2 镜像拉取故障排查
bash
# 问题 1:证书错误
# 解决方案:配置 insecure-skip-verify
# [plugins."io.containerd.grpc.v1.cri".registry.configs."harbor.local".tls]
# insecure_skip_verify = true
# 问题 2:超时
# 检查网络和 DNS
nslookup registry-1.docker.io
curl -v https://registry-1.docker.io/v2/ 2>&1 | head -20
# 问题 3:镜像不存在
# 检查 imagePullPolicy 和 tag
crictl pull docker.io/library/nginx:latest
# 问题 4:认证失败
# 创建 imagePullSecret 或配置 containerd 认证
# crictl 不支持直接登录,需要在 K8s 中使用 imagePullSecret4. 面试高频问题
Q: containerd 重启会杀死已运行的容器吗?
不会。containerd 使用 shim 架构:
1. 每个 Pod 对应一个 containerd-shim 进程
2. shim 由 init 进程(PID 1)接管,不依赖 containerd
3. containerd 重启时,shim 进程继续运行
4. containerd 重启后通过 shim 重新获取容器状态
验证:
systemctl restart containerd
crictl ps # 容器仍然在运行
systemctl status containerd-shim-* # shim 进程仍在Q: 如何排查 Pod 一直处于 ContainerCreating 状态?
bash
# 1. 查看 Pod 事件
kubectl describe pod <name> | tail -30
# 2. 常见原因
# a. 镜像拉取失败 → 检查 imagePullPolicy、镜像名、仓库认证
# b. Init Container 失败 → kubectl logs <pod> -c <init-container>
# c. 挂载卷失败 → 检查 PV/PVC、StorageClass、NFS/iSCSI 连通性
# d. 网络插件未就绪 → 检查 Calico/Cilium Pod 状态
# e. 资源不足 → kubectl describe node <node> | grep -A5 'Allocated'
# 3. containerd 层面排查
journalctl -u containerd --since "10 minutes ago" | grep -i error
crictl ps -a | grep <pod-name>
crictl inspect <container-id> 2>/dev/nullQ: Docker 和 containerd 的关系?为什么 K8s 弃用 Docker?
关系:
Docker → containerd → runc
Docker 是 containerd 的上层封装(增加了 build、compose 等功能)
弃用原因:
1. K8s 只需要 CRI 接口,Docker 的额外功能是负担
2. Docker 需要 dockershim 适配层(维护成本高)
3. K8s 1.24 移除 dockershim,直接使用 containerd/CRI-O
影响:
- 生产环境:用 containerd + crictl 替代 docker + docker CLI
- 开发环境:Docker Desktop 仍可用(它内部也用 containerd)
- 镜像构建:用 BuildKit/Kaniko 替代 docker build