Loading

Kuberneter Pod内存分析

内存指标

  • container_memory_working_set_bytes

    kubectl top 等工具显示的内存用量都是基于它,Kubernetes 里判断 容器内存压力 和触发 OOM 的重要指标;包含容器正在使用的实际内存,包括 RSS 和活跃的缓存,但不包括那些可以被随时回收的读缓存(Page Cache),会包含“写缓存”(dirty pages),因为写缓存是写操作的数据,必须先写入磁盘后才能释放。

    Linux 内核将文件读写缓存分为两类:

    • 读缓存(page cache):可随时回收,不会导致内存压力。
    • 写缓存(dirty pages):必须同步到磁盘,不能随时回收,可能导致内存不断增长,最终触发 OOM。

    对于大量文件 IO 的服务,写缓存积累会使 working_set_bytes 持续上升,表现为内存使用不断涨,直到系统释放缓存或发生 OOM。

  • container_memory_rss

    表示进程实际驻留在物理内存的大小,包含匿名内存和部分缓存,更偏向“进程层面”的内存占用,不一定代表内核层面真正的活跃内存压力。

RSS

先看什么是 RSS。RSS 是 Resident Set Size 的缩写,简单来说它就是指进程真正申请到物理页面的内存大小。这是什么意思呢?

应用程序在申请内存的时候,比如说,调用 malloc() 来申请 100MB 的内存大小,malloc() 返回成功了,这时候系统其实只是把 100MB 的虚拟地址空间分配给了进程但是并没有把实际的物理内存页面分配给进程。*

当进程对这块内存地址开始做真正读写操作的时候,系统才会把实际需要的物理内存分配给进程。而这个过程中,进程真正得到的物理内存,就是这个 RSS 。

Page Cache

每个进程除了各自独立分配到的 RSS 内存外,如果进程对磁盘上的文件做了读写操作,Linux 还会分配内存,把磁盘上读写到的页面存放在内存中,这部分的内存就是 Page Cache。

Page Cache 的主要作用是提高磁盘文件的读写性能,因为系统调用 read() 和 write() 的缺省行为都会把读过或者写过的页面存放在 Page Cache 里。

代码程序去读取 100MB 的文件,在读取文件前,系统中 Page Cache 的大小是 388MB,读取后 Page Cache 的大小是 506MB,增长了大约 100MB 左右,多出来的这 100MB,正是我们读取文件的大小。

在 Linux 系统里只要有空闲的内存,系统就会自动地把读写过的磁盘文件页面放入到 Page Cache 里。那么这些内存都被 Page Cache 占用了,一旦进程需要用到更多的物理内存,执行 malloc() 调用做申请时,就会发现剩余的物理内存不够了,那该怎么办呢?

这就要提到 Linux 的内存管理机制了。 Linux 的内存管理有一种内存页面回收机制(page frame reclaim),会根据系统里空闲物理内存是否低于某个阈值(wartermark),来决定是否启动内存的回收。

内存回收的算法会根据不同类型的内存以及内存的最近最少用原则,就是 LRU(Least Recently Used)算法决定哪些内存页面先被释放。因为 Page Cache 的内存页面只是起到 Cache 作用,自然是会被优先释放的。

所以,Page Cache 是一种为了提高磁盘文件读写性能而利用空闲物理内存的机制。同时,内存管理中的页面回收机制,又能保证 Cache 所占用的页面可以及时释放,这样一来就不会影响程序对内存的真正需求了。

已生成图片

查看容器中实际内存情况

cat /sys/fs/cgroup/memory/memory.stat
cache 235646976
rss 30494720
rss_huge 12582912
mapped_file 208896
swap 0
pgpgin 15027525
pgpgout 15050952
pgfault 574983
pgmajfault 131
inactive_anon 0
active_anon 30470144
inactive_file 234209280
active_file 1421312
unevictable 0
hierarchical_memory_limit 268435456
hierarchical_memsw_limit 268435456
total_cache 235646976
total_rss 30494720
total_rss_huge 12582912
total_mapped_file 208896
total_swap 0
total_pgpgin 0
total_pgpgout 0
total_pgfault 0
total_pgmajfault 0
total_inactive_anon 0
total_active_anon 30470144
total_inactive_file 234209280
total_active_file 1421312
total_unevictable 0

rss:进程实际使用物理内存。

cache:文件页缓存。

rss_huge:大页内存使用量。

其他字段反映内存回收、分页情况。

Buffer 与 Cache

区别及作用

  • Buffer(缓冲区)
    • 是对磁盘块数据的缓存。
    • 主要用于写磁盘数据时的缓存,也会用作读磁盘时的缓存。
    • 作用是减少磁盘I/O操作次数,提高磁盘读写效率。
  • Cache(缓存)
    • 是对文件数据的缓存,具体是指页缓存(Page Cache)
    • 主要用于读取文件数据时的缓存,同样也会用于写文件时的缓存(写回机制)。
    • 作用是减少对磁盘文件的直接访问,加快文件的读取和写入速度。

总结

内存中存在大量“写缓存”——也称为脏页(Dirty Pages),即已经修改但尚未写回磁盘的数据页。内核将这些脏页暂存在内存中,等待异步刷盘操作完成后释放。当写入速度超过刷盘速度时,脏页会持续累积,占用大量内存资源,导致可用内存减少。

此时如果有新的内存申请请求到来,内核因无法及时释放足够内存满足需求,最终导致物理内存耗尽,触发OOM(Out Of Memory)机制。脏页的积压是导致内存紧张、OOM发生的重要因素之一。

防止脏页(Dirty Pages)堆积,减少因写缓存导致的内存紧张甚至OOM,主要是通过调整内核的几个关键参数来控制脏页的生成和刷盘行为。下面是常用的内核参数及调优建议:

关键内核参数说明

  • vm.dirty_ratio 系统内存中允许被脏页占用的最大百分比,超过该比例时进程会被阻塞,等待刷盘释放内存。 20(即20%)
  • vm.dirty_background_ratio 后台刷脏页的阈值,达到这个比例后,内核会启动后台线程异步刷盘。 10(即10%)
  • vm.dirty_bytes 如果设置了这个,优先级高于dirty_ratio,表示允许脏页最大字节数。 默认不设置,使用dirty_ratio
  • vm.dirty_background_bytes 同理,后台刷盘的阈值字节数,优先于dirty_background_ratio。 默认不设置,使用dirty_background_ratio
  • vm.dirty_expire_centisecs 脏页被认为“过期”的时间(百分之一秒),超过这个时间脏页就会被刷盘。 3000(即30秒)
  • vm.dirty_writeback_centisecs 内核刷盘周期(百分之一秒),内核会定时将脏页写回磁盘。 500(即5秒)

建议

  1. 日志写入

    • 尽量将日志输出到标准输出(stdout),由外部日志系统收集,减少 Pod 内部频繁写磁盘导致的写缓存堆积。
    • 做日志切割(50-100MB)并保留有限历史,避免写缓存无限膨胀。
  2. 内存限制与预留

    • 针对计算密集型应用(如 JVM),可限制程序最大内存使用(比如 10GB 容器限制程序用 8.5GB),预留一定内存给内核和写缓存,降低 OOM 风险。
    • 具体配置需结合应用实际内存需求。

https://blog.csdn.net/qq_34556414/article/details/121022005
https://developer.aliyun.com/article/972803
https://cloud.tencent.com/developer/article/2402308
https://mihai-albert.com/2022/02/13/out-of-memory-oom-in-kubernetes-part-4-pod-evictions-oom-scenarios-and-flows-leading-to-them/
https://github.com/kubernetes/kubernetes/issues/131913

posted @ 2025-08-11 13:38  亚里士多智  阅读(17)  评论(0)    收藏  举报