Docker API 内存用量比 docker stats 显示值高
Linux Docker CLI 会扣除特定文件缓存项,API保留原始总量。按客户端版本与 cgroup 对齐公式,保留单位、采样时间和告警阈值边界。
监控程序读取 Docker Engine API 的 memory_stats.usage,数值比同一容器的 docker stats 内存列高。确认容器没有选错后,仍然差出一段内存,不一定是 API 数据错误:Linux 上的 Docker CLI 会从总量中减去特定的文件缓存统计项,API 则保留原始字段供调用方选择。
这里处理 Linux 容器的统计口径差异,不适用于 Windows 容器的内存显示规则,也不把两种读数中的一个直接称为应用实际分配的内存。
先对齐容器、时间和单位
用完整容器 ID 对照监控请求与命令行结果。以下 myapp 是正在运行的目标容器示例名:
docker inspect --type container --format '{{.Id}}' myapp
docker version
docker info --format '{{.OSType}} {{.CgroupVersion}}'
docker stats --no-stream --no-trunc myapp
同时保存监控端取得的完整 stats JSON,至少包含容器标识、采样时间和 memory_stats。API 调用路径是 /containers/{id}/stats;通过原有经过认证的客户端读取,不需要为了排错把 Docker socket 暴露到网络。
--no-stream 只取一次统计结果,不会冻结容器。两次独立查询之间,内存仍可能变化;不能要求不同时间的读数逐字相等。API 内存字段以字节表达,CLI 又会格式化显示,比较时应统一单位,区别 MiB 与十进制 MB。
容器已经停止、请求返回错误或关键字段缺失时,先处理采样问题。不要把没有读到数据记成内存占用为零。
按 CLI 版本和 cgroup 选择字段
当前 Docker CLI 的 Linux 规则是:cgroup v1 使用 memory_stats.stats.total_inactive_file,cgroup v2 使用 memory_stats.stats.inactive_file,从 memory_stats.usage 中扣除对应值。
Docker 19.03 及更早版本采用的是 stats.cache。因此,照搬旧脚本的 usage - cache,不一定能与当前 CLI 对齐;把 v1 的字段名原样用于 v2,同样会漏掉或错误处理数据。
这里应核对运行 docker stats 的客户端版本,而不只是监控服务器的程序版本。远程客户端与 daemon 可能不同,宿主机 cgroup 模式也应以运行容器的 Linux 主机为准。
如果监控的目标就是复现当前 CLI 的显示,可对保存下来的单个完整 JSON 对象采用以下计算。把 stats.json 换成实际采样文件;示例不发起网络请求:
import json
with open("stats.json", encoding="utf-8") as source:
sample = json.load(source)
memory = sample["memory_stats"]
usage = memory["usage"]
fields = memory["stats"]
if "total_inactive_file" in fields and fields["total_inactive_file"] < usage:
displayed = usage - fields["total_inactive_file"]
elif fields.get("inactive_file", 0) < usage:
displayed = usage - fields.get("inactive_file", 0)
else:
displayed = usage
print("raw_bytes:", usage)
print("cli_style_bytes:", displayed)
print("cli_style_MiB:", displayed / (1024 * 1024))
这段按当前 CLI 的字段判断及回退逻辑计算:扣减值必须小于总量,否则不会机械相减出负数;没有可用扣减项时保留总量。它假定输入是成功取得的 Linux stats 数据,不负责修复错误响应或验证任意外部 JSON。
如果文件是多个持续推送的 JSON 对象,不能直接当作一个 JSON 文档传给 json.load()。应由采集器按消息边界解析,并逐条计算,不把不同采样的总量和缓存字段拼在一起。
扣掉的部分不是“内存泄漏已消失”
API 总量与 CLI 显示采用不同口径,都有用途。监控看板可以同时保留原始总量和按 CLI 规则计算的值,并在指标名或说明中写清楚。不要仅把原指标改名为 RSS:上述扣减并不是读取进程 RSS,也不等于程序自己申请的堆大小。
一次口径修正不会改变容器的真实资源使用、内存限制或 OOM 风险。不要为了让两张图数值一致去清页缓存、重启业务或降低内存限额。若容器确实被终止,应另查同一时段的状态、OOM 事件和资源峰值,不能仅凭扣减后的较小数值排除内存问题。
验证并保留历史指标的含义
先用同一份保存的 JSON 核对总量、扣减字段和结果,再将持续采样与临近时间的 CLI 输出比较,允许采样间隔与显示舍入造成差异。验证范围要包含实际使用的 cgroup 模式;旧客户端不应被套进新公式后仍标成旧版原始指标。
修改采集器前保存原计算逻辑和指标定义。变更时标注时间,避免把计算方式变化造成的阶跃误判为业务内存下降。告警阈值也应按所用指标重新评估;回退时恢复原公式及对应阈值,不要把两种口径的数据无标记地混成一条连续曲线。


