容器运行时深度对比
容器运行时深度对比
概述
Kubernetes 从 v1.24 起正式移除了 dockershim,容器运行时通过 CRI(Container Runtime Interface) 标准接口与 kubelet 通信。目前 K8s 生态中两个主流的 CRI 实现是 containerd 和 CRI-O,底层都通过 runc 创建容器。此外,gVisor 和 Kata Containers 提供了更强的安全隔离。
一句话:如果 K8s 是云原生的操作系统,容器运行时就是它的”内核”——kubelet 不直接操作容器,所有 Pod 创建/销毁/日志操作都通过 CRI 委托给运行时。
CRI 协议概述
CRI 是一套 gRPC API,定义了两个服务:
| 服务 | 职责 | 关键 RPC |
|---|---|---|
| RuntimeService | Pod 和容器的生命周期管理 | RunPodSandbox、CreateContainer、StartContainer、StopContainer、RemoveContainer |
| ImageService | 镜像管理 | PullImage、ListImages、RemoveImage、ImageStatus |
kubelet → CRI gRPC (Unix Socket) → Container Runtime (containerd/CRI-O)
↓
runc (OCI Runtime)
↓
Linux Namespace + cgroup
容器生命周期的关键概念:Pod Sandbox(pause 容器)。每个 Pod 先创建 sandbox(设置 network namespace + IPC namespace),然后所有业务容器共享这个 sandbox。
containerd vs CRI-O
containerd
containerd 是 CNCF 毕业项目,Docker 的核心组件。K8s v1.24+ 默认容器运行时。
架构:
kubelet
↓ CRI gRPC
containerd (守护进程)
├── CRI Plugin(内置,处理 CRI 请求)
├── Content Store(镜像层存储)
├── Snapshotter(overlayfs/devmapper 等文件系统)
└── Task Service → runc
| 维度 | 详情 |
|---|---|
| 开发者 | CNCF(原 Docker 分拆) |
| 语言 | Go |
| CRI 支持 | 内置 CRI Plugin,无需额外服务 |
| 镜像格式 | OCI + Docker V2 |
| snapshotter | overlayfs(默认)、devmapper、btrfs、zfs |
| GPU 支持 | NVIDIA Container Toolkit(nvidia-container-runtime 作为 OCI runtime) |
| 配置路径 | /etc/containerd/config.toml |
| 管理 CLI | ctr(底层)、crictl(CRI 标准)、nerdctl(Docker 兼容) |
| K8s 集成 | v1.24+ 默认,kubeadm 默认使用 |
CRI-O
CRI-O 是专门为 Kubernetes 设计的轻量级 CRI 实现,只做 CRI 需要的功能。
架构:
kubelet
↓ CRI gRPC
cri-o (守护进程)
├── CRI Server
├── Container + Image Storage
└── OCI Runtime → runc / crun
| 维度 | 详情 |
|---|---|
| 开发者 | Red Hat / CNCF 孵化项目 |
| 语言 | Go |
| CRI 支持 | 唯一功能,不为任何非 K8s 场景设计 |
| 镜像格式 | OCI |
| snapshotter | overlayfs(默认)、devmapper |
| GPU 支持 | NVIDIA Container Toolkit |
| 配置路径 | /etc/crio/crio.conf |
| 管理 CLI | crictl |
| K8s 集成 | OpenShift 默认、RHEL K8s 推荐 |
架构对比
containerd:
kubelet ─CRI─→ containerd ─OCI─→ runc
├── Docker Image Pull (可独立运行 Docker 命令)
├── BuildKit(可选,镜像构建)
└── 非 K8s 场景也可独立使用
CRI-O:
kubelet ─CRI─→ cri-o ─OCI─→ runc / crun
└── 只为 K8s 存在,不做多余的事
选型对比
| 维度 | containerd | CRI-O |
|---|---|---|
| K8s 默认 | ✅ (v1.24+) | ❌(Red Hat 系除外) |
| 非 K8s 使用 | ✅ 可作为通用容器引擎 | ❌ |
| Docker 兼容 | ✅ nerdctl | ❌ |
| 镜像构建 | ✅ BuildKit | ❌ |
| 复杂度 | 中等 | 低 |
| 社区生态 | 极大(Docker/Moby 生态) | 集中(K8s/OpenShift) |
| 安全审计面 | 较大 | 较小(代码量更少) |
| OCI runtime | runc | runc + crun(可选,C 语言实现更快) |
| 典型用户 | 通用 K8s、GKE、EKS、AKS | OpenShift、OKD、RHEL K8s |
选 containerd 如果:需要 Docker 兼容工具链(nerdctl build/run),或使用托管 K8s(GKE/EKS/AKS 默认 containerd)。 选 CRI-O 如果:只用 K8s、重视最小攻击面、Red Hat/OpenShift 生态。
crictl —— CRI 标准管理工具
无论底层是 containerd 还是 CRI-O,crictl 提供统一的运维命令:
# crictl 配置(指向 CRI socket)
cat /etc/crictl.yaml
# runtime-endpoint: unix:///run/containerd/containerd.sock (containerd)
# runtime-endpoint: unix:///var/run/crio/crio.sock (CRI-O)
# Pod 管理
crictl pods # 列出所有 Pod(含 sandbox)
crictl podss --name nginx # 按名称过滤
# 容器管理
crictl ps # 运行中的容器
crictl ps -a # 所有容器(含已退出的)
crictl logs <container-id> # 容器日志
crictl exec -it <id> /bin/sh # 进入容器
# 镜像管理
crictl images # 本地镜像
crictl pull image:tag # 手动拉取(通常不需要,kubelet 自动拉)
crictl rmi <image-id> # 删除镜像
# 运行时信息
crictl info # 运行时版本和配置
crictl stats # 容器资源使用统计
containerd 配置要点
# /etc/containerd/config.toml(关键配置段)
version = 2
[plugins."io.containerd.grpc.v1.cri"]
# cgroup 驱动(K8s v1.31+ 必须 systemd)
[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
# 镜像仓库 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.gcr.io", "https://docker.io"]
[plugins."io.containerd.grpc.v1.cri".registry.configs]
[plugins."io.containerd.grpc.v1.cri".registry.configs."registry.example.com".auth]
username = "robot$myorg"
password = "<token>"
# sandbox (pause) 镜像
sandbox_image = "registry.k8s.io/pause:3.10"
# 镜像 GC(磁盘不足时回收)
[plugins."io.containerd.grpc.v1.cri".image]
discard_unpacked_layers = true # pull 后立即释放解压层
# cgroup v2 验证
[plugins."io.containerd.runtime.v2.task"]
platforms = ["linux/amd64"]
sched_core = true
安全运行时:gVisor vs Kata
普通容器(runc)共享宿主机内核。如果容器逃逸,攻击者获得宿主机权限。安全运行时通过额外的隔离层解决这个问题。
gVisor(Google)
gVisor 用 Go 实现了一个用户态内核(Sentry),拦截应用程序的系统调用,自己处理或转给宿主机。不共享宿主机内核,性能损失 5-15%。
应用 → Sentry(Go 实现的 Linux 内核)→ 宿主机系统调用(过滤后)
↓ Gofer(文件 I/O 代理)
宿主机文件系统
Kata Containers(Intel/Hyper.sh → CNCF)
Kata 为每个容器启动一个轻量级虚拟机(使用 Firecracker/QEMU microVM),有独立内核。隔离性最强,性能损失 10-20%。
应用 → 独立 Linux 内核(轻量 VM)→ virtio → 宿主机
对比
| 维度 | runc | gVisor | Kata |
|---|---|---|---|
| 隔离层级 | 进程级(Namespace) | 用户态内核 | 硬件虚拟化 |
| 内核 | 共享宿主机 | Sentry(Go) | 独立内核 |
| 启动速度 | ~50ms | ~100ms | ~150-300ms |
| 性能损失 | 0% | 5-15% | 10-20% |
| GPU 支持 | ✅ 原生 | ❌ 不支持 | ❌ 不支持(社区实验性) |
| 适用场景 | 自己集群的可信工作负载 | 多租户 SaaS、Serverless | 最高安全要求、不可信代码执行 |
| runtimeClass 名称 | runc(默认) | gvisor | kata |
在 containerd 中启用
# /etc/containerd/config.toml
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes]
# gVisor
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runsc]
runtime_type = "io.containerd.runsc.v1"
# Kata
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.kata]
runtime_type = "io.containerd.kata.v2"
# Pod 声明使用安全运行时
apiVersion: node.k8s.io/v1
kind: RuntimeClass
metadata:
name: gvisor
handler: runsc
---
apiVersion: v1
kind: Pod
spec:
runtimeClassName: gvisor
containers:
- name: untrusted
image: untrusted-code
GPU 运行时集成
NVIDIA Container Toolkit
GPU 容器需要额外的运行时库(CUDA、nvidia-container-runtime)来暴露 GPU 设备:
# 安装 NVIDIA Container Toolkit
distribution=$(. /etc/os-release;echo $ID$VERSION_ID)
curl -fsSL https://nvidia.github.io/libnvidia-container/gpgkey | gpg --dearmor -o /usr/share/keyrings/nvidia-container-toolkit-keyring.gpg
curl -s -L https://nvidia.github.io/libnvidia-container/$distribution/libnvidia-container.list | \
sed 's#deb https://#deb [signed-by=/usr/share/keyrings/nvidia-container-toolkit-keyring.gpg] https://#g' | \
tee /etc/apt/sources.list.d/nvidia-container-toolkit.list
apt-get update && apt-get install -y nvidia-container-toolkit
containerd 配置 GPU runtime:
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.nvidia]
runtime_type = "io.containerd.runc.v2"
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.nvidia.options]
BinaryName = "/usr/bin/nvidia-container-runtime"
SystemdCgroup = true
GPU Operator —— 零运维 GPU 节点
NVIDIA GPU Operator 自动化 GPU 驱动、Container Toolkit、DCGM、MIG 配置:
helm install gpu-operator nvidia/gpu-operator \
-n gpu-operator --create-namespace \
--set driver.enabled=true \
--set toolkit.enabled=true \
--set mig.strategy=mixed
日常运维
镜像空间管理
# containerd 查看镜像占用
crictl images | awk '{print $3}' | sort | uniq -c | sort -rn | head -10
# 或 containerd 原生命令
ctr -n k8s.io images ls | awk '{print $1, $5}' | column -t
# 清理未使用的镜像
crictl rmi --prune
# 查看 containerd 磁盘使用
du -sh /var/lib/containerd/
常见故障处理
| 症状 | 原因 | 解决 |
|---|---|---|
crictl ps 不返回任何容器 | CRI socket 未配置或权限不足 | 检查 /etc/crictl.yaml 指向正确的 socket |
Pod 创建失败:CreateContainerError | 镜像拉取失败(registry 不可达/认证失效) | crictl pull image:tag 手动拉取验证 |
| 容器日志不显示 | containerd 日志未转发到 journald/stdout | containerd 使用 CRI log driver |
SystemdCgroup=false → cgroup v2 异常 | 未开 SystemdCgroup 或运行在 v1 模式 | sed -i 's/SystemdCgroup = false/SystemdCgroup = true/' /etc/containerd/config.toml |
| GPU 容器启动失败 | nvidia-container-runtime 未注册到 containerd | 检查 containerd config 中 nvidia runtime 配置 |
| 镜像 pull 慢 | 从 docker.io 拉取,被限速 | 配置 registry mirror |
关联知识
- OCI Runtime 与镜像内部机制 — 本文的内部机制深度补充(OCI Spec、runc 流程、镜像 Manifest/Layer、Snapshotter GC)
- ../linux/cgroup v2 详解 — cgroup v2 是容器运行时的底层隔离机制
- ../linux/CPU 隔离与中断亲和性 — CPU Manager 底层的 cpuset 控制在运行时配置
- CNI 网络插件对比与排障 — CNI 在 Pod Sandbox 创建时由 CRI 调用
- DRA 动态资源分配详解 — 替代 Device Plugin 的 GPU 资源分配方式
- ../../terraform/Terraform 基础设施即代码 — GPU 节点池的创建和 GPU Operator 部署
参考资源
- containerd 文档:https://github.com/containerd/containerd/blob/main/docs/
- CRI-O 文档:https://github.com/cri-o/cri-o/blob/main/tutorial.md
- gVisor 文档:https://gvisor.dev/docs/
- Kata Containers 文档:https://katacontainers.io/docs/
- NVIDIA Container Toolkit:https://docs.nvidia.com/datacenter/cloud-native/container-toolkit/
学习时间
| 阶段 | 时间 | 备注 |
|---|---|---|
| 对比与配置 | 2026-07-02 | 完成:CRI 协议、containerd vs CRI-O、crictl、安全运行时、GPU 集成、排障 |
状态: 🌱 学习中 下次复习日期: 2026-07-09