对比 `cgroup v2 cpu.max` 与 `cpu.shares` 对剖析结果的影响
解读
国内云原生面试常把“资源隔离”与“可观测性”放在一起拷问。
cpu.shares 是 cgroup v1 的相对权重机制,只在整机 CPU 繁忙时生效,数值无单位,剖析工具只能看到“谁抢到的 CPU 时间多”,却无法量化“到底能跑多少”。
cpu.max 是 cgroup v2 的绝对带宽控制器,格式 quota period,直接给出每周期内可用时间,内核会强制节流,剖析结果里能精确看到“被 throttle 了多少 ns”以及“实际使用率与上限的差值”。
因此,同一应用在不同控制器下的火焰图、perf 采样、容器 CPU 利用率等指标会呈现明显差异:cpu.shares 场景下看似“热点”可能是资源竞争,而 cpu.max 场景下“热点”就是真正的代码热点。
知识点
- cgroup v1 cpu.shares
- 权重范围 2–1024,默认 1024
- 只在整机满载时按比例分配,单核空闲时可超限
- 内核统计字段
cpu.stat仅提供nr_throttled与throttled_time,无法区分是权重不足还是空闲让出
- cgroup v2 cpu.max
- 格式
quota period,如200000 100000表示每 100 ms 最多跑 200 ms(2 核) - 绝对上限,不管机器是否空闲,超即节流
- 内核新增
cpu.stat字段usage_usec、throttled_usec、nr_periods、nr_throttled,可精确计算被压制比例
- 格式
- 剖析工具差异
- perf/ebpf:在
cpu.shares下采样到的 CPU 周期可能随竞争动态变化,同一段代码在不同采样轮次占比差异大;在cpu.max下采样总量被硬封顶,火焰图稳定、可重现 - cadvisor + prometheus:
cpu.shares场景下container_cpu_usage_seconds_total可能持续飙高但throttled_time为 0,误导 SRE 以为应用吃满配额;cpu.max场景下throttled_time与usage同步增长,一眼可知是否真缺 CPU
- perf/ebpf:在
- 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
- 国内主流发行版(Aliyun Linux、TencentOS、CentOS 7)默认仍开 v1,需手动开启 grub 参数
答案
“cpu.shares 给出的是相对优先级,只在节点打满时生效,剖析结果里看到的 CPU 时间片波动大,难以判断是代码慢还是资源抢不到;cpu.max 给出的是绝对时间片上限,内核强制节流,剖析工具能直接读到被 throttle 的精确时长,火焰图与 CPU 利用率曲线稳定,热点即代码热点,不再被资源竞争干扰。因此,在线上压测或性能回归场景,优先用 cgroup v2 + cpu.max 做基准剖析,可一次性定位是真瓶颈还是配额不足。”
拓展思考
- 混部场景:国内大厂晚高峰常把在线与离线任务混布,用 cpu.shares 做离线压制;但在线业务需绝对兜底,此时给在线容器配 cpu.max 保底、离线容器用 cpu.shares 弹性,剖析时就能通过
throttled_usec快速量化离线任务对在线的干扰。 - 超卖比量化:在 v2 下把
quota设得远小于period(如 0.5 核),结合nr_throttled / nr_periods可算出实际可超卖比例,为成本核算提供数据,而 v1 的 cpu.shares 无法得出该指标。 - eBPF 自动调参:利用
cpu.max的精确节流事件,编写 eBPF 程序动态上调 quota,实现“无感扩容”,这是国内金融云正在落地的 “弹性配额” 方案,面试中可借此展示对内核与业务联动的深度理解。