4. cgroup v2 CPU 控制器:cpu.weight、cpu.max、cpu.stat 接口变更与优势

cgroup v2 对 CPU 控制器进行了根本性的重构,摒弃了 v1 中多个独立子系统(cpu、cpuacct)并存的复杂模型,转而采用统一的 cpu 控制器。这一变革带来了更清晰的层次结构、更直观的权重分配机制以及更强大的带宽控制能力。本节将深入剖析 v2 核心接口的设计哲学与操作优势。

4.1 核心接口变更:从 shares/quota 到 weight/max

cgroup v2 将 v1 中的 cpu.shares、cpu.cfs_quota_us、cpu.cfs_period_us 等分散文件,整合为两个核心文件:cpu.weight 和 cpu.max。这种设计不仅减少了文件数量,更在语义上实现了统一。

功能维度 cgroup v1 接口 cgroup v2 接口 变更要点
相对权重 cpu.shares(范围:2-262144) cpu.weight(范围:1-10000) 范围缩小,默认值从 1024 变为 100,更易理解
带宽上限 cpu.cfs_quota_us + cpu.cfs_period_us cpu.max(格式:$MAX $PERIOD) 合并为一个文件,支持 max 关键字表示无限制
CPU 统计 cpuacct.usage、cpu.stat(分散) cpu.stat(统一) 整合调度与记账统计,新增压力指标
层级继承 子组默认继承父组 shares 子组默认继承父组 weight,但可独立覆盖 继承逻辑更清晰,支持按需调整
迁移提示: 从 v1 迁移到 v2 时,cpu.shares 到 cpu.weight 的转换并非线性。v2 的 weight 值 100 对应 v1 的 1024,但 v2 的权重竞争模型在低值区间(1-100)提供了更精细的控制粒度,适合对延迟敏感的容器场景。

4.2 cpu.weight:细粒度权重分配

cpu.weight 是 cgroup v2 中实现 CPU 时间相对分配的核心参数。与 v1 的 cpu.shares 相比,v2 的权重模型在竞争激烈时表现更平滑,且对 CFS 调度器的负载均衡算法进行了优化。

  • 范围与默认值: 取值范围为 1(最低优先级)到 10000(最高优先级),默认值为 100。这个范围设计使得在容器编排平台(如 Kubernetes)中,可以轻松地将 QoS 等级映射为具体的 weight 值。
  • 竞争模型: 当多个 cgroup 同时争用 CPU 时,调度器按 weight / 所有活跃子组 weight 之和 的比例分配时间片。空闲 CPU 不会被浪费,而是按比例分配给有需求的组。
  • 层级行为: 子 cgroup 的 weight 仅在兄弟节点之间生效。父组的 weight 决定了整个子树在全局中的份额,而子组之间的竞争则基于各自的 weight 值。
# 创建两个竞争组
mkdir -p /sys/fs/cgroup/cpu/container-A /sys/fs/cgroup/cpu/container-B

# 设置权重:A 获得 2/3,B 获得 1/3
echo 200 > /sys/fs/cgroup/cpu/container-A/cpu.weight
echo 100 > /sys/fs/cgroup/cpu/container-B/cpu.weight

# 启动两个 CPU 密集型进程并加入对应组
# 预期:container-A 中的进程将获得约 2 倍于 container-B 的 CPU 时间
最佳实践: 在容器化环境中,建议将 cpu.weight 与容器的资源请求(requests)关联。例如,一个请求 2 个 CPU 的容器,其 weight 可以设置为 200,而请求 1 个 CPU 的容器设置为 100。这样在资源争用时,权重比例自然反映业务优先级。

4.3 cpu.max:统一的带宽上限控制

cpu.max 文件取代了 v1 中 cpu.cfs_quota_us 和 cpu.cfs_period_us 的组合,以 $MAX $PERIOD 的格式提供带宽限制。其核心优势在于语法简洁、支持动态调整,并且引入了 max 关键字来明确表示“无限制”。

配置场景 v1 写法 v2 写法 说明
限制为 1.5 个 CPU echo 150000 > cpu.cfs_quota_us
echo 100000 > cpu.cfs_period_us
echo "150000 100000" > cpu.max v2 合并为一行,减少原子性问题
无限制 echo -1 > cpu.cfs_quota_us echo "max 100000" > cpu.max v2 使用 max 关键字,语义更清晰
动态调整周期 需分别修改两个文件 一次性写入新值 v2 减少竞态条件风险
# 限制容器最多使用 2 个 CPU 核心(周期 100ms)
echo "200000 100000" > /sys/fs/cgroup/cpu/container-A/cpu.max

# 移除限制,允许使用所有空闲 CPU
echo "max 100000" > /sys/fs/cgroup/cpu/container-A/cpu.max
注意事项: 设置 cpu.max 时,$MAX 值必须小于或等于 $PERIOD 乘以 CPU 核心数。例如,在 4 核系统上,周期为 100000us(100ms)时,$MAX 最大可设置为 400000。超出此范围的值将被内核静默截断,可能导致实际限制与预期不符。

4.4 cpu.stat:统一且丰富的统计视图

cgroup v2 的 cpu.stat 文件整合了 v1 中分散在 cpu.stat 和 cpuacct.stat 中的信息,并新增了调度延迟和压力指标,为运维监控提供了更全面的数据源。

# 查看某个容器的 CPU 统计
cat /sys/fs/cgroup/cpu/container-A/cpu.stat

# 输出示例:
# usage_usec 123456789          # 总 CPU 使用时间(微秒)
# user_usec 100000000           # 用户态使用时间
# system_usec 23456789          # 内核态使用时间
# nr_periods 500                # 经过的周期数(仅当设置了 cpu.max 时)
# nr_throttled 10               # 被限流的周期数
# throttled_usec 5000000        # 被限流的总时间(微秒)
# nr_bursts 0                   # 突发使用次数(v6.1+ 新增)
# burst_usec 0                  # 突发使用总时间(v6.1+ 新增)
统计字段 含义 运维价值
usage_usec 该 cgroup 及其子 cgroup 的总 CPU 消耗 用于容量规划和计费
nr_throttled 因超出 cpu.max 而被限流的周期数 判断容器是否频繁触及上限,辅助调整配额
throttled_usec 被限流的总时长 量化限流对应用性能的影响
nr_bursts / burst_usec 突发使用统计(需内核支持 SCHED_BURST) 评估突发能力是否被有效利用

4.5 架构优势对比:v1 与 v2 的 CPU 控制路径

下面的 SVG 图展示了 cgroup v1 和 v2 在 CPU 控制路径上的架构差异。v2 通过统一的 cpu 控制器,简化了数据流和决策路径,减少了内核态的开销。

cgroup v1 vs v2 CPU 控制路径架构对比 cgroup v1(分散控制) cpu.shares cpu.cfs_quota_us cpu.cfs_period_us cpuacct.usage cpu.stat(v1) cpuacct.stat CFS 调度器(多路径输入) 问题:文件分散、原子性差、 统计路径不一致 cgroup v2(统一控制) cpu.weight cpu.max cpu.stat(统一统计) CFS 调度器(统一输入) 优势:接口统一、原子写入、 统计完整、支持 max 关键字 重构 v2 通过减少文件数量和统一写入路径,降低了竞态条件风险,提升了调度决策的原子性

4.6 容器化环境中的实际优势

在 Kubernetes 等容器编排平台中,cgroup v2 的 CPU 控制器带来了以下可量化的改进:

  1. 配置原子性: 写入 cpu.max 是一次系统调用,避免了 v1 中先后写入 quota 和 period 可能导致的中间状态(例如 quota 已更新但 period 未更新,导致调度器读到不一致的配置)。
  2. 统计一致性: cpu.stat 文件中的 usage_usec 等字段,在读取时保证是该 cgroup 及其所有子 cgroup 的瞬时快照,无需像 v1 那样从多个文件拼凑数据。
  3. 层级管理简化: 在 v2 中,Pod 级别的 QoS 类(Guaranteed、Burstable、BestEffort)可以直接映射为 cpu.weight 值,而无需像 v1 那样通过复杂的 shares 计算和额外的 cpu.rt_runtime_us 配置。
  4. 压力感知调度: v2 的 cpu.stat 为后续的 PSI(Pressure Stall Information)提供了基础数据,使得调度器可以感知 CPU 压力,从而做出更优的负载均衡决策。
关键结论: cgroup v2 的 CPU 控制器通过接口精简、语义统一和统计整合,显著降低了容器化环境中资源管理的复杂度和出错概率。对于运行现代 Linux 内核(5.8+)的生产集群,强烈建议启用 cgroup v2 以获得更稳定、更可预测的 CPU 调度行为。