Кто убил мой сайт? Разбираемся с OOM Killer в Linux
Введение
Иногда владельцы VPS сталкиваются со странной ситуацией:
-
сайт внезапно перестаёт открываться;
-
база данных отключается;
-
панель управления становится недоступна;
-
сервер начинает сильно тормозить;
-
отдельные приложения неожиданно завершаются.
При этом сам VPS продолжает работать и даже отвечает по SSH.
Во многих случаях причиной оказывается OOM Killer (Out Of Memory Killer) - это встроенный механизм ядра Linux, который автоматически завершает процессы при критической нехватке оперативной памяти.
Зачем нужен OOM Killer
Когда системе перестаёт хватать памяти, она оказывается в опасном состоянии.
Если не принять меры:
-
новые процессы не смогут запускаться;
-
приложения начнут зависать;
-
ядро может потерять стабильность;
-
сервер станет полностью недоступен.
Чтобы избежать полного зависания системы, Linux выбирает один или несколько процессов и принудительно завершает их для освобождения памяти. Именно этим занимается OOM Killer.
Проверяем наличие событий OOM
Если сервер не отвечает на запросы SSH, то можно подключится по VNC/IPMI и посмотреть, что происходит на экране. Если сработка была, то на экране будет предупреждение:
Если доступ к SSH есть, но все жутко тормозит, то смотрим логи.
Для современных систем
journalctl -k | grep -i oom
Либо:
journalctl -k | grep -i "killed process"
Пример вывода:
Это сообщение из лога ядра Linux означает, что OOM Killer сработал и принудительно завершил процесс, потому что системе не хватало оперативной памяти:
-
Out of memory - ядро обнаружило критическую нехватку памяти.
-
Killed process 1005 - уничтожен процесс с PID 1005.
-
(stress-ng-vm) - имя процесса. Это стандартный инструмент для стресс-тестирования памяти (часть пакета
stress-ng). Он специально потребляет память, чтобы спровоцировать OOM.
Для старых систем
dmesg | grep -i oom
или
dmesg | grep -i "killed process"
Сколько памяти доступно на сервере
Проверяем текущее состояние памяти:
free -h
Пример:
Расшифруем показания на скриншоте выше:
-
free (638 MiB) - память, которая вообще не используется и доступна немедленно.
-
available (612 MiB) - реально доступная для новых приложений память с учётом того, что часть
buff/cache(123 MiB) может быть освобождена при необходимости. -
Swap = 0 - подкачка отключена. Это значит, что при исчерпании RAM система вынуждена будет немедленно убивать процессы через OOM Killer.
Почему available чуть меньше free?
Потому что в free не входит shared (35 MiB) и часть кэша, которая может быть занята активно используемыми страницами. В целом можно ориентироваться на available - это самый точный показатель.
Какие процессы потребляют больше всего памяти
Удобный способ:
top
или
htop
Если htop отсутствует:
apt install htop
Сортировка по памяти позволяет быстро определить наиболее «тяжёлые» процессы. Подробнее про htop можно прочитать у нас в статье.
Часто виновниками становятся:
-
Java-приложения;
-
Docker-контейнеры;
-
MySQL/MariaDB;
-
PostgreSQL;
-
PHP-FPM;
-
Elasticsearch;
-
Redis;
-
Minecraft-серверы.
Просмотр процессов по потреблению памяти
Выполните команду:
ps aux --sort=-%mem | head -20
или
ps aux --sort=-%mem | head -20 | awk 'NR==1 {print; next} {printf "%s %s %s %s %.1fM %.1fM %s %s %s %s %s\n", $1,$2,$3,$4,$5/1024,$6/1024,$7,$8,$9,$10,$11}'
Пример вывода:
Первое, что бросается в глаза, - все процессы, которые больше всего нагружают оперативную память, относятся к утилите стресс-тестирования stress-ng. Это нормально, потому что мы её специально запустили, чтобы проверить, как работает механизм OOM Killer.
Самые прожорливые процессы - это четыре копии stress-ng-vm с пометкой [run]. У каждого из них значение RSS, то есть реально занятая физическая память, составляет примерно 29 мегабайт. Процент памяти, который они занимают от общего объёма, - 2,9 процента. Учитывая, что вся система имеет 967 мегабайт ОЗУ, это не так много, но эти процессы постоянно работают и могут увеличивать своё потребление.
Чуть ниже идут два управляющих процесса stress-ng, которые запускали дочерние потоки. Первый был запущен с параметром --vm 4 --vm-bytes 90% --vm-keep, то есть он пытается занять 90 процентов доступной памяти, создавая четыре рабочих потока. Второй управляющий процесс ещё агрессивнее: он был запущен с параметром --vm 1 --vm-bytes 120% – это означает, что он пытается выделить памяти на 20 процентов больше, чем есть в системе. Такая команда гарантированно вызовет нехватку памяти и срабатывание OOM Killer. Оба управляющих процесса потребляют по 27–28 мегабайт RSS.
Далее идут обычные системные процессы. Процесс /sbin/init (PID 1) занимает около 14 мегабайт - это нормально для современного systemd. Затем службы sshd, systemd-journald (системный журнал), systemd-udevd (управление устройствами) и systemd-logind – они потребляют от 9 до 12 мегабайт каждая. Все эти процессы имеют статус Ss (спящий, но с лидером сессии) или Ssl, что означает, что они работают в фоне и не мешают.
В конце списка находятся ещё несколько процессов stress-ng-vm с пометкой [wait]. Они имеют статус T, то есть остановлены. Их память гораздо меньше - около 5 мегабайт RSS. Это, скорее всего, дочерние процессы, которые уже выполнили свою работу и ждут, когда родительский процесс завершит тест.
Обратите внимание на статус TL у управляющих процессов stress-ng. Буква T означает, что процесс был остановлен (возможно, после завершения теста), а буква L – что он заблокировал некоторые страницы памяти, то есть удерживает их даже в остановленном состоянии. Поэтому эти процессы продолжают висеть в памяти и не отдают её системе.
В целом, этот вывод наглядно показывает, как утилита stress-ng создаёт искусственную нагрузку на память. Четыре активных рабочих процесса, два управляющих с агрессивными настройками и ещё несколько ожидающих – всё вместе они занимают около 180–200 мегабайт оперативной памяти. Этого недостаточно, чтобы полностью исчерпать ресурсы системы, но если процессы начнут расширяться (а параметр --vm-bytes 120% именно это и делает), система быстро упрётся в нехватку памяти и запустит OOM Killer. Именно это и произошло в логе, который мы видели ранее: ядро убило один из таких процессов, чтобы спасти систему от зависания.
Проверяем использование swap
swapon --show
или
free -h
Если swap заполнен почти полностью, это говорит о постоянном дефиците оперативной памяти.
Почему возникает OOM Killer
Недостаточный объём RAM
Самая частая причина. Например:
-
VPS имеет 1 ГБ памяти;
-
работают MySQL, PHP-FPM и панель управления;
-
нагрузка увеличивается.
В результате памяти перестаёт хватать.
Утечка памяти (Memory Leak)
Некоторые программы могут постепенно увеличивать потребление памяти.
Симптомы:
-
после перезапуска всё работает нормально;
-
через несколько часов или дней память снова заканчивается.
Неправильные настройки приложений
Например:
MySQL:
innodb_buffer_pool_size=2G
на VPS с 2 ГБ RAM.
В таком случае база данных фактически занимает всю доступную память.
Docker-контейнеры без ограничений
По умолчанию контейнер может использовать практически всю память хоста. Рекомендуется задавать лимиты:
docker run --memory=512m
или в Docker Compose:
mem_limit: 512m
Большое количество PHP-процессов
Например:
pm.max_children = 100
Каждый процесс PHP-FPM может потреблять десятки мегабайт памяти. В результате сервер быстро достигает лимита.
Можно ли отключить OOM Killer
Технически это возможно. Однако делать этого не рекомендуется.
Если полностью отключить механизм защиты, сервер при нехватке памяти может зависнуть целиком и перестать отвечать даже по SSH. В большинстве случаев лучше найти причину нехватки памяти и устранить её.
Как снизить вероятность срабатывания OOM Killer
1) Изменение oom_score_adj
Что такое oom_score_adj?
Это числовой параметр процесса (от -1000 до 1000), который влияет на выбор жертвы OOM Killer. Отрицательные значения уменьшают шанс быть убитым, положительные - увеличивают. Например, -500 делает процесс менее приоритетным для завершения, +500 - более приоритетным.
Как «включить» (настроить)?
Для работающего процесса с PID 1234 выполните (от root):
echo -500 > /proc/1234/oom_score_adj
Чтобы запустить новый процесс с нужным значением, используйте утилиту choom:
choom -n 500 ./myprogram
2) Добавить swap
Если swap отсутствует, то поочередно выполните команды:
fallocate -l 2G /swapfile
chmod 600 /swapfile
mkswap /swapfile
swapon /swapfile
Проверка:
swapon --show
Важно понимать, что swap не заменяет оперативную память, а лишь помогает переживать кратковременные пики нагрузки.
3) Ограничить приложения
Например:
-
уменьшить количество PHP-FPM процессов;
-
ограничить Docker-контейнеры;
-
скорректировать настройки баз данных;
-
уменьшить размер Java Heap.
4) Следить за памятью
Полезные команды:
free -h
htop
vmstat 1
Регулярный мониторинг помогает обнаружить проблему до возникновения аварийной ситуации.
5) Увеличить объём RAM
Если сервер постоянно работает на пределе памяти, оптимизация поможет лишь временно. В этом случае наиболее правильным решением будет переход на тариф с большим объёмом оперативной памяти.
Заключение
Мы на практике разобрали, как работает OOM Killer в Linux - механизм, который спасает систему от полного зависания, когда оперативная память и подкачка полностью исчерпаны. На примере утилиты stress-ng мы намеренно создали ситуацию нехватки памяти, увидели, как ядро выбирает «жертву» и завершает процесс, освобождая ресурсы, а также рассмотрели практические способы предотвращения неконтролируемого срабатывания OOM Killer - от настройки swap и контроля потребления памяти до изменения коэффициента oom_score_adj для критически важных процессов.







