文章

容器运行时深度对比

容器运行时深度对比

概述

Kubernetes 从 v1.24 起正式移除了 dockershim,容器运行时通过 CRI(Container Runtime Interface) 标准接口与 kubelet 通信。目前 K8s 生态中两个主流的 CRI 实现是 containerdCRI-O,底层都通过 runc 创建容器。此外,gVisorKata Containers 提供了更强的安全隔离。

一句话:如果 K8s 是云原生的操作系统,容器运行时就是它的”内核”——kubelet 不直接操作容器,所有 Pod 创建/销毁/日志操作都通过 CRI 委托给运行时。

CRI 协议概述

CRI 是一套 gRPC API,定义了两个服务:

服务职责关键 RPC
RuntimeServicePod 和容器的生命周期管理RunPodSandboxCreateContainerStartContainerStopContainerRemoveContainer
ImageService镜像管理PullImageListImagesRemoveImageImageStatus
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
snapshotteroverlayfs(默认)、devmapper、btrfs、zfs
GPU 支持NVIDIA Container Toolkit(nvidia-container-runtime 作为 OCI runtime)
配置路径/etc/containerd/config.toml
管理 CLIctr(底层)、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
snapshotteroverlayfs(默认)、devmapper
GPU 支持NVIDIA Container Toolkit
配置路径/etc/crio/crio.conf
管理 CLIcrictl
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 存在,不做多余的事

选型对比

维度containerdCRI-O
K8s 默认✅ (v1.24+)❌(Red Hat 系除外)
非 K8s 使用✅ 可作为通用容器引擎
Docker 兼容nerdctl
镜像构建✅ BuildKit
复杂度中等
社区生态极大(Docker/Moby 生态)集中(K8s/OpenShift)
安全审计面较大较小(代码量更少)
OCI runtimeruncrunc + crun(可选,C 语言实现更快)
典型用户通用 K8s、GKE、EKS、AKSOpenShift、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 → 宿主机

对比

维度runcgVisorKata
隔离层级进程级(Namespace)用户态内核硬件虚拟化
内核共享宿主机Sentry(Go)独立内核
启动速度~50ms~100ms~150-300ms
性能损失0%5-15%10-20%
GPU 支持✅ 原生❌ 不支持❌ 不支持(社区实验性)
适用场景自己集群的可信工作负载多租户 SaaS、Serverless最高安全要求、不可信代码执行
runtimeClass 名称runc(默认)gvisorkata

在 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/stdoutcontainerd 使用 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

关联知识

参考资源

学习时间

阶段时间备注
对比与配置2026-07-02完成:CRI 协议、containerd vs CRI-O、crictl、安全运行时、GPU 集成、排障

状态: 🌱 学习中 下次复习日期: 2026-07-09