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 会自动检测内核版本并切换驱动模式。
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 时,完整的调用链路如下:
- kubelet 通过 CRI 接口调用 containerd
- containerd 拉取镜像、解压快照、准备挂载点
- containerd 通过
ctr或内部shim进程调用 RunC - RunC 解析
config.json,创建 Namespace,配置 Cgroup v2 - RunC 执行容器 init 进程,返回 PID 给 containerd
- containerd 将容器状态上报给 kubelet
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 容量规划、监控、排障的理论基础