如何正确获取容器的CPU利用率?

大家好,我是飞哥!
在线上服务器观察线上服务运行状态的时候,绝大多数人都是喜欢先用 top 命令看看当前系统的整体 cpu 利用率 。例如,随手拿来的一台机器,top 命令显示的利用率信息如下

如何正确获取容器的CPU利用率?

文章插图
这个输出结果说简单也简单,说复杂也不是那么容易就能全部搞明白的 。例如:
问题 1:top 输出的利用率信息是如何计算出来的,它精确吗?问题 2:ni 这一列是 nice,它输出的是 cpu 在处理啥时的开销?问题 3:wa 代表的是 io wait,那么这段时间中 cpu 到底是忙碌还是空闲?
今天我们对 cpu 利用率统计进行深入的学习 。通过今天的学习,你不但能了解 cpu 利用率统计实现细节,还能 nice、io wait 等指标有更深入的理解 。
区别于以往的文章,今天我们不直接进入 linux 实现,而是先从自己的思考开始!
一、先思考一下抛开 Linux 的实现先不谈,如果有如下需求,有一个四核服务器,上面跑了四个进程 。
如何正确获取容器的CPU利用率?

文章插图
让你来设计计算整个系统 cpu 利用率的这个需求,支持像 top 命令这样的输出,满足以下要求:
  • cpu 使用率要尽可能地准确
  • 要能地体现秒级瞬时 cpu 状态
可以先停下来阅读思考几分钟 。
如何正确获取容器的CPU利用率?

文章插图
好,思考结束 。经过思考你会发现,这个看起来很简单的需求,实际还是有点小复杂的 。
其中一个思路是把所有进程的执行时间都加起来,然后再除以系统执行总时间*4 。
如何正确获取容器的CPU利用率?

文章插图
这个思路是没问题的,用这种方法统计很长一段时间内的 cpu 利用率是可以的,统计也足够的准确 。
但只要用过 top 你就知道 top 输出的 cpu 利用率并不是长时间不变的,而是默认 3 秒为单位会动态更新一下(这个时间间隔可以使用 -d 设置) 。我们的这个方案体现总利用率可以,体现这种瞬时的状态就难办了 。你可能会想到那我也 3 秒算一次不就行了?但这个 3 秒的时间从哪个点开始呢 。粒度很不好控制 。
上一个思路问题核心就是如何解决瞬时问题 。提到瞬时状态,你可能就又来思路了 。那我就用瞬时采样去看,看看当前有几个核在忙 。四个核中如果有两个核在忙,那利用率就是 50% 。
这个思路思考的方向也是正确的,但是问题有两个:
  • 你算出的数字都是 25% 的整数倍
  • 这个瞬时值会导致 cpu 使用率显示的剧烈震荡 。
比如下图:
如何正确获取容器的CPU利用率?

文章插图
在 t1 的瞬时状态看来,系统的 cpu 利用率毫无疑问就是 100%,但在 t2 时间看来,使用率又变成 0% 了 。思路方向是对的,但显然这种粗暴的计算无法像 top 命令一样优雅地工作 。
我们再改进一下它,把上面两个思路结合起来,可能就能解决我们的问题了 。在采样上,我们把周期定的细一些,但在计算上我们把周期定的粗一些 。
我们引入采用周期的概念,定时比如每 1 毫秒采样一次 。如果采样的瞬时,cpu 在运行,就将这 1 ms 记录为使用 。这时会得出一个瞬时的 cpu 使用率,把它都存起来 。
如何正确获取容器的CPU利用率?

文章插图
在统计 3 秒内的 cpu 使用率的时候,比如上图中的 t1 和 t2 这段时间范围 。那就把这段时间内的所有瞬时值全加一下,取个平均值 。这样就能解决上面的问题了,统计相对准确,避免了瞬时值剧烈震荡且粒度过粗(只能以 25 %为单位变化)的问题了 。
可能有同学会问了,假如 cpu 在两次采样中间发生变化了呢,如下图这种情况 。
 
如何正确获取容器的CPU利用率?

文章插图
 
在当前采样点到来的时候,进程 A 其实刚执行完,有一点点时间没有既没被上一个采样点统计到,本次也统计不到 。对于进程 B,其实只开始了一小段时间,把 1 ms 全记上似乎有点多记了 。
确实会存在这个问题,但因为我们的采样是 1 ms 一次,而我们实际查看使用的时候最少也有是秒级别地用,会包括有成千上万个采样点的信息,所以这种误差并不会影响我们对全局的把握 。


推荐阅读