登录
推荐 文章 Go 技术 课程 下载 专题 AI
首页 >  文章 >  linux

Linux cgroups v2 内存限制怎么验证:memory.high、memory.max 与 OOM 事件逐项核对

来源:17golang原创

时间:2026-08-26 23:11:10 304浏览 收藏

服务明明没有把宿主机内存吃满,容器却突然退出,日志里还出现了 OOMKilled。这类问题通常不是“机器没内存”这么简单,而是进程撞上了自己所在 cgroup 的限制。cgroups v2 里,memory.highmemory.maxmemory.events 各自记录不同层次的压力,必须放在同一条证据链里看。

先看 memory.current 与两个阈值的关系,再用 memory.events 判断是回收/限速还是触发了硬上限;只有看到 oom_kill 增长,才能确认 cgroup 内发生过 OOM 杀进程。

要点速览
  • memory.high 是压力线,超过后会回收并可能让任务变慢,不等于立即杀进程。
  • memory.max 是硬上限;无法回收时才会进入 cgroup OOM 路径。
  • memory.current 要和 memory.events 的计数一起读取,单看某一刻的数值不够。
  • 复查时保留限制文件、事件计数和进程退出时间,避免把宿主机指标当成容器证据。

先把 cgroups v2 的三条内存证据放到一起

下面以一个名为 demo-worker 的层级说明。实际环境中,Docker、containerd 或服务管理器可能把服务放在更深的目录里,先确认进程的 cgroup 路径,再读取对应目录。

cat /proc/$(pgrep -n demo-worker)/cgroup
cg=/sys/fs/cgroup/demo-worker
cat "$cg/memory.current" "$cg/memory.high" "$cg/memory.max"
cat "$cg/memory.events"

memory.current 是当前用量,memory.high 是触发内存压力控制的边界,memory.max 是不可突破的硬限制。文件显示 max 时表示该层级没有设置对应上限;它不代表整个主机没有上限。

Linux cgroups v2 内存限制阶梯:memory.current 接近 memory.high 后进入压力控制,超过 memory.max 才进入硬上限路径
把当前用量、压力线和硬上限放在同一条判断路径。
文件它回答的问题不要误读成
memory.current现在用了多少内存已经发生 OOM
memory.high何时进入压力控制达到后必然杀进程
memory.max该层级的硬上限宿主机总内存
memory.events压力与 OOM 事件是否累计发生当前瞬时用量

memory.high 超过后,为什么进程只是变慢

当用量越过 memory.high,内核会对该 cgroup 施加内存压力控制,任务可能在分配内存时被延迟,并触发回收。它更像“限速线”,不是直接的 kill 开关。因此,接口延迟突然拉高但进程仍然存活时,先看这条线和 high 计数。

awk '$1 ~ /^(high|max|oom|oom_kill)$/ {print}' "$cg/memory.events"
cat "$cg/memory.pressure"

如果 high 持续增加,而 oom_kill 不变,现象更接近回收和节流。这里别急着把 memory.high 调大:先确认工作负载是否存在缓存无界增长、批量读取没有分段,或子进程没有退出。

memory.max 与 OOM 事件如何交叉验证

当回收无法把用量压回 memory.max 以下,cgroup 才会进入 OOM 处理。此时应保存事件计数和应用日志,而不是只凭一次 docker ps -a 的退出状态下结论。

before=$(awk '$1=="oom_kill" {print $2}' "$cg/memory.events")
date -Is
cat "$cg/memory.current" "$cg/memory.max"
cat "$cg/memory.events"
printf 'oom_kill_before=%s\n' "$before"

重点看 oomoom_killoom_group_kill 是否增长。不同内核版本和工作负载下,事件字段的具体组合可能不同,但 oom_kill 增长是确认“cgroup 杀过进程”的关键证据。把这次输出和服务退出时间对齐,才能排除应用自己调用 exit、健康检查失败或上层编排器重启。

Linux cgroups v2 事件核对路径:先比较 memory.current 与 memory.max,再检查 memory.events 中 high 和 oom_kill 的变化
事件计数负责说明发生过什么,当前值负责说明现在有多满。

一套适合线上复盘的检查顺序

  1. 确认层级:从目标 PID 的 /proc/PID/cgroup 找到实际 cgroup 目录,避免读取了父层或另一个容器的指标。
  2. 记录阈值:保存 memory.highmemory.max 与采样时间;不要把 max 当成无限资源承诺。
  3. 记录当前值:连续采样 memory.current,观察是否在回收后下降,还是一直贴着硬上限。
  4. 核对事件:前后对比 memory.eventshigh 增长提示压力控制,oom_kill 增长才支持 OOM kill 判断。
  5. 对齐上层记录:把事件时间与应用日志、容器退出原因、编排器重启记录对齐,再决定是降峰、修复泄漏还是调整限制。

调整参数前,先把采样结果保存到故障单。否则重启容器后事件计数可能随层级销毁而丢失,之后只能凭印象争论“到底是不是内存杀”。

cgroups v2 内存限制常见问题:看懂数字,却看错了对象

宿主机 free 还有很多,为什么容器仍会被杀?

宿主机可用内存和 cgroup 上限是两套边界。容器可以在主机还有余量时先撞上自己的 memory.max

memory.high 增长是不是 OOM?

不是。它更常说明任务经历了内存压力控制。需要继续看 oom_kill 等事件是否增长。

把 memory.max 调大就能解决吗?

不一定。若根因是缓存或批处理无界增长,调大只会推迟故障,还可能把压力转移给宿主机。先用采样确认增长对象。

结论:用“阈值、当前值、事件计数”闭环判断

cgroups v2 的排查重点不是背参数,而是让三类证据互相印证:阈值说明边界,memory.current 说明此刻的占用,memory.events 说明压力和 OOM 是否累计发生。这样才能把“服务变慢”“容器重启”和“cgroup 杀进程”分成三个不同问题,后续的容量调整才有依据。

声明:本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
相关阅读
更多>
最新阅读
更多>
课程推荐
更多>