K8S/Docker中对于容器内存的监控 (www.ipcpu.com)
一、概述
在使用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中对于容器内存的监控