容器性能调试: CPU Bottleneck

最近遇到一个有趣的问题:假如一个 container CPU usage 达到了 CPU limit(比方说都 50%),该怎么办?

一个简单的思路就是把 CPU cap 调高一点。而 CPU cap 又由 CFS quota 来实现。我们知道,如果 CPU 不是 throttled 在 cgroup cap,增大 cpu.cfs_quota_us 并不会给予 container 更多的 CPU 资源。

在没有了解问题的根源之前,做任何决定是不妥的。Debug 中一个常用的方法,就是首先猜测可能结果,然后反推中间过程,再去追寻根源迹象。

对于开头问题,有以下几种可能:

  1. Throttled by CPU cap.
  2. Throttled by cpu.share/cpuset.
  3. Throttled by physical CPU resources.

Throttled by CPU cap

这种情况下是 cgroup 做 throttling。我们需要在 cgroup 里面读 cpu.stat 的 throttled_time 就可知 cgroup 是否做了 throttling。

目前 docker 并没有 expose 这个 metrics 出来。只能在 cgroup 里面找。

首先找出 cgroup cpu mount:

# cat /proc/mounts | grep cgroup
cgroup /sys/fs/cgroup/cpu,cpuacct cgroup ...

然后找出 container pid:

# docker inspect $container_id | grep -i pid
       "Pid": 2163

然后去找到对应的 cgroup dir (note: /kubepods 是一个 known convention. 不加也行):

# find /sys/fs/cgroup/cpu,cpuacct/kubepods -type f -name "tasks" | xargs grep "$pid"
/sys/fs/cgroup/cpu,cpuacct/kubepods/burstable/podc0798a90-e905-11e7-b5b4-42010a8000b1/7bd54b4c7c57c54f8696e9819f01a50c4eda7012d282ad319831544d60c5fb48/tasks:2163

然后在上面找到的 dir 里面读取 cpu.stat:

# cat /sys/fs/cgroup/...c5fb48/cpu.stat
nr_periods 0
nr_throttled 0
throttled_time 10000

如果观察到 throttled_time 在不断增加,就说明 throttled by cgroup cap。

Throttled by cpu.share/cpuset or physical CPU

这些情况下都是 CFS scheduler 把 task 给 context switch 了。

我们可以通过上一节找的 pid 查 process status:

# cat /proc/2163/status
nonvoluntary_ctxt_switches: 703

如果观察到 nonvoluntary_ctxt_switches 不断增加,就说明 task 被不断地 context switch。但是我们还不能确定 CPU 是否吃满了。因为如果有其他 cgroup tasks 存在并且他们都闲着,这时候 CPU 就没有被吃满。

这时候我们能做的就是检查其他 tasks 的 cpu usage。一般就 docker stat 或 top 检查下。如果其他 tasks cpu usage 都很高,说明 throttled by physical CPU;可以考虑把容器迁移到其他空闲机器上去。如果其他 tasks 有 idle 的,说明有空闲资源,可以调整调整相关配置,把 cpu resource 应用得更饱满。

总结

我常常听人教诲:遇到一个问题,先要仔细思考,切忌妄下定论。上面这个例子就是最好的佐证。通过深入学习,还可以发掘更多的知识,比如上面检查 CPU 是否被吃满还可以用来做调度优化的基础。

你可能感兴趣的:(容器性能调试: CPU Bottleneck)