前言
最近频繁收到服务器的告警
1 | 26.67% throttling of CPU in namespace monitoring for container node-exporter in pod node-exporter-kkmjf. |
说是节流率已经达到了26%, 但是当我上线去查看监控的时候,发现当前的pod资源使用率是非常低的,远远低于限定指标,那这个告警到底是什么回事呢?
Kubernetes的CPU资源请求和限制的实现方式
Kubernetes 使用 CFS [1]
做CPU资源的限制, 此调度器是集成在内核当中。简单的来说,CFS是通过添加了重分配周期和占用周期, 当一个进程在重分配周期内获得的CPU时间达到了占用周期,那么这个进程将不会在被调度,直到下一个重分配周期.
而CPU资源,则通过shares进行权重分配,每一个CPU权重值为1024. 如果一个节点中有4 core,而某个进程的权重为1024,那么这么进程只能使用1个CPU.
k8s使用了cgoup进行资源管理, 详细信息存储在虚拟文件系统(/sys/fs/cgroup)中。如果是CPU则为/sys/fs/cgroup/cpu,cpuacct/*

从上图我们看到,cgroup继承了4096个CPU份额, 也就是100%的CPU权重. 往下再看,可以看到有user.slice,system.slice 这两个是为系统级别应用,和用户空间的程序分配的资源。而我们也可以看到, kubepods 这个是由k8s创建的,用于分配资源给Pod使用.
细心的朋友可以看到, 上图中前两个节点分别拥有1024个份额,而kubepod使用了4096个。 明明root节点中只有4096个份额,而下面3个子节点竟然使用了(6144).
实际上改值是逻辑值, 调度器(CFS)通过该值的比例来分配配额.
比如在这种情况下,前两个cgoup分别获得680个(1024/61444096), 而kubepod获得其余的*2736**个。但是在资源空闲情况下,前两个cgoup并不会使用所有的资源,调度器有避免浪费的机制,会把空闲的资源释放到全局池中.
CPU 限制
上面我们说过了, CFS 是通过周期来进行配额的管理, 在cgroup的目录下有这两个文件, cfs_period_us和cfs_quota_us, 其中 cfs_period_us 用于定义时间段, 通常是100000us(100ms), 第二个cfs_quota_us 表示在周期中允许使用的配额.
假如说我们为节点限制200ms的配额, 在100ms的时间周期中,意味着可以使用2个时间周期, 也就是2个CPU. 如下图
而如果在100ms周期内, 使用配额超过200ms, 则就会触发限流操作.
让我们回到我们遇到的问题:
先看一下yaml中限制的配额
1
2
3
4
5
6
7resources:
limits:
cpu: 250m
memory: 180Mi
requests:
cpu: 102m
memory: 180Mi回到pod所在主机,查看cgroup的文件
1
2
3
4cat cpu.cfs_period_us
100000
cat cpu.cfs_quota_us
25000
我们可以看到, 周期时间是100ms, 配额是25ms, 也就是说这个节点可以使用1/4核的CPU资源。
1 | cat cpu.stat |
可以看到,这个容器被限流很多次已经占总周期的13%,
研究CFS的限流机制
推荐阅读这个文档: https://gist.github.com/bobrik/2030ff040fad360327a5fab7a09c4ff1
此文档详细测试了CFS在休眠间隔时间越久的情况下, 就会频繁的触发节流机制.
这是一个内核基本的bug,并且已经修复.参考这个: sched/fair: Fix bandwidth timer clock drift condition
结尾
当前bug已经在最新的内核版本修复, 并且主流的系统版本已经想代码合并
- Linux-stable: 4.14.154+, 4.19.84+, 5.3.9+
- Ubuntu: 4.15.0-67+, 5.3.0-24+
- Redhat Enterprise Linux:
- RHEL 7: 3.10.0-1062.8.1.el7+
- RHEL 8: 4.18.0-147.2.1.el8_1+
- CoreOS: v4.19.84+
解决方案:
- 升级内核
- 删除CPU的limit
- 提高limit限制