在 Linux 上,cgroups 和 Docker CPU 统计数据处理 CPU 的“时间片”,即 CPU 已使用的纳秒数。要获取百分比,请将容器 cgroup 的“已用时间”值与“可用时间”的整体系统值进行比较:/proc/stat
.
由于存储的“时间片”值是累积的,因此将当前值与先前收集的值进行比较以获得更瞬时的百分比。我认为这种比较是问题的基础。
统计数据收集
The docker stats
命令实际上在客户端中为这些信息做了很多跑腿工作。客户端查询所有容器,监视容器启动/停止的事件以及打开个人统计数据流 https://docs.docker.com/engine/api/v1.33/#operation/ContainerStats对于每个正在运行的容器。这些容器统计数据流用于计算百分比 https://github.com/docker/cli/blob/1401d5daf2f49a97791487dd5c5a8598907f0bf1/cli/command/container/stats_helpers.go#L168-L185在流中的每个统计数据转储上。
对于容器统计数据流,Docker 守护进程收集系统使用的CPU时间 https://github.com/moby/moby/blob/76531ccdeb5850156150b88c6a4a56bb8c35dc44/daemon/stats/collector.go#L75第一的。然后它使用 libcontainer 来读入容器 cgroup 文件并将文本解析为值 https://github.com/opencontainers/runc/blob/52454cf90807727923b375afd7d84ac8fd523959/libcontainer/cgroups/fs/cpuacct.go#L72。这里有所有统计数据结构 https://github.com/opencontainers/runc/blob/7139b61f7fdb904d0acb8db825709aa8d2d2ef36/libcontainer/cgroups/stats.go。然后将其作为 JSON 响应发送到客户端进行处理。
我相信至少部分问题源于阅读和解析/proc/stat
不同时间的系统信息和容器 cgroup 统计信息。每当读取容器信息的 goroutine 延迟一点时,与系统相比,该样本中会包含更多的纳秒。由于收集过程计划每 X 秒运行一次,因此下一次读取将包含更少的总纳秒,因此这些值可以在繁忙的系统上反弹,然后回落相同的量,因为第二次读取中没有包含完整的“刻度”样本。
运行的容器越多,系统就越繁忙,这个问题就会变得更加复杂。统计数据收集和转发给客户端似乎是一个相对重量级的过程,只是docker stats
大量容器足以导致更多的不准确性。我最好的猜测是所有试图读取统计数据的 goroutine 中存在争用。我不确定这是否能解释 Docker 显示的不准确程度。我要么完全错了,要么还有其他问题加剧了这个问题。
Linux cgroup
每个 Docker 容器的使用情况都在cgroup https://en.wikipedia.org/wiki/Cgroups。 CPU记账信息可以通过cgroup文件系统查看:
→ find /sys/fs/cgroup/cpuacct/docker -type d
/sys/fs/cgroup/cpuacct/docker
/sys/fs/cgroup/cpuacct/docker/f0478406663bb57d597d4a63a031fc2e841de279a6f02d206b27eb481913c0ec
/sys/fs/cgroup/cpuacct/docker/5ac4753f955acbdf38beccbcc273f954489b2a00049617fdb0f9da6865707717
/sys/fs/cgroup/cpuacct/docker/a4e00d69819a15602cbfb4f86028a4175e16415ab9e2e9a9989fafa35bdb2edf
/sys/fs/cgroup/cpuacct/docker/af00983b1432d9ffa6de248cf154a1f1b88e6b9bbebb7da2485d94a38f9e7e15
→ cd /sys/fs/cgroup/cpuacct/docker/f0478406663bb57d597d4a63a031fc2e841de279a6f02d206b27eb481913c0ec
→ ls -l
total 0
-rw-r--r-- 1 root root 0 Nov 20 22:31 cgroup.clone_children
-rw-r--r-- 1 root root 0 Nov 20 04:35 cgroup.procs
-r--r--r-- 1 root root 0 Nov 20 21:51 cpuacct.stat
-rw-r--r-- 1 root root 0 Nov 20 21:51 cpuacct.usage
-r--r--r-- 1 root root 0 Nov 20 22:31 cpuacct.usage_all
-r--r--r-- 1 root root 0 Nov 20 21:51 cpuacct.usage_percpu
-r--r--r-- 1 root root 0 Nov 20 22:31 cpuacct.usage_percpu_sys
-r--r--r-- 1 root root 0 Nov 20 22:31 cpuacct.usage_percpu_user
-r--r--r-- 1 root root 0 Nov 20 22:31 cpuacct.usage_sys
-r--r--r-- 1 root root 0 Nov 20 22:31 cpuacct.usage_user
-rw-r--r-- 1 root root 0 Nov 20 22:31 notify_on_release
-rw-r--r-- 1 root root 0 Nov 20 22:31 tasks
→ cat cpuacct.usage_percpu
3625488147 6265485043 6504277830
每个值都是该 CPU 上的累积使用量(以纳秒为单位)。
→ grep -w ^cpu /proc/stat
cpu 475761 0 10945 582794 2772 0 159 0 0 0
这里的值是USER_HZ http://man7.org/linux/man-pages/man5/proc.5.html== 1/100 秒,因此在 Docker 中进行一些转换。