runc与cgroup v2资源控制及运维故障诊断 - 第2节

2.1 RunC 运行时架构全景

RunC 是 OCI(Open Container Initiative)运行时规范的参考实现,也是容器运行时生态中最底层的执行引擎。理解 RunC 的架构,是掌握 Cgroup v2 协同运维的前提——因为所有资源限制最终都通过 RunC 传递给内核。

核心定位:RunC 是一个轻量级、可嵌入的 CLI 工具和库,用于按照 OCI 规范创建和运行容器。它直接操作 Linux 内核的 Namespace 和 Cgroup,不依赖任何上层编排系统。

2.1.1 RunC 核心组件

RunC 的架构可以拆解为以下五个核心组件,它们协同完成容器的全生命周期管理:

组件 职责 关键文件/接口
runc CLI 用户态命令行入口,解析子命令(create、start、run、exec、ps、delete 等) runc 二进制
libcontainer 核心库,封装 Namespace、Cgroup、Capabilities、Mount 等内核操作 libcontainer/ 目录
configs OCI 配置解析层,将 config.json 转换为内核可执行的参数 config.json、spec.go
cgroups manager Cgroup 驱动管理,支持 cgroupfs、systemd 两种驱动模式 libcontainer/cgroups/
init 进程 容器内第一个进程(PID 1),负责信号转发、子进程收割 libcontainer/init_linux.go
运维提示:在 Cgroup v2 环境下,cgroups manager 组件的行为会发生变化——v2 统一层级树要求所有控制器挂载在同一个路径下,RunC 会自动检测内核版本并切换驱动模式。
RunC 组件架构与调用链路 runc CLI configs (OCI 解析) libcontainer 核心库 Namespace / Capabilities / Mount / Seccomp cgroups manager cgroupfs / systemd 驱动 init 进程 (PID 1) 信号转发 / 子进程收割 Cgroup v2 环境下,cgroups manager 自动切换为 unified 层级驱动

2.2 OCI 运行时规范详解

OCI(Open Container Initiative)定义了容器运行时的两个核心规范:Runtime Specification(runtime-spec) 和 Image Specification(image-spec)。RunC 是 runtime-spec 的官方参考实现。

2.2.1 OCI 运行时规范核心要素

一个符合 OCI 标准的容器运行时,必须能够处理以下配置结构:

  • ociVersion:规范版本号,当前主流为 1.0.2 或 1.1.0
  • root:容器根文件系统路径,只读或读写挂载
  • mounts:额外挂载点列表,包括绑定挂载、tmpfs、procfs 等
  • process:容器内进程配置,包括 args、env、cwd、capabilities、rlimits、selinux 标签等
  • hostname:容器主机名
  • linux:Linux 特有配置,包括 Namespace、Cgroup 路径、设备白名单、seccomp、sysctl 等
Cgroup v2 关键差异:在 OCI runtime-spec 的 linux.resources 部分,v2 规范要求所有资源限制(CPU、Memory、IO、PIDs)统一放置在 unified 字段下,不再区分 cpu、memory 等子对象。RunC 1.0+ 已完全支持此格式。

2.2.2 config.json 示例(Cgroup v2 兼容)

{
  "ociVersion": "1.0.2",
  "root": { "path": "rootfs", "readonly": true },
  "process": {
    "terminal": true,
    "user": { "uid": 0, "gid": 0 },
    "args": ["/bin/sh"],
    "env": ["PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"],
    "cwd": "/",
    "capabilities": {
      "bounding": ["CAP_NET_RAW", "CAP_CHOWN"],
      "effective": ["CAP_NET_RAW", "CAP_CHOWN"]
    }
  },
  "linux": {
    "namespaces": [
      { "type": "pid" },
      { "type": "network" },
      { "type": "mount" },
      { "type": "uts" }
    ],
    "resources": {
      "unified": {
        "memory.max": "536870912",
        "cpu.max": "50000 100000",
        "io.max": "8:0 rbps=1048576 wbps=1048576"
      }
    },
    "cgroupsPath": "/runc-cgroupv2-demo"
  }
}
运维要点:注意 unified 字段中的键名直接对应 Cgroup v2 控制器的接口文件名(如 memory.max、cpu.max、io.max),这是 v2 与 v1 最显著的区别。

2.3 RunC 与 containerd 的关系

在 Kubernetes 容器运行时生态中,RunC 和 containerd 处于不同的抽象层级。理解它们的协作关系,是进行运行时容量运维的基础。

2.3.1 分层架构

层级 组件 职责 接口
编排层 Kubernetes / kubelet Pod 调度、生命周期管理 CRI (Container Runtime Interface)
容器管理引擎 containerd 镜像管理、存储、网络、容器生命周期编排 CRI + containerd API
低级运行时 RunC OCI 规范执行、Namespace/Cgroup 操作 OCI Runtime Spec
内核层 Linux Kernel Namespace、Cgroup v2、文件系统 系统调用

2.3.2 调用流程

当 kubelet 请求创建一个 Pod 时,完整的调用链路如下:

  1. kubelet 通过 CRI 接口调用 containerd
  2. containerd 拉取镜像、解压快照、准备挂载点
  3. containerd 通过 ctr 或内部 shim 进程调用 RunC
  4. RunC 解析 config.json,创建 Namespace,配置 Cgroup v2
  5. RunC 执行容器 init 进程,返回 PID 给 containerd
  6. containerd 将容器状态上报给 kubelet
RunC 与 containerd 协作架构 kubelet (CRI 客户端) CRI 接口 containerd 镜像管理 / 快照 shim 进程 容器状态管理 OCI 运行时调用 RunC (OCI 运行时) Linux Kernel (Cgroup v2)

2.3.3 关键差异对比

维度 containerd RunC
抽象层级 容器管理引擎(高层) 容器执行器(底层)
镜像管理 支持拉取、存储、快照 不处理镜像,只操作 rootfs
网络配置 通过 CNI 插件配置网络 仅创建 Network Namespace
Cgroup 管理 传递资源限制参数 直接写入 Cgroup 接口文件
进程生命周期 管理容器状态机 管理 init 进程及子进程
适用场景 Kubernetes 节点、生产集群 调试、单容器运行、嵌入开发
容量运维视角:在 Cgroup v2 环境下,containerd 负责将 Pod 级别的资源配额(来自 kubelet)转换为 OCI 规范的 unified 字段,RunC 负责将这些配置写入 /sys/fs/cgroup/ 下的具体接口文件。任何资源限制的变更,最终都要经过 RunC 的 cgroups manager 落地到内核。

2.4 本章小结

  • RunC 由 CLI、libcontainer、configs、cgroups manager、init 进程 五大组件构成,其中 cgroups manager 在 v2 环境下自动切换驱动
  • OCI 运行时规范定义了容器的标准配置结构,Cgroup v2 下资源限制统一使用 unified 字段
  • containerd 与 RunC 是 管理引擎与执行器 的关系,containerd 负责上层编排,RunC 负责内核级操作
  • 理解这三者的关系,是后续章节进行 Cgroup v2 容量规划、监控、排障的理论基础