Акция Панель управления ispmanager для VDS — первый месяц бесплатно
до 31.07.2026 Подробнее
Выберите продукт

systemd-oomd на VDS: приручаем OOM killer с PSI и приоритетами памяти

Когда VDS упирается в RAM, классический OOM killer срабатывает поздно и часто бьёт по критичным сервисам. systemd-oomd, опираясь на PSI и cgroup v2, реагирует заранее и завершает второстепенные юниты. Разберём включение, приоритеты, тесты и zram.
systemd-oomd на VDS: приручаем OOM killer с PSI и приоритетами памяти

Если вы хоть раз ловили внезапный пад сайта или крона из‑за нехватки ОЗУ на VDS, значит сработал классический oom killer ядра: он приходит в последний момент и безжалостно завершает «самый прожорливый» процесс. Сегодня в арсенале Linux есть более аккуратный инструмент — systemd-oomd. Он опирается на PSI (Pressure Stall Information) и cgroup v2, чтобы увидеть растущий «затвор» по памяти заранее и мягко освободить ресурсы, жертвуя наименее важными рабочими сервисами, а не базами данных и nginx.

OOM killer vs systemd-oomd: в чём разница

Классический OOM killer активируется, когда ядро уже не может удовлетворить запрос на выделение памяти. Он анализирует процессы по «стоимости» (badness) и убивает кого‑то быстро и без прелюдий. Это спасает хост, но часто ломает бизнес‑критичные штуки — БД, кеши, веб‑фронт.

systemd-oomd работает иначе:

  • следит за PSI (pressure) по памяти — сколько времени задачи простаивают, ожидая освобождения RAM;
  • применяет политики на уровне cgroup v2 (unit/slice), учитывая приоритеты сервисов;
  • вмешивается раньше ядра: ограничивает, стопорит или завершает менее важные единицы, чтобы защитить «ядро» приложения;
  • использует настройки MemoryMax=, MemoryLow=, MemoryMin=, ManagedOOMPreference= для выбора «жертв».

Идея простая: вместо случайного «минус кто‑то один» получаем управляемое «минус что‑то второстепенное» — очереди, воркеры, фоновые консьюмеры, а не база или фронтенд.

PSI и «давление по памяти»: что смотреть

PSI показывает, сколько времени система теряет в ожидании освобождения памяти. Метрика доступна в /proc/pressure/memory и делится на some и full:

  • some — часть задач ждёт память;
  • full — практически всё «стоит», это уже плохо.
cat /proc/pressure/memory

Чем выше средние значения за 10s/60s/300s, тем сильнее заторы. systemd-oomd использует пороги и длительность окна, чтобы среагировать до «чёрного экрана».

Вывод /proc/pressure/memory с метриками PSI some/full

Предпосылки и совместимость

  • cgroup v2 должен быть включён (современные Debian/Ubuntu, AlmaLinux/RHEL включают по умолчанию).
  • Нужны сборки systemd с поддержкой oomd (везде, где systemd 247+).
  • Память/свап учёт включён (по умолчанию на новых дистрибутивах).

Проверка и запуск systemd-oomd

Проверим статус и журналы:

systemctl status systemd-oomd
journalctl -u systemd-oomd -b

Если сервис не активен, включите и запустите:

systemctl enable --now systemd-oomd
Виртуальный хостинг FastFox
Виртуальный хостинг для сайтов
Универсальное решение для создания и размещения сайтов любой сложности в Интернете от 95₽ / мес

Базовые пороги: oomd.conf

Файл настроек /etc/systemd/oomd.conf позволяет задать общие пороги срабатывания по PSI и использованию свопа. Пример разумных стартовых значений для VDS с небольшим запасом ОЗУ:

# /etc/systemd/oomd.conf
[OOM]
# Порог memory pressure (PSI) в процентах, при котором начинаем действовать
DefaultMemoryPressureLimitPercent=60
# Минимальная длительность превышения порога (гистерезис)
DefaultMemoryPressureDurationSec=45s
# При почти полном свопе действуем решительнее
DefaultSwapUsedLimitPercent=95

После изменения перезапустите сервис:

systemctl restart systemd-oomd

Эти параметры заставляют oomd реагировать до падения ядра, когда задержки по памяти стали систематическими.

Расстановка приоритетов: MemoryMax, MemoryLow/Min, ManagedOOMPreference

Глобальные пороги — это только половина дела. Настоящая магия — в расстановке приоритетов внутри вашего стека, чтобы «мягко» освобождать ресурсы.

Ограничиваем прожорливых: MemoryMax

MemoryMax= жёстко ограничивает память юнита (cgroup). Пример drop-in для фонового воркера:

# systemctl edit my-worker.service
[Service]
MemoryAccounting=yes
MemoryMax=1G

С MemoryMax= воркер не сможет вытеснить из памяти критичные сервисы.

Защищаем критичное: MemoryLow и MemoryMin

MemoryLow= — мягкая защита: oomd старается не отбирать память ниже указанного объёма. MemoryMin= — жёсткая гарантия (на cgroup v2), ниже которой вытеснение не производится. Для веб‑фронта и БД это must‑have.

# systemctl edit nginx.service
[Service]
MemoryAccounting=yes
# Гарантируем nginx минимум 256 МБ под активные воркеры и кэш
MemoryLow=256M
# systemctl edit postgresql.service
[Service]
MemoryAccounting=yes
# Не даём никому вытеснить БД ниже безопасного минимума (например, 2 ГБ)
MemoryMin=2G

Подбирайте величины из профиля нагрузки и headroom VDS. Начинайте с консервативных значений и наблюдайте.

Кого «попросить выйти» первым: ManagedOOMPreference

ManagedOOMPreference= подсказывает oomd, кого он должен стараться избегать, а кого можно завершить первым при давлении. Для критичных сервисов — «избегать», для фоновых — «можно убивать».

# systemctl edit php-fpm.service
[Service]
ManagedOOMPreference=avoid
# systemctl edit queue-consumer.service
[Service]
ManagedOOMPreference=kill

Значения поддерживаются в новых версиях systemd. Проверьте свойства юнита:

systemctl show -p ManagedOOMPreference -p MemoryLow -p MemoryMin -p MemoryMax php-fpm.service

Работа со slice: user.slice и system.slice

Часто стоит ограничить интерактивные и пользовательские процессы, чтобы они не мешали продакшен‑сервисам:

# systemctl edit user.slice
[Slice]
MemoryAccounting=yes
MemoryMax=512M
ManagedOOMPreference=kill

А вот system.slice и ваши приложение‑слайсы можно защитить:

# systemctl edit system.slice
[Slice]
MemoryAccounting=yes
MemoryLow=512M

Структурируйте сервисы по слайсам: фронт, бэк, БД, воркеры — так oomd будет принимать решения на уровне групп, а не отдельных процессов. Подробно про разбиение на слайсы для PHP‑FPM и не только — в статье Организация cgroup‑срезов для сервисов.

Наблюдение и диагностика

  • Текущие значения PSI: cat /proc/pressure/memory.
  • Потребление и лимиты юнита: systemctl status, systemctl show -p MemoryCurrent -p MemoryMax <unit>.
  • Дерево cgroup: systemd-cgls.
  • Топ по cgroup: systemd-cgtop.
  • Журнал решений oomd: journalctl -u systemd-oomd -b.

Когда oomd действует, вы увидите в журнале, какие cgroup попали под раздачу, с какими метриками PSI и свопа.

Тестовый прогон: проверяем политику

Создадим управляемое давление и посмотрим, кого «выгонят» первым. Встаньте на стенд или staging.

apt-get update
apt-get install -y stress-ng

Запустите потребление памяти в отдельном unit, чтобы видеть поведение:

systemd-run --unit=mem-hog --property=MemoryAccounting=yes --property=ManagedOOMPreference=kill stress-ng --vm 2 --vm-bytes 70% --timeout 120s

Следим за журналом и PSI:

journalctl -u systemd-oomd -f
watch -n 1 cat /proc/pressure/memory

Если всё настроено корректно, oomd сначала завершит низкоприоритетные единицы (ManagedOOMPreference=kill) или ограниченные MemoryMax=, сохранив в живых критичные MemoryLow/Min.

zram: мягкая подушка перед жёстким OOM

Сжатый своп в RAM (zram) часто улучшает ситуацию на небольших VDS. Он не «добавляет» физической памяти, но позволяет пережить кратковременные пики с минимальными задержками.

Установка и базовая настройка zram-tools

apt-get update
apt-get install -y zram-tools

Файл настроек по умолчанию: /etc/default/zramswap. Пример:

# /etc/default/zramswap
ALGO=zstd
PERCENT=50
PRIORITY=100

Перезапустите службу:

systemctl restart zramswap

Проверьте:

swapon --show
free -h

Для эффективности zram можно чуть поднять vm.swappiness и снизить порог «сжигания» pagecache:

sysctl vm.swappiness=80
sysctl vm.vfs_cache_pressure=50

Закрепите значения в отдельном файле:

printf "vm.swappiness = 80\nvm.vfs_cache_pressure = 50\n" > /etc/sysctl.d/99-zram.conf
sysctl --system

Свяжите это с oomd: когда DefaultSwapUsedLimitPercent близко к 100% и PSI высок, oomd примет меры ещё до того, как ядро начнёт массово отстреливать процессы.

Настройка zram-tools и проверка swapon на сервере

Паттерны настройки для типичного веб‑стека

Nginx

  • MemoryLow= 128–256M, чтобы не гасить фронт под пиковой нагрузкой.
  • ManagedOOMPreference=avoid.

PHP-FPM или другой приложение‑рантайм

  • MemoryMax= на пул или сервис (например, 1–2G), с учётом количества воркеров.
  • ManagedOOMPreference=avoid для основного пула, а для второстепенных — kill.

Очереди/воркеры

База данных

  • MemoryMin= достаточно высокий, чтобы кэш не проседал до смертельных значений.
  • Разумные настройки собственной памяти БД (shared_buffers, innodb_buffer_pool_size) под общий бюджет VDS.

Отладка инцидентов: что смотреть после срабатывания

  • Журнал systemd-oomd — какие unit/slice были затронуты и почему.
  • События ядра в journalctl -k — не произошло ли классического OOM одновременно.
  • Потребление памяти по времени в мониторинге — как быстро росло давление.
  • Параметры unit: systemctl show -p MemoryCurrent -p MemoryLow -p MemoryMin -p MemoryMax <unit>.
  • Стек‑трейсы приложений — почему росла память (утечки, бурст загрузки, GC).

Контейнеры и Kubernetes

В контейнерах oomd тоже полезен, если вы запускаете их как systemd units. Однако в оркестраторах приоритеты лучше выражать через лимиты/requests и политики эвикции. На одиночных VDS с Docker/Podman без оркестратора удобно использовать systemd unit‑обёртки для контейнеров и те же MemoryMax, ManagedOOMPreference.

Чек‑лист внедрения на VDS

  1. Включите systemd-oomd, задайте пороги в /etc/systemd/oomd.conf.
  2. Разнесите сервисы по слайсам и выставьте бюджеты MemoryMax.
  3. Для критичных — MemoryLow/Min, для второстепенных — ManagedOOMPreference=kill.
  4. Подключите zram, настройте vm.swappiness.
  5. Проведите нагрузочный тест с stress-ng, посмотрите журналы.
  6. Доведите значения до баланса между устойчивостью и производительностью.

Ответы на частые вопросы

Если у меня включён swap на диске, нужен ли zram? На медленном диске — zram часто лучше: сжатие в RAM быстрее диска и даёт буфер. Но не заменяет планирование памяти, это лишь смягчение пиков.

Будет ли oomd убивать сервисы слишком агрессивно? Если завышены пороги PSI и свопа, вмешательство наступит поздно; если занижены — рано. Начните с умеренных значений и наблюдайте 1–2 недели.

Чем MemoryLow отличается от MemoryMin? MemoryLow — мягкая защита: oomd старается не опускаться ниже. MemoryMin — жёсткая гарантия на уровне cgroup v2, которая ограничивает вытеснение даже при сильном давлении.

Нужно ли настраивать что‑то на уровне ядра про OOM? Как правило, нет: oomd вмешивается раньше. Изредка полезно проверить vm.overcommit_memory и особенности вашего рантайма.

Итоги

systemd-oomd превращает хаос «последнего удара» от OOM killer в управляемую деградацию сервиса. С PSI вы видите приближение проблемы, а с MemoryMax, MemoryLow/Min и ManagedOOMPreference определяете, кто жизненно важен, а кого можно перезапустить или завершить. Добавьте к этому небольшую «подушку» в виде zram, и ваш VDS перестанет падать из‑за разового бурста памяти — вместо этого вы получите предсказуемое и контролируемое поведение.

Поделиться статьей

Вам будет интересно

Debian/Ubuntu: mount: wrong fs type, bad option, bad superblock — как быстро найти и исправить причину OpenAI Статья написана AI (GPT 5)

Debian/Ubuntu: mount: wrong fs type, bad option, bad superblock — как быстро найти и исправить причину

Ошибка mount: wrong fs type, bad option, bad superblock в Debian/Ubuntu может означать и простую опечатку в имени раздела, и пробл ...
Debian/Ubuntu: XFS metadata corruption и emergency read-only — пошаговое восстановление OpenAI Статья написана AI (GPT 5)

Debian/Ubuntu: XFS metadata corruption и emergency read-only — пошаговое восстановление

Если XFS-раздел внезапно стал доступен только для чтения, а сервер ушёл в emergency mode, главное — не спешить. Разберём безопасны ...
Debian/Ubuntu: как исправить Failed to fetch при apt update OpenAI Статья написана AI (GPT 5)

Debian/Ubuntu: как исправить Failed to fetch при apt update

Ошибка Failed to fetch при apt update в Debian и Ubuntu обычно связана не с самим APT, а с DNS, сетью, зеркалом, прокси, временем ...