对比 `cgroup v2 cpu.max` 与 `cpu.shares` 对剖析结果的影响

解读

国内云原生面试常把“资源隔离”与“可观测性”放在一起拷问。
cpu.sharescgroup v1 的相对权重机制,只在整机 CPU 繁忙时生效,数值无单位,剖析工具只能看到“谁抢到的 CPU 时间多”,却无法量化“到底能跑多少”。
cpu.maxcgroup v2 的绝对带宽控制器,格式 quota period,直接给出每周期内可用时间,内核会强制节流,剖析结果里能精确看到“被 throttle 了多少 ns”以及“实际使用率与上限的差值”。
因此,同一应用在不同控制器下的火焰图、perf 采样、容器 CPU 利用率等指标会呈现明显差异cpu.shares 场景下看似“热点”可能是资源竞争,而 cpu.max 场景下“热点”就是真正的代码热点。

知识点

  1. cgroup v1 cpu.shares
    • 权重范围 2–1024,默认 1024
    • 只在整机满载时按比例分配,单核空闲时可超限
    • 内核统计字段 cpu.stat 仅提供 nr_throttledthrottled_time无法区分是权重不足还是空闲让出
  2. cgroup v2 cpu.max
    • 格式 quota period,如 200000 100000 表示每 100 ms 最多跑 200 ms(2 核)
    • 绝对上限,不管机器是否空闲,超即节流
    • 内核新增 cpu.stat 字段 usage_usecthrottled_usecnr_periodsnr_throttled可精确计算被压制比例
  3. 剖析工具差异
    • perf/ebpf:在 cpu.shares 下采样到的 CPU 周期可能随竞争动态变化,同一段代码在不同采样轮次占比差异大;在 cpu.max 下采样总量被硬封顶,火焰图稳定、可重现
    • cadvisor + prometheus:cpu.shares 场景下 container_cpu_usage_seconds_total 可能持续飙高但 throttled_time 为 0,误导 SRE 以为应用吃满配额cpu.max 场景下 throttled_timeusage 同步增长,一眼可知是否真缺 CPU
  4. Docker 默认行为
    • 国内主流发行版(Aliyun Linux、TencentOS、CentOS 7)默认仍开 v1,需手动开启 grub 参数 systemd.unified_cgroup_hierarchy=1 才能用 cpu.max
    • docker run --cpu-shares 映射到 cpu.shares--cpus / --quota / --period 在 v2 下统一转成 cpu.max

答案

“cpu.shares 给出的是相对优先级,只在节点打满时生效,剖析结果里看到的 CPU 时间片波动大,难以判断是代码慢还是资源抢不到;cpu.max 给出的是绝对时间片上限,内核强制节流,剖析工具能直接读到被 throttle 的精确时长,火焰图与 CPU 利用率曲线稳定,热点即代码热点,不再被资源竞争干扰。因此,在线上压测或性能回归场景,优先用 cgroup v2 + cpu.max 做基准剖析,可一次性定位是真瓶颈还是配额不足。”

拓展思考

  1. 混部场景:国内大厂晚高峰常把在线与离线任务混布,用 cpu.shares 做离线压制;但在线业务需绝对兜底,此时给在线容器配 cpu.max 保底、离线容器用 cpu.shares 弹性,剖析时就能通过 throttled_usec 快速量化离线任务对在线的干扰。
  2. 超卖比量化:在 v2 下把 quota 设得远小于 period(如 0.5 核),结合 nr_throttled / nr_periods 可算出实际可超卖比例,为成本核算提供数据,而 v1 的 cpu.shares 无法得出该指标。
  3. eBPF 自动调参:利用 cpu.max 的精确节流事件,编写 eBPF 程序动态上调 quota,实现“无感扩容”,这是国内金融云正在落地的 “弹性配额” 方案,面试中可借此展示对内核与业务联动的深度理解。