1430 字
7 分钟

systemd-oomd 在 cgroup v2 下的触发机制与服务级 OOM 控制实践

在 cgroup v2 成为主流之后,传统的内核 OOM Killer 已经不再是唯一的内存回收机制。systemd 引入的 systemd-oomd 通过 PSI(Pressure Stall Information)感知系统内存压力,在内核 OOM 触发前主动终止高风险服务,从而避免整机卡死。

本文将从 PSI 指标含义入手,分析 systemd-oomd 的三种触发场景,并结合 memory.highslice 配置与实际示例,说明它与内核 OOM 的区别以及在生产环境中的应用方式。

PSI(Pressure Stall Information)#

PSI (Pressure Stall Information) 是 Linux 内核(4.20+)提供的一项高级功能,用于实时监控 CPU、内存和 IO 资源的压力情况。它量化了任务因等待硬件资源而停顿的时间,能准确反映资源紧张对系统性能的影响,帮助预测系统崩溃并优化资源利用率。

被监控的四种资源#

  • cpu
  • io
  • irq
  • memory

PSI 输出结构#

Terminal window
some avg10=12.50 avg60=8.23 avg300=5.01
full avg10=2.30 avg60=1.10 avg300=0.50

some 和 full 的区别#

  • some - 至少有一个任务因为内存压力而被阻塞
  • full - 所有非 idle 任务都在等待资源

avg#

avg 表示时间跨度,是“在过去的一段时间内”的意思,比如 avg10 就是在过去 10s 内的意思。因此,结合起来,some avg10=12.50 这段输出可以理解为 在过去 10 秒里,有 12.5% 的时间至少有一个任务因为内存问题被卡住。也就是说,在这 10 秒中,有 10 秒 × 12.5% = 1.25 秒处于有进程被卡住的状态。

PSI 目录#

  • /proc/pressure/cpu
  • /proc/pressure/memory
  • /proc/pressure/io
  • /proc/pressure/irq

systemd-oomd 的触发条件#

它主要根据三种情况杀进程

memory.high 持续超标 + PSI 高#

如果某个 cgroup:

  • memory.high (cgroups 内存软性限制,超过只会被限制和回收资源,不会直接触发 OOM) 被持续超过
  • PSI memory pressure 高
  • 持续一段时间

它就会选一个进程杀(依据 cgroups oom_score_adj 和实时计算的oom_score

swap 用尽#

包括

  • swap 使用率过高
  • 系统接近swap exhaustion

此时会优先杀大内存进程

全局内存压力严重#

如果整机 memory pressure 很高,包括 PSI full超过阈值且 持续一定时间,会随机杀死 某个 service 来尝试解决内存压力。

服务使用的内存超过 memory.max 的情况#

当服务所在 cgroup 的 memory.max 被突破时,触发 OOM 的是内核(kernel),不是 systemd-oomd。

这是故意的设计,因为memory.max是内存硬限制, 必须由内核立即处理,一旦alloc_pages()失败,即刻触发。

systemd-oomd 与内核 OOM 的区别#

对比内核 OOMsystemd-oomd
运行位置内核态用户态
触发时机内存耗尽内存压力高
依据oom_scorePSI + cgroup
精细度较粗按 service 粒度
可控性

systemd-oomd 配置文件#

  • 主配置文件:/etc/systemd/oomd.conf
  • 默认模板:/usr/lib/systemd/oomd.conf
  • 查看当前生效配置:systemd-analyze cat-config systemd/oomd.conf

[OOM] SECTION OPTIONS#

  1. DefaultMemoryPressureLimit

当某个受管控的 cgroup 的 memory PSI 超过 指定的百分比时(比如 60%) 时,进入候选杀名单。这个百分比针对的是PSI some avg10,而不是内存使用率或 free memory 的百分比。

  1. DefaultMemoryPressureDurationSec

memory pressure 超过阈值持续一定时长才触发(比如 30s),这是一个抗抖动的机制,通过设定一定的冗余时间,允许进程偶尔超过设定的标准而不被杀死。

  1. SwapUsedLimit

如果 swap 使用率超过 指定的百分比(比如 90%),则可以触发 kill。

  1. 典型 systemd-oomd 配置
[OOM]
DefaultMemoryPressureLimit=60%
DefaultMemoryPressureDurationSec=30s
SwapUsedLimit=90%

如何让指定的服务启动 systemd-oomd#

需要修改服务的 service 文件,增加两个参数:

  • ManagedOOMMemoryPressure
  • ManagedOOMSwap

都有三种可选值:

含义
auto默认行为
kill允许被 oomd 杀
none禁止被 oomd 杀

比如:

[Service]
ManagedOOMMemoryPressure=kill
ManagedOOMSwap=kill

和 memory.high 的关系#

systemd-oomd 只会重点关注设置了 memory.high 的 cgroup或 memory pressure 显著的 cgroup;因此如果一个服务没有设置 memory.high,PSI 符合配置文件的定义时,仍可能触发 systemd-oomd,但优先级通常低一些。

为整个 slice 启用配置#

例如,要为所有 user.slice 下的服务启用配置

  1. 创建 drop-in 目录:/etc/systemd/system/user.slice.d/
  2. 创建配置文件:/etc/systemd/system/user.slice.d/oom.conf,写入配置,例如
[Slice]
ManagedOOMMemoryPressure=kill
  1. 重载 systemd 配置 systemctl daemon-reload
  2. 确认配置:systemctl show user.slice

创建独立的 slice 并将服务纳入该 slice 以实现资源组控制#

以创建一个 web slice,并为该 slice 下的 nginx 和 node 服务统一设置内存和 cpu 限制

创建 web.slice#

  1. 创建配置文件:/etc/systemd/system/web.slice
  2. 写入配置
[Unit]
Description=Web Services Slice
[Slice]
CPUAccounting=yes
MemoryAccounting=yes
MemoryHigh=2G
CPUQuota=100%
ManagedOOMMemoryPressure=kill

其中CPUAccountingMemoryAccounting 是用来启用资源限制的参数,同时也为 systemd-cgtop 提供了根据 cgroups slice 分组的资源可视化

ManagedOOMMemoryPressure配置为 kill,这意味着当 slice 组的资源使用量之和超过声明的量时,systemd-oomd 会生效,从占用资源最多的进程开始杀。

  1. 生效配置:systemctl daemon-reload

让 nginx.service 使用 web.slice (使用 drop-in 方式)#

  1. 创建 drop-in 目录:/etc/systemd/system/nginx.service.d/
  2. 创建配置文件:/etc/systemd/system/nginx.service.d/slice.conf
  3. 在配置文件中声明 nginx 服务所归属的 slice
[Service]
Slice=web.slice
  1. 应用配置

创建 node 应用并配置 slice(在主服务文件中显式声明)#

  1. node app 代码
const http = require('http');
const server = http.createServer((req, res) => {
res.writeHead(200, { 'Content-Type': 'text/plain' });
res.end('Hello World\n');
});
server.listen(8000, () => {
console.log('Server running at http://localhost:8000/');
});
  1. 创建 service 服务文件
[Unit]
Description=Node.js App on Port 8000
After=network.target
[Service]
Slice=web.slice # 在这里显式声明所属的slice
Type=simple
User=root
WorkingDirectory=/opt/node-app
ExecStart=/root/.nvm/versions/node/v20.19.0/bin/node /opt/node-app/app.js
Restart=on-failure
RestartSec=10
StandardOutput=journal
StandardError=journal
Environment=NODE_ENV=testing
[Install]
WantedBy=multi-user.target
  1. 运行服务

验证#

  1. 使用 systemd-cgls命令
[root@exp-bridge system]# systemd-cgls -u web.slice # 直接查看 slice 的完整内容
Unit web.slice (/web.slice):
├─nginx.service
│ ├─1750 nginx: master process /usr/sbin/nginx
│ ├─1751 nginx: worker process
│ ├─1752 nginx: worker process
│ ├─1753 nginx: worker process
│ └─1754 nginx: worker process
└─node-app.service
└─3103 /root/.nvm/versions/node/v20.19.0/bin/node /opt/node-app/app.js
  1. 使用 systemd-cgtop 查看 slice 的资源利用率
CGroup Tasks %CPU Memory Input/s Output/s
/web.slice 12 - 12.8M - -
/web.slice/nginx.service 5 - 5.8M - -
/web.slice/node-app.service 7 - 6.9M - -

支持与分享

如果这篇文章对你有帮助,欢迎分享给更多人或赞助支持!

赞助
systemd-oomd 在 cgroup v2 下的触发机制与服务级 OOM 控制实践
https://white-festa.net/posts/systemd-oomd/
作者
常世常暗
发布于
2026-02-09
许可协议
CC BY-NC-SA 4.0

评论区

Profile Image of the Author
常世常暗
If you shout loud enough, you'll be the one.
公告 - 2025/12/21
本站框架已由Ghost迁移到Astro!
音乐
封面

音乐

暂未播放

0:00 0:00
暂无歌词
分类
标签
站点统计
文章
60
分类
11
标签
36
总字数
115,331
运行时长
0
最后活动
0 天前

目录