rabbit-obsidian/Clippings/k8s CPU limit和throttling的迷思.md

8.0 KiB
Raw Permalink Blame History

title source author published created description tags
k8s CPU limit和throttling的迷思 https://nanmu.me/zh-cn/posts/2021/myth-of-k8s-cpu-limit-and-throttle/
nanmu42
2021-11-14 2024-12-16 你应当小心设定k8s中负载的CPU limit太小的值会给你的程序带来额外的、无意义的延迟太大的值会带来过大的爆炸半径削弱集群的整体稳定性。
clippings

你应当小心设定k8s中负载的CPU limit太小的值会给你的程序带来额外的、无意义的延迟太大的值会带来过大的爆炸半径削弱集群的整体稳定性。

request和limit🔗

k8s的一大好处就是资源隔离通过设定负载的request和limit我们可以方便地让不同程序共存于合适的节点上。

其中request是给调度看的调度会确保节点上所有负载的CPU request合计与内存request合计分别都不大于节点本身能够提供的CPU和内存limit是给节点kubelet看的节点会保证负载在节点上只使用这么多CPU和内存。例如下面配置意味着单个负载会调度到一个剩余CPU request大于0.1核剩余request内存大于200MB的节点并且负载运行时的CPU使用率不能高于0.4核超过将被限流内存使用不多余300MB超过将被OOM Kill并重启

yaml resources: requests: memory: 200Mi cpu: "0.1" limits: memory: 300Mi cpu: "0.4"

CPU的利用率🔗

CPU和内存不一样它是量子化的只有“使用中”和“空闲”两个状态。

!520cd82b1a232f3c698c665d1d67c08b_MD5.jpg

我和老婆聊了聊CPU和内存的不同她帮我画了一张插图 图/我的妻子

当我们说内存的使用率是60%时我们是在说内存有60%在空间上已被使用还有40%的空间可以放入负载。但是当我们说CPU的某个核的使用率是60%时我们是在说采样时间段内CPU的这个核在时间上有60%的时间在忙40%的时间在睡大觉。

你设定负载的CPU limit时这个时空区别可能会带来一个让你意想不到的效果——过分的降速限流 节点CPU明明不忙但是节点故意不让你的负载全速使用CPU服务延时上升。

CPU限流🔗

k8s使用CFSCompletely Fair Scheduler完全公平调度限制负载的CPU使用率CFS本身的机制比较复杂但是k8s的文档中给了一个简明的解释,要点如下:

  • CPU使用量的计量周期为100ms
  • CPU limit决定每计量周期100ms内容器可以使用的CPU时间的上限
  • 本周期内若容器的CPU时间用量达到上限CPU限流开始容器只能在下个周期继续执行
  • 1 CPU = 100ms CPU时间每计量周期以此类推0.2 CPU = 20ms CPU时间每计量周期2.5 CPU = 250ms CPU时间每计量周期
  • 如果程序用了多个核CPU时间会累加统计。

举个例子假设一个API服务在响应请求时需要使用A, B两个线程2个核分别使用60ms和80ms其中B线程晚触发20ms我们看到API服务在100ms后可给出响应

!4b784792e070edb8552c4919d6757b51_MD5.png

没有CPU限制的情况响应时间为100ms

如果CPU limit被设为0.8核即每100ms内最多使用80ms CPU时间API服务的线程B会受到一次限流灰色部分服务在140ms后响应

!01686fa147d1df509c57e23c678cc1ee_MD5.png

CPU limit = 0.8响应时间为140ms

如果CPU limit被设为0.6核即每100ms内最多使用60ms CPU时间API服务的线程A会受到一次限流灰色部分线程B受到两次限流服务在220ms后响应

!d9a772e8487ccb6d4e68f2b7fe8b21a3_MD5.png

CPU limit = 0.6响应时间为220ms

注意,即使此时CPU没有其他的工作要做限流一样会执行,这是个死板不通融的机制。

这是一个比较夸张的例子一般的API服务是IO密集型的CPU时间使用量没那么大你在跑模型推理当我没说但还是可以看到限流会实打实地延伸API服务的延时。因此对于延时敏感的服务我们都应该尽量避免触发k8s的限流机制。

下面这张图是我工作中一个API服务在pod级别的CPU使用率和CPU限流比率CPU Throttling我们看到CPU限流的情况在一天内的大部分时候都存在限流比例在10%上下浮动这意味着服务的工作没能全速完成在速度上打了9折。值得一提这时pod所在节点仍然有富余的CPU资源节点的整体CPU使用率没有超过50%.

!30fa0ec26223ea554da9e5e6707cf988_MD5.png

一个实际的降速限流的例子服务的处理速度被kubelet降低了10%

你可能注意到监控图表里的CPU使用率看上去没有达到CPU limit橙色横线这是由于CPU使用率的统计周期1min太长造成的信号混叠Aliasing如果它的统计统计周期和CFS的一样100ms我们就能看到高过CPU limit的尖刺了。这不是bug这是feature

不过,内核版本低于4.18的Linux还真有个bug会造成不必要的CPU限流。┑( ̄Д  ̄)┍

避免CPU限流🔗

有的开发者倾向于完全弃用CPU limit,裸奔直接跑,特别是内核版本不够有bug的时候

我认为这么做还是太过放飞自我了如果程序里有耗尽CPU的bug例如死循环我不幸地遇到过整个节点及其负载都会陷入不可用的状态爆炸半径太大特别是在大号的节点上16核及以上

我有两个建议:

  1. 监控一段时间应用的CPU利用率基于利用率设定一个合适的CPU limit例如日常利用率的95分位 * 10同时该limit不要占到节点CPU核数的太大比例例如2/3这样可以达到性能和安全的一个平衡。
  2. 使用automaxprocs一类的工具让程序适配CFS调度环境各个语言应该都有类似的库或者执行参数根据CFS的特点调整后程序更不容易遇到CPU限流

结语🔗

上面说到的信号混叠采样频率不足和Linux内核bug让我困扰了一年多现在想想主要还是望文生义惹的祸文档还是应该好好读基础概念还是要搞清遂记此文章于错而知新

题外话性能和资源利用率有时是相互矛盾的。对于延时不敏感的程序CPU限流率控制在10%以内应该都是比较健康可接受的,量体裁衣,在线离线负载混合部署,可以提升硬件的资源利用率。 有消息说腾讯云研发投产了基于服务优先级的抢占式调度,这是一条更难但更有效的路,希望有朝一日在上游能看到他们的相关贡献。

致谢🔗

感谢腾讯云的客服和TKE技术人员的多次协查和答疑。

更新记录🔗

  • 2022/01修正了错误的图注感谢知乎网友 Pnmlgb 和 童话音乐 的指正。

参考资料🔗