为什么容器的 CPU 用量明明没到上限,延迟却变高了?因为 cgroup 的配额是按周期算的,一个周期内用光配额就会被暂停到下一个周期。这篇用 cpu.stat 的 nr_throttled 和 throttled_usec,配合任务延迟,实测节流的表现。
不限制:p50=30.0 p95=30.0 max=30.7 nr_periods=0 nr_throttled=0 throttled_ms=0 --cpus 0.5(配额 50 ms / 周期 100 ms):p50=71.7 p95=77.2 max=79.2 nr_periods=60 nr_throttled=60 throttled_ms=2622 --cpus 0.5,周期改成 10 ms(配额 5 ms / 周期 10 ms):p50=56.3 p95=59.4 max=60.4 nr_periods=601 nr_throttled=576 throttled_ms=2625
--cpus 0.5 是每 100 ms 周期最多用 50 ms CPU。30 ms 的任务第一个周期做完 30 ms 后,第二个任务在同一周期里只能再做 20 ms,剩下的要等到下一个周期;所以单次延迟 p50 到了 71.7 ms,100 次累计被节流 2622 ms。cpu.stat 里的 nr_throttled 和 throttled_usec 就是这个记录;没有设置限制时这两项是 0。
把周期改成 10 ms、配额 5 ms,同样是 0.5 个 CPU,总的被节流时间几乎一样(2625 ms),但单次延迟更短(p50 56.3 ms),因为每次等的时间更短。我没有测周期过小带来的调度开销。
--cpus 0.5,5 ms 的任务:p50=5.0 p95=17.4 max=24.8 nr_periods=10 nr_throttled=10 throttled_ms=125
5 ms 的任务大多不被节流(p50 5.0 ms),但偶尔撞上周期边界,p95 到 17.4 ms,max 24.8 ms。平均 CPU 利用率不高,不代表没有延迟毛刺。
不限制:10 次的墙钟时间 最快=44 中位=47 最慢=67 ms nr_throttled=0 throttled_ms=0 --cpus 1:10 次的墙钟时间 最快=47 中位=64 最慢=188 ms nr_throttled=10 throttled_ms=1071 --cpus 4:10 次的墙钟时间 最快=43 中位=48 最慢=68 ms nr_throttled=0 throttled_ms=0
4 个进程同时跑,一个周期内能把配额瞬间用光:--cpus 1 时 10 次全被节流,中位 64 ms、最慢 188 ms;配额给到 4 就没有节流,和不限制一样。多线程的服务设置过小的 --cpus,代价是尾部延迟。