VPS 的 Linux OOM 杀手调整:实用指南
12 分钟阅读 - 2026年6月8日

调整 VPS 上的 Linux OOM 杀手,保护数据库和 SSH,使用 cgroups 封顶失控进程,阻止错误服务被杀死。
VPS 的 Linux OOM 杀手调优
当内存耗尽时,Linux OOM 杀手是内核的最后手段:它会挑选一个进程并将其终止,以维持系统运行。在内存紧张且无后备方案的 VPS 上,默认选择往往是错误的。 您的数据库进程被终止,而某个长期运行的后台进程却幸存下来,您只能费力去探究原因。本指南将介绍 OOM 杀手如何对进程进行评分,如何将评分机制向关键服务倾斜,以及如何利用 cgroups 确保单个失控进程不会拖垮整个系统。
OOM 杀手如何选择牺牲品
当内核无法通过页面缓存驱逐或交换机制回收足够的内存时,便会调用 OOM 杀手。每个进程都有一个 oom_score 0 到 1000 之间的分数,该分数主要基于其驻留集大小 (RSS) 和交换空间使用情况。得分最高的进程将收到 SIGKILL 信号。
RSS 在计算中占据主导地位,因此被终止的几乎总是内存消耗最大的进程。这通常是您的数据库、应用服务器,或是正在执行最重要工作的任何长生命周期进程。实际触发内存分配的进程(即“调用者”)极少会被终止。
有两种类型的 OOM 事件需要区分:
- 全局 OOM:主机(或整个 VPS)的 RAM 和交换空间已耗尽。内核会扫描所有进程,并终止得分最高的进程。
- cgroup OOM:某个特定的 cgroup 已达到其内存限制。内核仅在该 cgroup 内部终止进程,即使系统其余部分仍有可用内存。
如果您已配置 systemd 单元限制,或者正在运行容器,那么您遇到的 OOM 事件大多将是 cgroup OOM。这是件好事:影响范围被限制在特定范围内。
在系统崩溃前发现内存压力
OOM 事件几乎从未突然发生。通常会有一个压力逐渐加剧的窗口期,而监控的目标就是在这个窗口期内捕捉到它。
free -h 为您提供系统视图。关键的列是 available,而非 free:它代表可回收的页面缓存,反映了在不进行交换的情况下实际可分配的内存。保持 MemAvailable 该值大致维持在峰值负载的10%至15% MemTotal 。
若需按进程分析,请按 RSS 排序:
ps aux --sort=-%mem | head -10或者使用 htop 并按 RES。此处显示的数值会直接纳入内核的评分机制,因此排在前列的条目最有可能成为内存不足(OOM)的处理目标。
在 4.20 及更高版本内核中,压力停滞信息(Pressure Stall Information)是值得集成到监控中的早期预警系统:
cat /proc/pressure/memory该 some avg10 该数值表示过去十秒内,至少有一个任务因等待内存而陷入阻塞状态所占的时间百分比。低于 5% 则属正常。若该值持续高于 10%,则意味着系统正在耗费实际时间被内存回收操作阻塞,此时发生 OOM 杀进程的情况是可能的。
交换抖动会在 vmstat 1 中表现为非零 si 和 so 列中持续出现。少量驻留交换是无害的。但持续的交换进出则不然。
使用 oom_score_adj 保护关键进程
内核计算的评分可通过 oom_score_adj,评分范围从 -1000(免疫)到 +1000(优先终止)。该调整值将直接加到最终评分中。
若要对正在运行的进程进行一次性调整:
echo -500 | sudo tee /proc/$(pidof sshd)/oom_score_adj若需在重启后保持配置,请在 systemd 单元中设置。sshd、数据库以及任何您无法承受丢失的服务都应在此配置:
[Service]
OOMScoreAdjust=-900合理的默认起始值:
- sshd:-1000。若在内存危机期间丢失远程访问,恢复工作将变得极其困难。
- MySQL、PostgreSQL、Redis:-800 至 -900。在提供强力保护的同时,确保在真正灾难性情况下仍可对其进行干预。
- 应用程序工作进程、批处理任务、cron 任务:+100 至 +500。这些是您宁愿看到它们被终止,也不愿数据库受影响的进程。
切勿将所有进程的阈值都设为 -1000。如果没有任何进程可以被终止,内核最终会陷入恐慌或死机,这反而更糟。
使用 cgroups 和 systemd 限制内存
调整评分会影响哪些进程被终止。cgroups 则决定了全局终止操作是否会发生。通过为每个服务设置硬性上限,您可以将内存故障限制在单个 cgroup 内,而非让单个进程耗尽整个 VPS 的内存。
在 systemd 单元文件中:
[Service]
MemoryHigh=400M
MemoryMax=512M
OOMPolicy=stop
Restart=on-failure
RestartSec=5sMemoryHigh 是软限流:内核会在此阈值之上积极回收该 cgroup 的内存页,但不会终止任何进程。 MemoryMax 是硬性上限。若 cgroup 尝试分配超过该限额的内存,内核将终止该 cgroup 内的某个进程。通过 Restart=on-failure 该服务将立即自动重启。
在 cgroup v2 环境下(Ubuntu 22.04 及更高版本、近期 Debian 及 RHEL 9), memory.oom.group 会同时终止 cgroup 中的所有进程,而非留下孤儿进程。这对 PHP-FPM 进程池等多进程服务尤为有用,因为若仅终止部分进程,该组将出现异常行为。
以下是几个值得参考的应用程序特定注意事项:
- PHP-FPM:在
pm = ondemand在小型 VPS 实例上,并将pm.max_children时,应参照平均每个工作进程的 RSS 值,而非默认值。若在 2 GB 的 VPS 上将池大小设置为 4 GB 作为缓冲,则首次填满时就会触发 OOM 错误。 - Node.js:使用
--max-old-space-size=512(单位为 MB)。若不设置,Node 会持续增长直至内核介入。 - MySQL 和 PostgreSQL:
innodb_buffer_pool_size和shared_buffers应为操作系统页面缓存、连接内存以及服务器上的其他进程预留充足的余量。默认设置假设的是专用服务器。
在 OOM 事件后阅读日志
当 OOM 杀手触发时,内核会将一份详细报告写入环形缓冲区。可通过以下命令提取:
dmesg -T | grep -iE 'killed process|out of memory'
journalctl -k --grep='Out of memory'需要仔细阅读的日志段以“invoker”开头,以“victim”结尾。内核会打印完整的任务列表,其中包含每个进程的RSS、交换空间使用情况以及最终 oom_score_adj。有三点值得检查:
- 限制条件。
CONSTRAINT_NONE表示全局 OOM,CONSTRAINT_MEMCG表示 cgroup 触发了限制。这两种情况的解决方法不同。 Free swap。若出现此情况0kB,则表示内存和交换空间均已耗尽。可选择增加交换空间、提高MemoryMax该进程的优先级,或降低并发度。- 受影响进程的评分与其他所有进程的对比。如果受影响进程的评分仅比后几位进程高出一点点,说明您的
oom_score_adj设置的值未能有效发挥作用。请扩大其与其他进程的差距。
针对 cgroup 内存不足(OOM)的情况,kill 计数器位于 memory.events cgroup内部:
cat /sys/fs/cgroup/system.slice/mysql.service/memory.events计数器的上升 oom_kill 计数意味着该服务正在反复触及其限制。这表明应提高 MemoryMax、分析工作负载,或将服务迁移至更大配额的方案,而非陷入循环重启的死循环。
总结
调整 OOM 杀手并非为了将其彻底消除,而是为了在内存耗尽时控制哪个进程承担代价,并在此时缩小影响范围。生产环境中行之有效的模式是:
- 为无法承受损失的服务(尤其是 sshd 和数据库)设置 Score-protect 保护。
- 将其他所有服务通过
MemoryMax,这样单个进程失控只需重启一次,而非导致服务中断。 - 监控 PSI 并
MemAvailable,而非等待dmesg事后才通过报告得知情况。 - 预留 15% 至 20% 的内存作为缓冲空间。即使进行调优,也无法弥补 VPS 规格根本无法满足工作负载的情况。
如果内存压力是结构性的而非可通过配置解决的,您需要更多内存或更快的交换分区存储。FDC Servers 的 VPS 方案基于 AMD EPYC 处理器并配备 NVMe 存储,这能确保交换分区的读取速度足够快,从而避免短暂的内存突发导致进程被强制终止。

VPS 的 Linux OOM 杀手调整:实用指南
调整 VPS 上的 Linux OOM 杀手,保护数据库和 SSH,使用 cgroups 封顶失控进程,阻止错误服务被杀死。
12 分钟阅读 - 2026年6月8日
Linux 流量控制 (tc):实用指南
12 分钟阅读 - 2026年6月5日