背景

当一台机器上跑有多个 Docker Container 的时候,我们需要知道,哪些容器占用了多少资源。采集这些指标,来让我们可以更加好的分配资源给每个 Container。

获取容器CPU使用率

正好,这两天碰到一个需求。需要我们在监控 Host 的 CPU 时候,同时将每个容器的 CPU使用率 也要拿到。

平常我们来看容器的资源占用,通过 stat 命令就可以

这里的字段 CPU 表示了CPU的使用率。

但是这样的命令结果我们要拿到并做处理的时候并不方便。不过,据说 Docker Daemon 还有 API 接口,可以用来直接获取JSON格式的数据。

来来来,接口搞起来……
当我拿到数据的时候,我是拒绝的:

"cpu_stats" : {
"cpu_usage" : {
"percpu_usage" : [
8646879,
24472255,
36438778,
30657443
],
"usage_in_usermode" : 50000000,
"total_usage" : 100215355,
"usage_in_kernelmode" : 30000000
},
"system_cpu_usage" : 739306590000000,
"throttling_data" : {"periods":0,"throttled_periods":0,"throttled_time":0}
}

这都是些什么鬼……

其实,要理解上面的数据,要现有一些前序的知识

Linux 如何计算CPU使用率

在 Linux 中,CPU 可不是像一个有刻度的尺子一样,把百分比刻在上面,CPU使用了多少,就把刻度记录一下。

实际上,CPU使用率的计算是通过时间比率计算的。简单来说,CPU只会有两种状态: 忙(被占用)、闲(空闲)。当我们把1s的时间分成1000份,也就是以毫秒为单位来看待CPU的时候。

假设下面两种情况:

  1. 假设有一个进程占用了 CPU 250ms;那么整个CPU在1s的维度上来看,他的使用率是25%。
  2. 假设有两个进程一起使用CPU,每个都是用了 500ms;那么整个CPU使用率是 100%,每个进程占用了 50%


实际上,Linux对于CPU的使用分配和记录远比这个要复杂。比如,还要记录是系统调用的CPU占用呢 还是用户的CPU占用呢,还是等待IO的占用呢,等等。CPU时间的分配工作也是非常复杂,Linux中采用了完全公平调度器(Completely Fair Scheduler)来分配CPU。

继续深入就有点跑题了。这些东西会在今年后续的文章中陆续谈到。

那么,实际上, Linux Kernel 将 CPU 使用的信息记录在哪了呢?这个信息记录在了 /proc/stat 这个文件中。

它看起来就像下面这样:

# intr 那个字段太长,截断掉了
[root@sean ~]# cat /proc/stat
cpu 1344357 729 298522 107841813 41774 0 1754 0 0 0
cpu0 1344357 729 298522 107841813 41774 0 1754 0 0 0
intr 206534153 29 10 0 0 154 .......
ctxt 317949799
btime 1524823711
processes 677986
procs_running 3
procs_blocked 0
softirq 66031632 2 35326999 1 2928073 535817 0 32 0 0 27240708

我们关心的是第一行的意义。通过查看 man 5 proc,可以确定它的具体意义

user   (1) 用户模式花费的CPU时间
nice (2) 低优先级用户态花费的CPU时间
system (3) 系统模式花费的CPU时间
idle (4) 空闲的CPU时间
iowait (since Linux 2.5.41)
(5) 等待IO的CPU时间
irq (since Linux 2.6.0-test4)
(6) 中断花费的CPU时间
softirq (since Linux 2.6.0-test4)
(7) 软中断花费的时间
steal (since Linux 2.6.11)
(8) 被虚拟环境其他操作系统占用的CPU时间
guest (since Linux 2.6.24)
(9) 在Linux内核的控制下为客户操作系统运行一个虚拟CPU花费的时间。
guest_nice (since Linux 2.6.33)
(10) 同上,低优先级的。

这些加起来,就是花费的CPU总体时间。

Container 如何记录CPU使用

这个文件中,记录了所有的进程花费的总的开销,并不能标识出某一个 Container 总共花费了多少。那如何找出 Container 的开销呢?

这个就要看 CGroup 的了。

Linux下的Container技术依赖于 CGroup(Control Group),它不仅仅追踪进程组,还会暴露 CPU、Memory、IO 使用率的指标出来。CGroup 通过伪文件系统的方式将它们记录下来。在最近几个版本的发行版中,一般会放在 /sys/fs/cgroup 中。

在这个文件夹下,可以看到多个子文件夹,代表了 cgroup 记录的各个分类

[root@test cgroup]# pwd
/sys/fs/cgroup
[root@test cgroup]# ll
total 0
dr-xr-xr-x 3 root root 0 May 10 12:59 blkio
lrwxrwxrwx 1 root root 11 Jan 29 12:07 cpu -> cpu,cpuacct
lrwxrwxrwx 1 root root 11 Jan 29 12:07 cpuacct -> cpu,cpuacct
dr-xr-xr-x 3 root root 0 May 10 12:59 cpu,cpuacct
dr-xr-xr-x 3 root root 0 May 10 12:59 cpuset
dr-xr-xr-x 3 root root 0 May 10 12:59 devices
dr-xr-xr-x 3 root root 0 May 10 12:59 freezer
dr-xr-xr-x 3 root root 0 May 10 12:59 hugetlb
dr-xr-xr-x 3 root root 0 May 10 12:59 memory
dr-xr-xr-x 3 root root 0 May 10 12:59 net_cls
dr-xr-xr-x 3 root root 0 May 10 12:59 perf_event
dr-xr-xr-x 3 root root 0 May 10 12:59 pids
dr-xr-xr-x 5 root root 0 May 10 12:59 systemd

对于 Docker 来说,在每一个分类下,都会有一个 docker 的文件夹,来记录docker相关的信息。而docker container 的信息,则记录在 更下一层的目录中,以 container 的 全ID 为目录名。以记录container CPU相关的信息为例,目录应该在 /sys/fs/cgroup/cpu/docker/<container-full-id>

对于 CPU 来说,Linux 使用了 The CPU Accounting (cpuacct) 子系统。它会定期自动汇报有关 CGroup中 任务使用 CPU 资源的使用情况。

cpuacct 有三种汇报记录:

  • cpuacct.stat Group中 CPU 总共的使用时间(纳秒为单位),包含所有类型的CPU消耗
  • cpuacct.usage Group中 用户模式 和 系统模式 各自使用的 CPU时间。这个汇报的单位是 1/100 秒,在 Linux 中也叫"user jiffies"。文件中有两个字段
    • user 用户使用的 CPU时间
    • system 系统使用的 CPU 时间
  • cpuacct.usage_percpu 每个 CPU 的使用时间
total 0
-rw-r--r-- 1 root root 0 May 9 18:22 cgroup.procs
-r--r--r-- 1 root root 0 May 10 13:07 cpuacct.stat
-rw-r--r-- 1 root root 0 May 10 13:07 cpuacct.usage

这就是,到现在为止,我们需要的所有前置知识啦。下面我们回到主题。

/container/<cid>/stats API 的CPU数据如何来的?

这些数据就是我们上面两个小节所讲的地方拿来的。

"cpu_stats" : {
"cpu_usage" : {
"percpu_usage" : [
8646879,
24472255,
36438778,
30657443
],
"usage_in_usermode" : 50000000,
"total_usage" : 100215355,
"usage_in_kernelmode" : 30000000
},
"system_cpu_usage" : 739306590000000,
"throttling_data" : {"periods":0,"throttled_periods":0,"throttled_time":0}
}

cpu_usage 中的所有的值 是从 cpuacct.usagecpuacct.usage_percpucpuacct.stat 获取。
system_cpu_usage 从 /proc/stat 中获取。

⚠️ 这里需要注意的是,这三个文件中,cpuacct.stat 使用的单位和 其他两个是不一样的,在获取数据的代码中,要将以 USER_HZ 单位的值转为 以纳秒为单位的值。

来看看代码

有了数据,怎么计算 Container CPU 使用率

代码在此

我把它贴在下面,分析一下:

func calculateCPUPercentUnix(previousCPU, previousSystem uint64, v *types.StatsJSON) float64 {
var (
cpuPercent = 0.0
// calculate the change for the cpu usage of the container in between readings
cpuDelta = float64(v.CPUStats.CPUUsage.TotalUsage) - float64(previousCPU)
// calculate the change for the entire system between readings
systemDelta = float64(v.CPUStats.SystemUsage) - float64(previousSystem)
) if systemDelta > 0.0 && cpuDelta > 0.0 {
cpuPercent = (cpuDelta / systemDelta) * float64(len(v.CPUStats.CPUUsage.PercpuUsage)) * 100.0
}
return cpuPercent
}

有同学说,有了系统总时间,有了Container总时间,做一个简单的除法运算就可以完成比例的运算过程:

Container CPU使用率 = container CPU使用总时间 / 系统CPU的总时间 * CPU核数

注意,注意!这个是有问题的。
注意,注意!这个是有问题的。
注意,注意!这个是有问题的。

前面讲过了,这个数值是 使用CPU的时间,如果计算的时候抛开了时间的概念,那么肯定是不对的。在刚才那个算式中,有一个假设的前提:Container启动的时间 和 系统的启动时间 是一样的。这明显是不对的。

所以,参见上面的代码。Docker 在计算 Container 的 CPU使用率 的时候,会取两次数值,通过两次数值的差值再计算使用率。

其他 metrics

我们的文章主要介绍了如何获取CPU使用率。但是,其他的指标也是同样的方法,比如内存使用,IO使用。

源码在我贴出的代码片段附近就可以看到,有兴趣的同学可以继续深入研究下。

结尾

作者和出处(reposkeeper) 授权分享 By CC BY-SA 4.0

关注微信公众号,获取新文章的推送!

Docker CPU Usage的更多相关文章

  1. Docker CPU 资源限制——CPU分片功能测试

    之前的一篇随笔——Docker CPU 资源限制 中介绍了针对COU的某个或某几个核的控制,今天介绍下CPU分片功能,即CPU占比. 测试步骤 1.下载CPU测试image.agileek/cpuse ...

  2. CPU利用率和CPU负荷(CPU usage vs CPU load)

    对于CPU的性能监测,通常用top指令能显示出两个指标:cpu 利用率和cpu负荷. 其中%Cpu相关的内容: us表示用户进程cpu利用率,sy表示系统内核进程cpu利用率,ni表示运行正常进程消耗 ...

  3. Unity Profiler CPU Usage(CPU使用情况)

    在Profiler界面点击左侧CPU Usage,Profiler界面下方Hierarchy窗口会列出各个函数对当前CPU的耗时,从大到小排序. 然后分析,各个函数的耗时是否异常,分析有没有可以优化的 ...

  4. How to Limit NodeRunner.exe High Memory, CPU Usage

    roblem: NodeRunner.exe is consuming a lot of memory and CPU resulted in performance issues on ShareP ...

  5. CPU Usage (C#) 测试

    注意:算法仅供参考. cpuusage.cs using System; using System.Collections.Generic; using System.Diagnostics; usi ...

  6. C++第四十二篇 -- CPU Usage

    前言 目的:读取并控制CPU占用率 近期在做CPU Usage方面的事情,让CPU以一种高占用率的状态运行一定的时间,需要读取CPU各个核的占用率,网上关于这方面的资料好少,FQ也只找到了一个WMI的 ...

  7. Docker CPU 资源限制——CPU固定核功能测试

    Docker使用Linux cgroup来实现资源的限制,对于CPU的限制有两种方法: 1.cpuset CPU Set限定容器使用某个固定的CPU核.使用默认的libcontainer引擎时,可以通 ...

  8. 关于sys CPU usage 100%问题的分析

    最近一个客户抱怨他的核心EBS数据库出现性能问题.这是一个10.2.0.3的数据库,运行在Red Hat Enterprise Linux Server release 5.5 (Linux x86- ...

  9. 【DPDK】【CPU usage】DPDK应用如何计算当前系统的压力

    [前言] 使用DPDK开发的朋友应该都了解使用dpdk的fwd线程的工作模式是polling模式,即100%轮询的方式去加速网络IO,这样我们在操作系统层面上来观察目标processer会发现usag ...

随机推荐

  1. Java常见错误列表

    Java常见错误列表: 找不到符号(symbol) 类X是public的,应该被声明在名为X.java的文件中 缺失类.接口或枚举类型 缺失X 缺失标识符 非法的表达式开头 类型不兼容 非法的方法声明 ...

  2. Asp.Net MVC源码调试

    首先下载MVC源代码,下载地址为:https://aspnetwebstack.codeplex.com/ 打开项目,卸载test文件夹下的所有项目和System.Web.WebPages.Admin ...

  3. ZT 线程处理函数pthread_cleanup_push / pthread_cleanup_pop

    http://bbs.csdn.net/topics/390688105 2)创建了线程,但是线程退出时没有线程调用pthread_join() 线程资源没有回收,如果持续创建线程,到一定数量后将不能 ...

  4. 定义类、System.Object对象、构造函数与析构函数、抽象类与静态类

    一.类定义 class MyClass { //类成员 } 1.访问级别 默认访问级别为internal(内部类),也可以是public(公共类) internal(内部类):当前项目中的代码才能访问 ...

  5. 019.2 map集合类

    Map<k,v>Map:双列集合,一次存一对,键值对,类似于python的字典.共性功能:1.添加    v put(key,value)     //返回key的旧值    putAll ...

  6. java继承-重写-super实例补充

    方法重写: 是指子类根据需要父类继承来的方法进行改写,是多态机制的前奏. 重写注意点: 1.重写方法必须和被重写方法具有相同的方法名,参数列表和返回值. 2.重写方法方法不能使用比被重写方法更严格的访 ...

  7. CXF+JAXB处理复杂数据

    CXF+JAXB处理复杂数据   CXF默认使用JAXB 来实现对象和XML之间的映射.在前面的例子 中,使用CXF发布的Webservice,其方法的参数和返回值都是简单类型. 本文讨论对象复杂性的 ...

  8. Xiaocms验证码绕过分析

    事实证明这套程序验证码没有办法存在绕过.具体分析在t00ls上.但是这套程序获取验证码的逻辑是存在问题的,思路是可以借鉴的. 第一次请求后台是,红线位置是请求验证码的url. 当我们第一次请求时,也就 ...

  9. 4、Android-数据存储方案(SQLite数据库存储)

    4.4.SQLite数据库存储 这是Android内置的数据库 是一款轻量级的关系型数据库 运算速度非常快.占用资源少.通常只需要几百kb的内存就够了 因而特别适合在移动端设备上使用 SQLite不仅 ...

  10. Http请求发送json数据用实体类接收

    以上是请求URL以及json数据 接收层