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