4. CPU 控制器详解:cpu.weight、cpu.max、cpu.stat 的配置与监控

Cgroup v2 的 CPU 控制器是容器运行时容量运维的核心组件,它通过三个关键接口文件实现 CPU 资源的分配、限制与监控。本章将深入解析 cpu.weight(权重分配)、cpu.max(硬限制)和 cpu.stat(状态监控)的工作原理、配置方法及运维实践。

4.1 CPU 控制器架构概览

在 Cgroup v2 中,CPU 控制器采用“权重 + 上限”的双层模型:权重决定竞争时的比例分配,上限则提供的资源天花板。下图展示了 CPU 控制器的核心架构与数据流。

CPU 控制器核心架构 用户空间配置接口 cpu.weight 权重分配(1-10000) cpu.max 硬限制(配额/周期) cpu.stat 状态与统计监控 CFS 完全公平调度器(内核空间) 物理 CPU 核心 权重决定竞争比例 → cpu.max 设置上限 → cpu.stat 提供监控反馈

4.2 cpu.weight:权重分配机制

cpu.weight 控制组之间 CPU 时间的相对比例,仅在 CPU 资源竞争时生效。默认值为 100,取值范围 1-10000。

4.2.1 权重分配原理

当多个 cgroup 同时争用 CPU 时,调度器按权重比例分配 CPU 时间。计算公式如下:

cgroup_i 的 CPU 份额 = (weight_i) / (所有活跃 cgroup 的 weight 之和) × 可用 CPU 时间
关键理解:权重是相对值而非值。如果只有一个 cgroup 活跃,即使 weight=1 也能使用全部 CPU;当多个 cgroup 竞争时,权重比例决定分配。

4.2.2 权重配置示例

以下示例展示如何为两个容器设置不同的 CPU 权重:

# 容器 A:高优先级(weight=200)
echo 200 > /sys/fs/cgroup/system.slice/docker-containerA.scope/cpu.weight

# 容器 B:低优先级(weight=50)
echo 50 > /sys/fs/cgroup/system.slice/docker-containerB.scope/cpu.weight

# 验证配置
cat /sys/fs/cgroup/system.slice/docker-containerA.scope/cpu.weight
cat /sys/fs/cgroup/system.slice/docker-containerB.scope/cpu.weight

当两个容器同时满载运行时,CPU 分配比例为 200:50 = 4:1,即容器 A 获得约 80% 的 CPU 时间,容器 B 获得约 20%。

4.2.3 权重与 CPU 数量的关系

权重值 含义 典型场景
1 最低优先级,仅当无竞争时获得 CPU 后台日志处理、数据备份
100 默认权重,基线优先级 普通业务容器
500 高优先级,获得 5 倍于默认的份额 核心 API 服务、实时处理
1000 极高优先级,适合关键控制面组件 Kubernetes 控制面、etcd
10000 最大权重,几乎独占竞争资源 紧急恢复、关键监控代理

4.3 cpu.max:硬限制配置

cpu.max 以“配额/周期”格式设置 CPU 使用的上限,格式为 $quota $period。默认值为 max 100000(无限制)。

4.3.1 配额与周期详解

周期(period)默认为 100000μs(100ms),配额(quota)表示在每个周期内允许使用的 CPU 时间。配额值可以大于周期值,表示可以使用超过 1 个 CPU 核心。

配置值 含义 等效 CPU 核心数
max 100000 无限制 所有可用核心
50000 100000 每 100ms 最多使用 50ms CPU 0.5 核心
100000 100000 每 100ms 最多使用 100ms CPU 1 核心
200000 100000 每 100ms 最多使用 200ms CPU 2 核心
250000 100000 每 100ms 最多使用 250ms CPU 2.5 核心

4.3.2 配置示例与验证

# 限制容器使用 1.5 个 CPU 核心
echo "150000 100000" > /sys/fs/cgroup/system.slice/docker-container.scope/cpu.max

# 验证配置
cat /sys/fs/cgroup/system.slice/docker-container.scope/cpu.max
# 输出: 150000 100000

# 移除限制(设置为无限制)
echo "max 100000" > /sys/fs/cgroup/system.slice/docker-container.scope/cpu.max
运维警告:设置过小的配额(如 10000 100000,即 0.1 核心)可能导致容器频繁被节流(throttled),引发延迟敏感型应用的性能抖动。建议结合 cpu.stat 监控节流情况。

4.4 cpu.stat:状态监控与诊断

cpu.stat 文件提供 CPU 使用和节流的关键统计信息,是容量运维的核心监控数据源。

4.4.1 关键字段解读

字段名 单位 含义 运维关注点
usage_usec 微秒 该 cgroup 累计使用的 CPU 时间 计算 CPU 使用率的基础
user_usec 微秒 用户态累计 CPU 时间 区分用户态/内核态开销
system_usec 微秒 内核态累计 CPU 时间 系统调用密集度判断
nr_periods 次数 经过的调度周期总数 监控周期基数
nr_throttled 次数 被节流的周期数 核心指标:节流频率
throttled_usec 微秒 累计被节流的总时间 核心指标:节流严重程度

4.4.2 监控与诊断实践

以下脚本展示如何实时监控 CPU 节流情况:

#!/bin/bash
# cpu_throttle_monitor.sh - 监控容器 CPU 节流

CGROUP_PATH="/sys/fs/cgroup/system.slice/docker-container.scope"
INTERVAL=5

echo "监控 CPU 节流情况(每 ${INTERVAL} 秒)..."
echo "时间戳 | 使用率(%) | 节流次数 | 节流比例(%) | 节流时间(ms)"

while true; do
  STAT=$(cat ${CGROUP_PATH}/cpu.stat)
  MAX=$(cat ${CGROUP_PATH}/cpu.max | awk '{print $1}')

  USAGE=$(echo "$STAT" | grep usage_usec | awk '{print $2}')
  PERIODS=$(echo "$STAT" | grep nr_periods | awk '{print $2}')
  THROTTLED=$(echo "$STAT" | grep nr_throttled | awk '{print $2}')
  THROTTLED_USEC=$(echo "$STAT" | grep throttled_usec | awk '{print $2}')

  # 计算使用率(基于最近间隔)
  sleep ${INTERVAL}
  STAT_NEW=$(cat ${CGROUP_PATH}/cpu.stat)
  USAGE_NEW=$(echo "$STAT_NEW" | grep usage_usec | awk '{print $2}')
  PERIODS_NEW=$(echo "$STAT_NEW" | grep nr_periods | awk '{print $2}')
  THROTTLED_NEW=$(echo "$STAT_NEW" | grep nr_throttled | awk '{print $2}')

  USAGE_DELTA=$(( (USAGE_NEW - USAGE) / 1000 ))  # 转换为 ms
  PERIODS_DELTA=$(( PERIODS_NEW - PERIODS ))
  THROTTLED_DELTA=$(( THROTTLED_NEW - THROTTLED ))

  # 计算 CPU 使用率(百分比)
  CPU_PERCENT=$(echo "scale=2; ${USAGE_DELTA} / (${INTERVAL} * 1000) * 100" | bc)

  # 计算节流比例
  if [ ${PERIODS_DELTA} -gt 0 ]; then
    THROTTLE_RATIO=$(echo "scale=2; ${THROTTLED_DELTA} / ${PERIODS_DELTA} * 100" | bc)
  else
    THROTTLE_RATIO="0.00"
  fi

  TIMESTAMP=$(date "+%H:%M:%S")
  echo "${TIMESTAMP} | ${CPU_PERCENT}% | ${THROTTLED_DELTA} | ${THROTTLE_RATIO}% | $(( THROTTLED_USEC / 1000 ))ms"
done

4.5 权重与上限的协同策略

在实际运维中,cpu.weight 和 cpu.max 需要协同配置才能实现精细化的资源管理。下图展示了不同配置组合的效果。

权重与上限协同策略矩阵 高权重 + 高上限 weight=500, max=200000 竞争时优先获得资源 无硬限制时可爆发使用 适用:核心控制面、API 网关 高权重 + 低上限 weight=500, max=50000 竞争时优先但受硬限制 防止单个容器过度消耗 适用:高优但需限流的批处理 低权重 + 高上限 weight=50, max=max 空闲时可利用全部资源 竞争时让出资源给高优任务 适用:后台分析、日志处理 低权重 + 低上限 weight=50, max=30000 严格限制资源使用 竞争和空闲时均受限 适用:非关键任务、沙箱容器 ← 权重(竞争优先级)→ ← 上限(限制)→

4.6 运维最佳实践

容量运维黄金法则:
  1. 先设上限,再调权重:先用 cpu.max 防止资源滥用,再用 cpu.weight 优化竞争分配。
  2. 监控节流比例:nr_throttled / nr_periods 超过 5% 时,考虑增加配额或优化应用。
  3. 避免过度限制:配额设置过低会导致频繁节流,增加上下文切换开销。
  4. 结合 NUMA 拓扑:在多 NUMA 节点环境中,配合 cpuset 控制器使用效果更佳。
  5. 定期校准:业务负载变化后,重新评估权重和上限配置。

4.7 常见问题排查

现象 可能原因 排查方法 解决方案
容器 CPU 使用率低但延迟高 频繁节流(throttling) 检查 cpu.stat 的 nr_throttled 增加 cpu.max 配额
高优容器未获得预期资源 权重配置未生效或存在更高优任务 检查所有活跃 cgroup 的权重值 调整权重比例或使用 cpu.max 限制低优任务
CPU 使用率波动剧烈 配额/周期设置不合理 分析 throttled_usec 时间分布 增大周期值(如 200000μs)平滑分配
设置 cpu.max 后无效果 CPU 控制器未启用 检查 /sys/fs/cgroup/cgroup.controllers 向 cgroup.subtree_control 写入 +cpu
重要提示:在 Kubernetes 环境中,cpu.weight 对应 Pod 的 requests.cpu,cpu.max 对应 limits.cpu。理解底层映射关系有助于更精准地配置容器资源。