背景
最近在把服务改造成docker容器化,但是在压测中发现,使用docker stats查看容器情况,发现内存一直在增长,
但是使用ps -aux查看的RSS(实际物理内存)内存占用是固定的,没有增长。针对此问题,现记录一下排查过程。
主机系统是CentOS 7.9.2009,docker版本是26.1.4。
排查过程
基础知识
docker stats统计的是cgroup memory.usage_in_bytes,主要包括:
- RSS
- 程序未释放但是占用的虚拟内存(anonymous memory)
- Page Cache(文件缓存)
- Shmem(共享内存)
- tmpfs/overlayfs 缓冲区
- Mapped文件
- 网络相关的buffer
- 内核为容器分配的内存
ps -aux查看的就是RSS(实际物理内存)。
开始排查
- 查看容器的内存实际占用情况
|
|
需要注意的是container_id是完整的,不是缩写。 我们使用docker ps查看容器id时,默认显示的是缩写的id。
第一次数据为:
|
|
第二次的数据:
|
|
统计两次的差异为:
| 字段 | snapshot1 (MB) | snapshot2 (MB) | diff (MB) |
|---|---|---|---|
| cache(缓存) | 597.652 | 636.128 | 38.476 |
| rss(常驻内存) | 31.129 | 31.426 | 0.297 |
| pgpgin(内存页读入次数) | 0.222 | 0.232 | 0.010 |
| pgpgout(内存页写出次数) | 0.073 | 0.074 | 0.001 |
| pgfault(缺页次数) | 0.106 | 0.108 | 0.002 |
| active_anon(匿名活跃页) | 6.094 | 6.362 | 0.268 |
| inactive_file(文件未活跃页) | 265.583 | 278.000 | 12.417 |
| active_file(文件活跃页) | 331.942 | 358.000 | 26.058 |
| total_cache(总缓存) | 597.652 | 636.128 | 38.476 |
| total_rss(总常驻内存) | 31.129 | 31.426 | 0.297 |
| total_active_anon(总匿名活跃) | 6.094 | 6.362 | 0.268 |
| total_inactive_file(总文件未活跃) | 265.583 | 278.000 | 12.417 |
| total_active_file(总文件活跃) | 331.942 | 358.000 | 26.058 |
可以看到:
cache/total_cache: 增长了38.476MB,
inactive_file: 增长了12.417MB,
active_file: 增长了26.058MB,总共就38.476MB, 说明文件cache增多。
rss/total_rss: 增长了0.297MB,可以忽略。
这种 cache的增长原因是:容器内频繁访问文件,包括:
- 日志文件
- overlay2 文件层
- 配置文件
- 静态文件
- 访问volume
这个是正常情况,内核会在内存不足时,自动回收。
但是内存一直增长不是正常现象,需要进一步排查原因。
- 排查
cache增长的原因
在主机上临时清除缓存:
|
|
执行之后,可以发现docker stats会瞬间下降到RSS的大小。
因为我把日志挂载到了主机上, 基本可以确定是日志文件导致的cache增长。
- 如何解决
原本是修改日志组件, 每次Write之后就Sync到磁盘, 但是改完之后效果不大,Page Cache还是会增长。
采用的方案有两种:
- 修改系统的内存回收机制:
|
|
sysctl -p 生效
效果不太明显,增长的速度只是慢了一些,但是还是一直增长。
- 是在
docker-compose.yaml中添加mem_limit和mem_reservation参数, 限制容器内存占用:
|
|
在测试中发现,设置的最大内存为40M,程序的RSS占用为37M, 当docker stats增长还没到40M时(大概35M),
会主动回收Page Cache。导致docker stats的内存不会增长到40M。
总结
- 当
RSS的大小不增长时,可以认定服务是没有内存泄漏问题。 - 当
Page Cache的大小增长时,这类就需要设置系统层面上的内存回收机制, 限制容器内存占用,让其自动回收。