4. cgroups v2 CPU 子系统:架构、权重、上限与绑核
cgroups v2 是 Linux 内核统一控制组接口,在容器运行时中逐步取代 v1。本章聚焦其 CPU 子系统的核心设计:扁平化架构、cpu.weight 与 cpu.max 的调度语义,以及 cpuset 控制器的绑核实现。理解这些机制是后续配置 containerd CPU 管理策略的基础。
4.1 cgroups v2 统一架构
cgroups v2 最显著的变化是采用单一层级树(Unified Hierarchy),所有控制器挂载在同一个虚拟文件系统下,不再允许 v1 中每个控制器独立挂载不同层级。这一设计消除了 v1 中因多层级导致的资源竞争与状态不一致问题。
关键变化: v2 中所有进程属于同一个层级树,控制器通过
cgroup.subtree_control 文件按需启用,子 cgroup 继承父级控制器状态。
4.2 cpu.weight:权重分配机制
cpu.weight 是 cgroups v2 中控制 CPU 时间分配的核心参数,取代 v1 的 cpu.shares。它定义 cgroup 在 CPU 竞争时的相对权重,仅在系统 CPU 资源紧张时生效。
| 参数 | cgroups v1 (cpu.shares) | cgroups v2 (cpu.weight) |
|---|---|---|
| 取值范围 | 2 – 262144 | 1 – 10000 |
| 默认值 | 1024 | 100 |
| 语义 | 相对份额,值越大获得 CPU 时间越多 | 相对权重,值越大获得 CPU 时间越多 |
| 空闲 CPU 处理 | 可超用(burst) | 可超用,配合 cpu.max 限制上限 |
实践建议: 在 containerd 中,
cpu.weight 对应 Kubernetes 的 requests.cpu 语义。例如两个容器权重分别为 100 和 200,在 CPU 满载时前者获得 1/3 时间,后者获得 2/3 时间。
4.3 cpu.max:硬上限与带宽控制
cpu.max 是 cgroups v2 中设置 CPU 使用硬上限的文件,格式为 $MAX $PERIOD。它限制 cgroup 在每个周期内最多使用的 CPU 时间,防止容器过度占用宿主机资源。
# 格式:cpu.max = <quota> <period>
# 示例:限制容器在 100ms 周期内最多使用 50ms CPU 时间(即 0.5 核)
echo "50000 100000" > /sys/fs/cgroup/<container>/cpu.max
# 示例:无限制(默认)
echo "max 100000" > /sys/fs/cgroup/<container>/cpu.max
| 参数 | 说明 | 典型值 |
|---|---|---|
| quota | 每个周期内允许的最大 CPU 时间(微秒) | 50000(0.5 核)、100000(1 核) |
| period | 周期长度(微秒),默认 100000 | 100000(100ms) |
| max | 表示无上限,允许超用 | max |
注意:
cpu.max 与 cpu.weight 协同工作:权重决定竞争时的分配比例,上限决定最大值。设置过小的 quota 可能导致容器频繁被节流(throttling),影响延迟敏感型应用。
4.4 cpuset 控制器:绑核与内存亲和性
cpuset 控制器在 cgroups v2 中负责将 cgroup 绑定到特定 CPU 核心和内存节点。它通过两个核心文件实现绑核:
cpuset.cpus:允许使用的 CPU 核心列表(如0-3,7)。cpuset.mems:允许使用的内存节点列表(如0或0-1)。
绑核的核心价值在于减少 CPU 缓存失效和上下文切换,提升性能敏感型工作负载的确定性。
4.5 配置示例:在 containerd 中使用 cpuset
以下示例展示如何通过 containerd 的 cri 插件为容器配置 cpuset 绑核:
# containerd 配置文件 /etc/containerd/config.toml
[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
# 创建容器时通过 cgroup 路径设置 cpuset
# 假设容器 cgroup 路径为 /sys/fs/cgroup/kubepods/besteffort/podxxx/container-yyy
echo "0-3" > /sys/fs/cgroup/kubepods/besteffort/podxxx/container-yyy/cpuset.cpus
echo "0" > /sys/fs/cgroup/kubepods/besteffort/podxxx/container-yyy/cpuset.mems
性能收益: 在 2 路 NUMA 服务器上,将 Redis 容器绑定到单个 NUMA 节点的核心,可减少 30%-50% 的跨 NUMA 内存访问延迟,吞吐量提升 15%-25%。