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

Linux cgroup v2 memory.oom.group 怎样统一处理进程组内存不足

来源:17golang原创

时间:2026-10-20 02:20:28 260浏览 收藏

多进程服务被 cgroup v2 限制后,最难排查的情况不是“进程被杀”,而是只剩下一半:主进程还在,工作进程已经退出;或者 sidecar 仍然存活,但它依赖的服务状态已经不完整。需要整体处理这类工作负载时,可以在目标非根 cgroup 中设置 memory.oom.group=1。

这个开关的含义是:当 OOM killer 在该 cgroup 内做决定时,把该 cgroup 及其后代中的任务视为不可拆分的工作负载,整体杀掉或整体不杀。它不会替代 memory.max,也不会把系统中其他 cgroup 的进程一起清理。

官方文档:https://docs.kernel.org/admin-guide/cgroup-v2.html

影响面:为什么只退出一个进程会留下残缺服务

假设一个服务由 master、worker 和 sidecar 组成,三者共享同一个 cgroup。内存达到上限后,回收无法继续,内核进入该 cgroup 的 OOM 处置路径。如果仍使用默认的 memory.oom.group=0,可能出现单个任务退出而其他任务继续运行的状态。

这种状态会把故障伪装成“服务还活着”:健康检查可能只看到 master,队列却没人消费;sidecar 还能上报心跳,却无法代表业务完成。复盘时先看工作负载是否允许部分存活,再决定是否需要组级处置。

触发条件:memory.max 负责上限,OOM 决策负责处置

memory.max 是硬内存边界。达到边界后,内核会先尝试直接回收;回收不能把使用量降下来时,分配可能进入 OOM 状态。memory.oom.group 只影响 OOM killer 如何选择该 cgroup 内的任务,不会让上限变大,也不会阻止前面的回收。

memory.max、回收、OOM 决策和多进程工作负载之间的静态关系说明图
图1:静态结构说明图,展示内存上限、回收判断和工作负载成员之间的关系,不是运行截图或实测证据。

可以把排查问题拆成三层:先确认目标 cgroup 确实启用了 memory 控制器,再确认内存上限是否在目标层级生效,最后确认被管理的进程是否都属于同一工作负载。不要把“进程退出”直接等同于“组策略生效”。

修复动作:把相关任务放入同一 cgroup 再开启组级策略

下面的命令是配置示例。路径和 PID 需要替换成实际环境中的值;示例只展示接口关系,不代表已经在本机执行。

# 目标 cgroup 由父级下放 memory 控制器后再使用
CG=/sys/fs/cgroup/my-service
sudo mkdir -p "$CG"

# 将同一工作负载视为不可拆分的 OOM 处置单元
echo 1 | sudo tee "$CG/memory.oom.group"

# 把 master 放入目标 cgroup;它创建的子进程通常继承该归属
echo "$MAIN_PID" | sudo tee "$CG/cgroup.procs"

# 读取策略值,确认配置文件里的当前设置
cat "$CG/memory.oom.group"

如果由 systemd、容器运行时或其他管理器创建 cgroup,应把这项配置放在管理器的资源配置层,而不是发布后手工写一次。手工写入可能在服务重启、单元重新生成或容器重建时丢失。

父级控制器、服务 cgroup、cgroup.procs 与 memory.oom.group 的静态配置关系图
图2:静态配置关系说明图,展示服务 cgroup、进程归属和 memory.oom.group=1 的边界,不是终端截图。

边界判断:哪些任务不会按组级策略被杀

第一,memory.oom.group 只影响触发 OOM 的那个 cgroup 及其后代,不会因为祖先 cgroup 设置了组策略,就把目标 cgroup 外的任务纳入处置。第二,设置了 oom_score_adj=-1000 的任务属于例外,内核文档明确说明这类任务不会被该 OOM 处置杀掉。

第三,进程迁移后,资源归属和进程归属不一定同时变化;服务启动时就应把 master、worker 等逻辑上不可拆分的成员放入正确层级。第四,组级杀进程不等于自动恢复,仍要由 systemd、容器编排器或服务自身的 supervisor 负责重新启动和清理。

防复发:发布后保留三项排查证据

每次调整内存策略后,至少保留三类信息:目标 cgroup 的路径和 memory.oom.group 当前值;关键 PID 的 /proc/PID/cgroup 归属;服务管理器记录的退出与重启原因。这样才能区分“配置没有加载”“进程没有进入目标组”和“组策略生效但服务没有恢复”三种情况。

排查对象要回答的问题不要误判成
memory.max目标组的硬上限是什么组级 OOM 开关
memory.oom.groupOOM 时是否整体处置提高可用内存
cgroup.procs 与 /proc/PID/cgroup哪些任务实际属于工作负载只看服务名猜归属

总结一下:先确认服务是否允许部分存活,再在同一 cgroup 内配置 memory.oom.group=1;同时保留 OOM 保护任务、祖先边界和进程归属这三个例外判断。这个方案解决的是“如何整体处置”,不是“如何避免触发内存上限”。

相关问题

设置 memory.oom.group=1 会扩大 memory.max 吗?

不会。它只改变 OOM killer 在目标组内的处置粒度,内存上限仍由 memory controller 的其他接口决定。

为什么 sidecar 还活着但业务已经不可用?

可能是 sidecar 与 worker 不在同一个目标 cgroup,或者仍使用默认的单任务处置策略。应先核对每个 PID 的 cgroup 归属,再判断是否需要把工作负载作为整体管理。

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