最新消息:

K8S/Docker中对于容器内存的监控

IT技术 ipcpu 1206浏览 0评论

一、概述

在使用Docker或者Kubernetes时,我们经常需要监控容器或者Pod的内存,同时我们也经常收到反馈内存不准确的情况,这不仅是因为存在Buffer、Cache的影响,不同的算法指标也会得出不同的结果。
接下来我们先回顾下我们最古老的计算方法,然后分别取分析docker stats 和 kubectl top 中的内存计算方法。

二、最原始的计算内存方法

我翻阅了以前的一些工作资料,发现从Docker 1.5 开始才提供了Docker stats接口,这个接口有API和命令行两种方式,并且API是stream方式,会不间断的发送数据,当时我们也不清楚如何和这样的API接口对接,所以我们选择了直接对cgroup文件下手。
我们读取了以下两个文件:

# 容器当前已使用的内存Bytes
/cgroup/memory/docker/$DOCKERID/memory.usage_in_bytes
# 容器最大限制内存Bytes
/cgroup/memory/docker/$DOCKERID/memory.limit_in_bytes

这两个数据相除就得到了内存使用率,当时并没有考虑Buffer、Cache等数据,也没有详细深究/cgroup/memory/docker/$DOCKERID/memory.stat文件内容,所以与docker stats略有偏差。

接下来我们看下Docker stats 中的实现方法

三、Docker stats中的内存计算方法

由于低版本的docker源码已经看不了了,在Docker V19和之前的版本中,

mem.Usage - mem.Stats["cache"]

看起来与我们上面实现的方式类似,减掉了Cache部分,具体源码如下:

// calculateMemUsageUnixNoCache calculate memory usage of the container.
// Page cache is intentionally excluded to avoid misinterpretation of the output.
func calculateMemUsageUnixNoCache(mem types.MemoryStats) float64 {
    return float64(mem.Usage - mem.Stats["cache"])
}

从Docker V20版本开始这部分有修改,计算方法为

mem.Usage - inactive_file

详细代码如下

// calculateMemUsageUnixNoCache calculate memory usage of the container.
// Cache is intentionally excluded to avoid misinterpretation of the output.
//
// On cgroup v1 host, the result is `mem.Usage - mem.Stats["total_inactive_file"]` .
// On cgroup v2 host, the result is `mem.Usage - mem.Stats["inactive_file"] `.
//
// This definition is consistent with cadvisor and containerd/CRI.
// * https://github.com/google/cadvisor/commit/307d1b1cb320fef66fab02db749f07a459245451
// * https://github.com/containerd/cri/commit/6b8846cdf8b8c98c1d965313d66bc8489166059a
//
// On Docker 19.03 and older, the result was `mem.Usage - mem.Stats["cache"]`.
// See https://github.com/moby/moby/issues/40727 for the background.
func calculateMemUsageUnixNoCache(mem types.MemoryStats) float64 {
    // cgroup v1
    if v, isCgroup1 := mem.Stats["total_inactive_file"]; isCgroup1 && v < mem.Usage {
        return float64(mem.Usage - v)
    }
    // cgroup v2
    if v := mem.Stats["inactive_file"]; v < mem.Usage {
        return float64(mem.Usage - v)
    }
    return float64(mem.Usage)
}

四、Kubernetes/cadvisor中的内存计算方法

kubectl top 的内存数据用working_set参数,底层数据是从kubelet中内置的cadvisor来读取的,但是cadvisor的旧代码我们也无迹可寻了,我们找了v0.30.0 9 Mar 2018 ,计算方式为:

    workingSet := ret.Memory.Usage
    if v, ok := s.MemoryStats.Stats["total_inactive_file"]; ok {
        if workingSet < v {
            workingSet = 0
        } else {
            workingSet -= v
        }
    }
    ret.Memory.WorkingSet = workingSet

简单的说,就是:

Memory.Usage - MemoryStats.Stats["total_inactive_file"]

这个方法和Docker V20以后是一致的,也就是说一直到Docker 20.10.0时间是2020-12-08,docker stats 和 kubectl top 内存数据才达成一致,之前都是不统一的。

五、统一的内存计算方法

所以,Docker 20.10.0(2020-12-08) 之后,docker stats 和 kubectl top都会使用下面方法计算内存使用:

memory_working_set  = Memory.Usage - Memeory.inactive_file

参考资料

https://qingwave.github.io/container-memory/
https://segmentfault.com/a/1190000021493607
https://blog.csdn.net/Ivan_Wz/article/details/119457692
https://kubesphere.com.cn/forum/d/1517-docker-stats-mem-top-res
https://github.com/docker/cli/blob/v19.03.15/cli/command/container/stats_helpers.go

转载请注明:IPCPU-网络之路 » K8S/Docker中对于容器内存的监控

发表我的评论
取消评论
表情

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址