Kubernetes Pod内存占用为何远高于其内部进程内存总和?
问题背景
- 技术栈为
nginx + php-fpm,相同部署配置在多个项目运行稳定,仅单个项目持续出现内存溢出问题 - 异常项目PHP-FPM容器内存持续累积,直至php-fpm进程被OOMKilled,最终导致Pod终止
- 监控数据存在明显统计偏差:
- 云监控、k9s的
%MEM/L列均显示Pod内存占用持续走高 - 容器内执行
top命令统计的进程内存占用处于极低水平,健康Pod与异常Pod的php-fpm进程数量、单进程内存消耗基本持平
- 云监控、k9s的
- 内存变化规律:
- 内存增长速率与流量正相关,无流量时内存曲线保持平稳
- Pod长时间不接收流量时,已占用的内存会逐步释放
- 单独跟踪单个php-fpm进程,进程处理完请求后可正常释放内存,无进程级内存泄漏特征
- 已完成排查动作:
- 将PHP-FPM进程数量限制在较小范围,长期监控单进程内存无异常
- 容器内执行
ipcs未查询到已分配的System V共享内存 - 应用接入Memcached后,内存异常问题自行消失
- 核心疑问:
- 未被进程统计到的隐藏内存来自哪里?
- 为何Pod内存统计值与容器内进程内存统计总和存在差值?
- 如何排查定位这部分“暗内存”?
问题解答
为什么Pod内存统计和容器内top结果存在差值
top默认展示的进程内存指标是RSS(常驻集大小),仅统计进程私有、已驻留物理内存的匿名页,以及进程映射的共享内存中被该进程实际访问的部分。以下几类内存完全不会被top统计到,但会100%计入Pod对应的cgroup内存统计(也就是云监控、k9s读取的内存值,OOM判定时也会全额计入配额):
- 基于文件映射的page cache:包括程序读写本地文件产生的文件缓存页
- tmpfs内存文件系统占用的页:比如
/dev/shm、内存模式emptyDir挂载点下写入的所有内容 - POSIX共享内存、通过
mmap(MAP_ANONYMOUS|MAP_SHARED)创建的共享匿名映射:ipcs默认仅展示System V类型的IPC共享内存,这类内存不会出现在ipcs输出中 - 内核态为cgroup内进程分配的内存:包括socket收发缓冲区、slab分配器维护的dentry/inode缓存、网络连接相关内核数据结构
暗内存的根因判断
结合所有观测特征:内存增长与流量正相关、无流量时内存可自行回收、接入Memcached后问题消失、同配置其他项目无异常,99%的概率是该项目未接入外部缓存时,将大量请求缓存、会话数据或高频访问的大文件存储在容器本地文件系统,产生了巨量page cache占用:
- 其他项目要么本地文件读写量极小,要么已经接入外部缓存,因此不会触发该问题
- 无流量时内核会逐步回收可驱逐的page cache,因此内存会缓慢下降
- 接入Memcached后,原有的本地文件读写缓存逻辑全部改为访问远端缓存服务,本地不再产生大量文件IO,page cache不会持续累积,问题自然消失
- 之前用
ipcs没查到共享内存属于正常情况,page cache不属于System V IPC范畴,本来就不会被ipcs统计 - 补充CPU对照实验的异常结果也符合该特征:大量本地文件IO会额外消耗CPU在内核态页管理、文件系统逻辑上,接入Memcached后本地IO消失,CPU占用也会同步下降,与观测结果一致。
排查定位步骤
按顺序操作即可快速定位:
- 进入异常Pod查看cgroup内存拆分统计:
- cgroup v1环境执行
cat /sys/fs/cgroup/memory/memory.stat - cgroup v2环境执行
cat /sys/fs/cgroup/memory.stat
重点核对字段: file字段值:对应page cache、tmpfs、POSIX共享内存的总占用,如果该值与观测到的暗内存大小基本一致,可直接确认是文件类缓存/共享内存占用sock、slab、kernel_stack字段值:如果这几个值异常偏高,说明是内核态内存占用,常见原因为socket缓冲区泄漏、slab对象泄漏
- cgroup v1环境执行
- 如果确认
file字段占比过高,先执行df -h查看/dev/shm、所有tmpfs挂载点的占用,再执行lsof | grep -E "(deleted|/tmp|/var/lib/php)"排查是否存在大量已删除但未被进程关闭句柄的临时文件——php如果频繁创建临时文件且未正常关闭句柄,即使文件被删除,对应的page cache也不会释放,直到进程退出或关闭句柄 - 执行
free -h查看输出中的buff/cache列数值,如果该值与暗内存差值基本匹配,可进一步坐实页缓存占用的判断 - 临时验证(需要容器持有
CAP_SYS_ADMIN权限):在Pod内存涨到高位时执行echo 3 > /proc/sys/vm/drop_caches,如果执行后监控显示内存直接回落至正常水平,可100%确认是page cache累积导致的问题。
修复方案
- 根本修复:对齐接入Memcached的逻辑,不要在容器本地存储高频访问的大体积缓存、会话数据,全部下沉到外部缓存服务
- 临时缓解:调整php-fpm配置,合理设置
pm.max_requests参数,让worker进程处理一定数量请求后自动重启,进程退出时会自动释放持有的文件句柄,关联的可回收page cache会被内核逐步回收;不推荐生产环境长期使用定时触发drop_caches的方案,会带来短暂的IO性能波动。 - 注意:该问题不属于php-fpm进程私有内存泄漏,之前跟踪单进程RSS正常已经排除了这类可能,不需要在php进程内存限制参数上做无用调优。
内容的提问来源于stack exchange,提问作者Rax
相关产品推荐
相关产品推荐

