来自 docker 统计数据与 cgroups 的 cpuPercent 指标

cpuPercent metric from docker stats vs cgroups

我是 cgroups 的新手,正在尝试使用 cgroups 获取容器统计信息。以前我使用的是 docker stats,但我也尝试使用 cgroups 收集类似的指标。

在 docker 统计中,cpu 统计部分如下所示:

"cpu_usage": {
        "total_usage": 27120642519,
        "percpu_usage": [27120642519],
        "usage_in_kernelmode": 4550000000,
        "usage_in_usermode": 19140000000
    },
    "system_cpu_usage": 42803030000000,

并且,cpu% 指标是使用以下等式计算的:

cpuDelta = float64(v.CpuStats.CpuUsage.TotalUsage - previousCPU)
systemDelta = float64(v.CpuStats.SystemUsage - previousSystem)
cpuPct = cpuDelta/systemDelta

我正在查看 cgroups 以收集 systemUsagetotalUsage,但它似乎没有类似的指标:

cgroups 有一个伪文件 cpuacct.stats,其中有 usersystem 刻度,但它们仅与 usage_in_user_modeusage_in_kernel_mode 匹配 docker stats 输出.

和 cpuacct.usage_per_cpu 伪文件每个 cpu 都有一个用法,这与上面 docker 统计输出中的 total_usage 相匹配。

$cat cpuacct.stat 
user 1914
system 455

$cat cpuacct.usage_percpu 
27120642519 

但是,我找不到任何方法来弄清楚如何从 cgroups 收集 "systemUsage"。

任何线索都会有很大帮助!

谢谢!

您问题的答案不在 cgroup 中。请参考以下几点:

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
}
  1. Docker统计API的"system_cpu_usage"是指主机的CPU使用情况。
  2. Docker 统计数据 API 的 "cpu_usage" > "total_usage" 是指容器的每个 CPU 使用情况。
  3. 因此,在计算 (cpuDelta/systemDelta) 后,我们得到每个系统的每个 CPU 使用量 CPU.
  4. 现在我们需要将第 3 步的结果与分配给 docker 容器的 CPU 总数相乘,得到每个系统的总 CPU 使用量 CPU.
  5. 第 4 步的结果乘以 100 得到 CPU 百分比利用率。

回到问题: 系统CPU是如何通过docker计算出来的?

要计算系统 CPU 使用情况 docker 使用 POSIX 定义的“/proc/stat”。它查找 CPU 统计行,然后汇总提供的前七个字段。下面提到了为执行所需步骤而编写的 golang 代码。

https://github.com/rancher/docker/blob/3f1b16e236ad4626e02f3da4643023454d7dbb3f/daemon/stats_collector_unix.go#L137

// getSystemCPUUsage returns the host system's cpu usage in
// nanoseconds. An error is returned if the format of the underlying
// file does not match.
//
// Uses /proc/stat defined by POSIX. Looks for the cpu
// statistics line and then sums up the first seven fields
// provided. See `man 5 proc` for details on specific field
// information.
func (s *statsCollector) getSystemCPUUsage() (uint64, error) {
    var line string
    f, err := os.Open("/proc/stat")
    if err != nil {
        return 0, err
    }
    defer func() {
        s.bufReader.Reset(nil)
        f.Close()
    }()
    s.bufReader.Reset(f)
    err = nil
    for err == nil {
        line, err = s.bufReader.ReadString('\n')
        if err != nil {
            break
        }
        parts := strings.Fields(line)
        switch parts[0] {
        case "cpu":
            if len(parts) < 8 {
                return 0, derr.ErrorCodeBadCPUFields
            }
            var totalClockTicks uint64
            for _, i := range parts[1:8] {
                v, err := strconv.ParseUint(i, 10, 64)
                if err != nil {
                    return 0, derr.ErrorCodeBadCPUInt.WithArgs(i, err)
                }
                totalClockTicks += v
            }
            return (totalClockTicks * nanoSecondsPerSecond) /
                s.clockTicksPerSecond, nil
        }
    }
    return 0, derr.ErrorCodeBadStatFormat
}

请将 docker 统计数据 API 的 "system_cpu_usage" 与下面提到的命令的输出进行匹配以确认:

cat /proc/stat|grep -w cpu|awk '{split([=12=],a,\" \"); sum=0; for(i=2;i<8;i++)(sum+=a[i])} END{print sum }'