深色模式
OOM 与 cgroup 限制排查
摘要:容器被杀时宿主机往往还有大量空闲内存,因为触发的是 cgroup 限额而非整机 OOM。本文教你在 dmesg、cgroup 接口和容器状态三处取证,并解释 OOM 评分机制为何"杀错进程"。
适用环境
bash
cat /sys/fs/cgroup/memory.max 2>/dev/null # cgroup v2
cat /sys/fs/cgroup/memory/memory.limit_in_bytes 2>/dev/null # cgroup v1
cat /proc/self/cgroup1
2
3
2
3
排障步骤
第 1 步:在内核日志中找 OOM 记录
bash
dmesg -T | grep -iE 'oom|killed process|out of memory' | tail -30
journalctl -k --no-pager | grep -iE 'oom|killed process' | tail -301
2
2
dmesg 可能被限制访问
出现 dmesg: read kernel buffer failed 时需检查 kernel.dmesg_restrict,或改用 journalctl -k。
典型输出包含 Killed process <PID> (<name>), UID ..., total-vm:... anon-rss:...,据此可确认被杀进程与实际占用。
第 2 步:区分整机 OOM 与 cgroup OOM
bash
dmesg -T | grep -i 'Memory cgroup out of memory'1
出现 Memory cgroup out of memory 即为 cgroup 限额触发,此时 free 显示宿主机仍有空闲内存属正常。
第 3 步:读 cgroup 内存指标
bash
# cgroup v2
cat /sys/fs/cgroup/memory.max
cat /sys/fs/cgroup/memory.current
cat /sys/fs/cgroup/memory.events # oom_kill 计数
# cgroup v1
cat /sys/fs/cgroup/memory/memory.limit_in_bytes
cat /sys/fs/cgroup/memory/memory.usage_in_bytes
cat /sys/fs/cgroup/memory/memory.oom_control1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
memory.events 中 oom_kill 非 0 即证明发生过 cgroup 级 OOM kill。
第 4 步:容器视角确认
bash
kubectl get pod <pod> -n <ns> -o jsonpath='{.status.containerStatuses[*].lastState}'
kubectl describe pod <pod> -n <ns> | grep -i oom -A 3
docker inspect <container> --format '{{.State.OOMKilled}} {{.State.ExitCode}}'1
2
3
2
3
退出码 137 的含义
137 = 128 + 9(SIGKILL),高度提示被 OOM 杀或外部 kill,但需结合 dmesg 确认,也可能是人为 kill。
第 5 步:理解 OOM 评分与"杀错进程"
bash
cat /proc/<PID>/oom_score
cat /proc/<PID>/oom_score_adj1
2
2
内核按 oom_score 从高到低杀,占内存越多的进程分越高。因此容器里最可能被杀的不是"有问题的那个",而是占内存最大的那个(往往是主进程自身)。
保护关键进程:
bash
echo -1000 > /proc/<PID>/oom_score_adj1
第 6 步:排查内存为何超限
bash
cat /sys/fs/cgroup/memory.stat 2>/dev/null | head -20
grep -E 'VmRSS|VmSwap' /proc/<PID>/status1
2
2
区分是 anon(堆、匿名内存)还是 slab/file(页缓存、内核对象)占用。
验证
bash
dmesg -T | grep -i 'out of memory' | tail -5
cat /sys/fs/cgroup/memory.events
kubectl get pod -n <ns>1
2
3
2
3
常见坑
容器被杀但宿主 free 很充足
这是 cgroup 限额触发的正常现象,调大宿主机内存无效,必须调整容器 limit 或降低应用占用。
cgroup v1 与 v2 路径完全不同
两者接口位置和文件名都不一样,脚本中应做兼容判断,不能只写一种路径。
只调大 limit 不查泄漏
被动扩容会掩盖泄漏,最终把压力转嫁到节点上,引发同节点其它 Pod 被驱逐。
关闭 OOM kill 或设为 panic
禁用 OOM killer 会让整机进入不可恢复的挂起状态,最终只能硬重启。任何环境都不应关闭。