获取 Docker container 中的资源使用情况(转)

Posted ajianbeyourself

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了获取 Docker container 中的资源使用情况(转)相关的知识,希望对你有一定的参考价值。

我在使用docker时也发现了这个问题,看来在docker内执行top/free命令的确有问题

原文:https://zhuanlan.zhihu.com/p/35914450

作者:两片

Docker 是一种对程序环境进行封装并实现资源的隔离技术,可秒级启动,可瞬间启动千千万万个实例,这些优点让它在出现之初就受到极大的关注,其火爆程度就如今天的深度学习一般。人们拿到了锤子就到处找钉子,什么都想套上 Docker,连生产数据库都想用 Docker 来运行,少不了碰到很多钉子,磕磕碰碰才形成今天这个比较合理的各个使用场景。

 

在一个完善的系统中,我们总需要知道自己的服务使用了多少CPU,内存资源,想知道磁盘读写多少,网络流量如何。如果在 Docker 容器中执行 top,free 等命令会发现我们能看到CPU所有核的使用情况以及宿主机的内存占用,这并不是我们需要的,我们需要的是这个容器被限制了多少 CPU,内存,当前容器内的进程使用了多少。

 

要明白为何 top,free 显示的是宿主机的情况,以及如何获得容器的资源限制需要先理解 Docker 的两项基础技术:Namespace 和 cgroup。两者在 CoolShell 的博客里都已经讲解得非常清楚,这里只简单说明一下。Namespace 解决了环境隔离的问题,它将进程的PID,Network,IPC等和其它进程隔离开来,结合 chroot,让进程仿佛运行在一个独占的操作系统中。cgroup 则对进程能够使用的资源作限制,如在一台48核256G的机器上只让容器使用2核2G。

 

容器中CPU,内存,磁盘资源都是被 cgroup 限制和统计的,所有信息都放在 /sys/fs/cgroup 这个虚拟文件夹里,在容器里运行 mount 命令可以看到这些挂载记录

...
cgroup on /sys/fs/cgroup/cpuset type cgroup (ro,nosuid,nodev,noexec,relatime,cpuset)
cgroup on /sys/fs/cgroup/cpu type cgroup (ro,nosuid,nodev,noexec,relatime,cpu)
cgroup on /sys/fs/cgroup/cpuacct type cgroup (ro,nosuid,nodev,noexec,relatime,cpuacct)
cgroup on /sys/fs/cgroup/memory type cgroup (ro,nosuid,nodev,noexec,relatime,memory)
cgroup on /sys/fs/cgroup/devices type cgroup (ro,nosuid,nodev,noexec,relatime,devices)
cgroup on /sys/fs/cgroup/freezer type cgroup (ro,nosuid,nodev,noexec,relatime,freezer)
cgroup on /sys/fs/cgroup/blkio type cgroup (ro,nosuid,nodev,noexec,relatime,blkio)
cgroup on /sys/fs/cgroup/perf_event type cgroup (ro,nosuid,nodev,noexec,relatime,perf_event)
cgroup on /sys/fs/cgroup/hugetlb type cgroup (ro,nosuid,nodev,noexec,relatime,hugetlb)
...

CPU

获取CPU信息需要用到 cpuset, cpu, cpuacct

  • cpuset 提供一个机制指定 cgroup 可以使用哪些CPU核哪些内存节点(an on-line node that contains memory)
  • cpu 设置CPU的使用配额/份额
  • cpuacct 提供 cgroup CPU 使用时长的统计信息

把上面三个文件夹的文件都看了一遍后,会发现 cgroup 里的提供的信息比物理机上的资源使用状况信息少很多,这其实是非常合理的,因为两者本质上就是不同的东西。比如物理机只有10%的时间在运行任务,那剩下90%时间物理机是真正称得上空闲,而 cgroup 限制组内进程最多只能用50%的CPU,而组内进程只用了10%的CPU,但不能说还有40%或者80%是空闲的,在 cgroup 内根本没有空闲这个概念,因此也就没法从中得到常见的 idle 指标。

另外,cgroup 内没有统计 iowait,中断,这些也没法拿到。不过没关系,细化的指标可以在进程里找,系统信息足够反映负载状况即可。这里介绍一下我比较关心的3个指标的获取方式。

LimitedCores: cgroup 限制可以使用的 CPU 核数

cgroup 有三种方式限制 CPU 的使用

  1. share,对应文件 cpu/cpu.shares,是系统内多个 cgroup 的进程同时运行时他们的CPU使用上限占比,比如只有两个cgroup: [cgroup1.shares: 1024, cgroup2.shares: 512],那 cgroup1 可以用 2/3 的 CPU。
  2. quota,对应文件 cpu/cpu.cfs_quota_uscpu/cpu.cfs_period_us,表示在每个 period(时间间隔)内 cgroup 可以使用的 CPU 时间。文件里数字的单位是微秒,假设 {quota: 200000, period: 100000},意思是每 100ms 可以使用 200ms CPU 时间,相当于可以使用两个核。quota 为 -1 的时候表示无限制。
  3. cpuset,对应文件 cpuset/cpuset.cpus, 表示 cgroup 可以用那几个 CPU,比如 0,3-7,12 可以使用 7 个核。

对于 share,我们没法知道有多少邻居以及邻居的值为多少,所以忽略这个限制。考虑到 cpuset 在生产环境极少使用,同时用 quota 和 cpuset 的就更少了,所以我们的策略是:先看 quota,如果 quota 有限制则返回,否则再看 cpuset。

Usage:cgroup 的 CPU 占用率,占了物理机的多少CPU

上面我们拿到了能用多少个核,自然知道 cgroup 的占用上限,只要知道 cgroup 用了物理机的多少 CPU 就可以知道饱和程度了。要获取这个首先需要知道一段时间内物理机用了多少CPU时间,然后获得 cgroup 用了多少CPU时间,最后相除。

  1. 物理机使用的CPU时间从 /proc/stat 里获取,将里面 cpu 那一行的数字相加并且乘以物理机CPU核数即可得到从开机到现在用的CPU总时间。可以设置一秒的时间间隔,求差即可得到这一秒内用的CPU时间。
  2. cgroup 使用的CPU时间可以从 cpuacct/cpuacct.usage 中获得,也是求一段时间的差即可。

特别需要注意的是 /proc/stat 里单位是纳秒,而 cpuacct.usage 里的是 Clock Tick,一般是 100纳秒/tick,准确数字可以通过 getconf CLK_TCK 命令获得。

Throttled:cgroup 的 CPU 使用被限制的次数

如果被频繁限制的话,说明很可能分配的CPU不够用了。可以从 cpu/cpu.stat 的字段获得。

内存

cgroup 对内存的使用做了比较多的统计,但是内存使用率本身就是一个糊涂账,因为很多内存是为了提高性能从磁盘映射过来的需要时可以清理掉。cgroup 里的内存信息和定义推荐看 Kernal Doc cgroup-v1 memory

需要额外提一下的是 cache 和 mapped_file,cache 是 page cache memory 也成为 disk cache 是磁盘映射到内存的内存,而 mapped_file 也是一样意思,但实际中常常会发现 cache 比 mapped_file 大很多,这其实是 mapped_file 是还被进程引用着的,而 cache 则包括曾经被进程用过但现在已经没有任何进程使用的映射,也就是 unmapped_file。

对于内存,可以重点关注以下几个指标(没有特别注明,所有指标都从 memory/memory.stat 中取:

  1. Total: cgroup 被限制可以使用多少内存,可以从文件里的 hierarchical_memory_limit 获得,但不是所有 cgroup 都限制内存,没有限制的话会获得 2^64-1 这样的值,我们还需要从 /proc/meminfo 中获得 MemTotal,取两者最小。
  2. RSS: Resident Set Size 实际物理内存使用量,在 memory/memory.stat 的 rss 只是 anonymous and swap cache memory,文档里也说了如果要获得真正的 RSS 还需要加上 mapped_file。
  3. Cached: memory/memory.stat 中的 cache
  4. MappedFile: memory/memory.stat 中的 mapped_file
  5. SwapTotal: 限制的 swap 大小,(hierarchical_memsw_limit - hierarchical_memory_limit) 同样会遇到内存没有限制的情况。
  6. SwapUsed: memory/memory.stat 中的 total_swap

磁盘

cgroup 通过 blkio 子系统实现对磁盘读写控制。当前有两种限制磁盘的策略,CFQ(就是各个cgroup平分磁盘使用时间)和 Throttling。

如果是 CFQ,可从 blkio/blkio.io_service_bytes_recursive 获得各个磁盘的 IO 统计

如果是 Throttling,可从 blkio/blkio.throttle.io_service_bytes 获得各个磁盘的 IO 统计

文件里会将 IO 分为 Read/Write, Sync/Async,将所有磁盘的 Read/Write相加即可得到磁盘读写量。

网络

网络统计信息放在 /sys/class/net/<ethX>/statistics 中,在容器中只能看到自己用到的网络接口,但网络接口的名字常常不确定,可以先通过命令 ip -o -4 route show to default | awk ‘{print $5}‘ 获得默认网络接口的名字。

然后就可以从 rx_bytes 获得接收字节数,从 tx_bytes 获得发出字节数了。

至此,比较重要的信息都已经拿到,有时还需要提防一下宿主机超卖的情况,有时出了问题并不是自己容器用资源太多,而是资源都被同主机的其它主机占用了,这时可以从 /proc 里拿额外的信息来判断。

package

发现目前没有专门针对 container 的指标获取,于是写了个 Go 的,放在 github 上,地址:container-metrics

参考链接

    1. CoolShell Docker
    2. Kernal Doc cgroup-v1
    3. Docker runmetrics
    4. docker stats命令源码分析结果
    5. Linux Cgroup系列(05):限制cgroup的CPU使用

以上是关于获取 Docker container 中的资源使用情况(转)的主要内容,如果未能解决你的问题,请参考以下文章

docker 的container模式

014-docker-终端获取 docker 容器(container)的 ip 地址

Docker swarm 获取service的container信息

原创 | Docker入门基础系列之详谈Docker的容器Container

docker资源限制和应用总结

如何获取 docker 容器(container)的 ip 地址(转)