Что такое OOM Killer и почему он появляется
OOM Killer (Out-of-Memory Killer) — это механизм ядра Linux, который автоматически завершает процессы, когда система исчерпала доступную оперативную память (RAM) и swap-пространство. Его цель — освободить память, чтобы ядро и критические системные процессы могли продолжить работу, предотвращая полный крах системы.
Обычно OOM Killer активируется, когда:
- Физическая RAM и swap заполнены на 100%.
- Приложение имеет утечку памяти (memory leak).
- На сервере запущено слишком много memory-intensive процессов.
- Неправильно настроены лимиты памяти в контейнерах (Docker/Kubernetes).
Если вы видите в логах сообщение Killed process или приложение внезапно завершается с кодом 137 (SIGKILL), скорее всего, виноват OOM Killer.
Как работает OOM Killer
Ядро Linux вычисляет oom_score для каждого процесса на основе:
- Доли памяти, потребляемой процессом (основной фактор).
- Привилегий процесса (процессы root имеют меньший шанс быть убитыми).
- Времени жизни процесса (долгоживущие процессы могут иметь более высокий score).
Процесс с самым высоким oom_score выбирается для завершения. Однако это не всегда оптимально: OOM Killer может убить важный сервис, а оставить фоновый процесс с утечкой.
Диагностика проблемы
Перед любыми действиями нужно подтвердить, что проблема именно в OOM Killer.
- Проверьте логи ядра:
dmesg | grep -i kill
Пример вывода:[12345.678] Out of memory: Kill process 1234 (nginx) score 500 or sacrifice child [12345.680] Killed process 1234 (nginx) total-vm:1234567kB, anon-rss:456789kB, file-rss:0kB
Здесь видно, что процессnginxс PID 1234 был убит. - Оцените общую память:
free -h
Обратите внимание на столбцыtotal,used,availableиSwap. Еслиavailableблизок к нулю, аSwapтакже заполнен — система в критическом состоянии. - Найдите процесс-потребителя:
top -b -n 1 | head -20
Или используйтеhtopс сортировкой по памяти (клавишаF6→MEM%). - Проверьте oom_score процессов:
for pid in $(ps -e | awk '{print $1}' | tail -n +2); do echo "PID $pid: $(cat /proc/$pid/oom_score 2>/dev/null) (adj: $(cat /proc/$pid/oom_score_adj 2>/dev/null))" done | sort -k3 -n -r | head -10
Это покажет топ-10 процессов с наивысшимoom_score.
Методы решения проблемы
Шаг 1: Настройка oom_score_adj для защиты ключевых процессов
Каждому процессу можно задать adj-значение от -1000 (максимальная защита) до +1000 (максимальный приоритет на убийство). Это самый быстрый способ защитить процесс.
Для одноразовой настройки (до перезагрузки):
# Замените <PID> на идентификатор процесса
echo -1000 > /proc/<PID>/oom_score_adj
Для постоянной настройки через systemd (рекомендуется): Создайте или отредактируйте юнит:
# /etc/systemd/system/ваш-сервис.service.d/oom-protect.conf
[Service]
OOMScoreAdjust=-1000
Затем перезапустите сервис: systemctl daemon-reload && systemctl restart ваш-сервис.
Важно: Не устанавливайте oom_score_adj=-1000 для всех процессов — это может привести к тому, что OOM Killer не сможет освободить память и система зависнет.
Шаг 2: Использование cgroups для ограничения памяти
cgroups (control groups) позволяют задать жёсткие лимиты памяти для группы процессов. Это лучший способ для контейнеров и изолированных сервисов.
Через systemd (современные дистрибутивы):
# Запустить команду с лимитом 500 МБ
systemd-run --scope -p MemoryMax=500M /путь/к/команде
# Или для существующего сервиса создайте дроп-ин:
# /etc/systemd/system/ваш-сервис.service.d/limits.conf
[Service]
MemoryMax=1G
MemorySwapMax=2G # если нужен swap
Вручную через cgroup v2:
# Создайте cgroup
sudo mkdir /sys/fs/cgroup/mylimit
# Установите лимит 1 ГБ
echo $((1*1024*1024*1024)) | sudo tee /sys/fs/cgroup/mylimit/memory.max
# Запустите процесс в этой группе
sudo echo $$ > /sys/fs/cgroup/mylimit/cgroup.procs && /путь/к/вашему/приложению
Шаг 3: Настройка параметров ядра
Изменение поведения OOM Killer на уровне ядра.
Вариант A: Запретить переcommit памяти (строгий контроль):
В /etc/sysctl.conf добавьте:
vm.overcommit_memory = 2
vm.overcommit_ratio = 100 # разрешить commit только до 100% RAM+swap
Примените: sudo sysctl -p. Это предотвратит выделение памяти, которой физически нет, но может вызвать ошибки fork: Cannot allocate memory у приложений.
Вариант B: Режим паники вместо убийства (для отладки):
vm.panic_on_oom = 2
При нехватке памяти система упадёт в kernel panic, что полезно для сбора дампов, но неприемлемо для продакшена.
Вариант C: Изменить степень агрессивности OOM Killer (редко используется):
vm.oom_kill_allocating_task = 1 # убивать процесс, который выделил память, а не случайный
Шаг 4: Оптимизация приложения или увеличение ресурсов
Если проблема вызвана утечкой памяти:
- Используйте профилировщики:
valgrind --leak-check=full,heaptrack,perf. - Для Java-приложений: настройте
-Xmxи-Xmsв JVM. - Для Python: проверьте на утечки (например, через
tracemalloc).
Если нагрузка обоснована:
- Увеличьте объём RAM на сервере.
- Добавьте swap-файл (временное решение, но не панацея):
sudo fallocate -l 4G /swapfile sudo chmod 600 /swapfile sudo mkswap /swapfile sudo swapon /swapfile
Профилактика OOM Killer
- Мониторинг памяти:
- Используйте
Prometheus + node_exporterилиZabbix. - Настройте алерты при использовании RAM > 80%.
- Пример команды для быстрой проверки:
awk '/MemAvailable/ {print $2/1024" GB available"}' /proc/meminfo.
- Используйте
- Логирование использования памяти:
# Запись каждые 5 минут в cron */5 * * * * /usr/bin/free -h >> /var/log/memory.log - Регулярный аудит процессов:
- Ищите процессы с аномально высоким
oom_score. - Проверяйте контейнеры на соответствие лимитам.
- Ищите процессы с аномально высоким
Особенности в контейнерах (Docker/Kubernetes)
В контейнерах OOM Killer работает внутри изоляции cgroups, но если контейнер исчерпает свой лимит, ядро убьёт процесс внутри него.
Docker:
# Запуск с лимитом 512 МБ RAM и 1 ГБ swap
docker run -d --memory=512m --memory-swap=1g ваш-образ
# Проверка лимитов
docker stats
Kubernetes:
resources:
limits:
memory: "512Mi"
cpu: "500m"
requests:
memory: "256Mi"
Убедитесь, что requests и limits установлены адекватно. При превышении лимита поды будет убит (OOMKilled).
Частые ошибки при настройке
- Защита всех процессов через
oom_score_adj=-1000: Это отключает OOM Killer полностью, что может привести к полной блокировке системы при нехватке памяти. - Установка слишком высоких лимитов cgroups: Если лимит выше, чем физическая RAM, OOM Killer всё равно сработает на уровне хоста.
- Игнорирование swap: Swap замедляет систему, но может дать время на реакцию. Полный отказ от swap (
swapoff -a) ускорит срабатывание OOM Killer. - Неправильное толкование логов: Сообщение
Killed processможет быть и от ручногоkill -9. Всегда проверяйтеdmesgи код выхода процесса (137 = SIGKILL, часто от OOM).
Что дальше?
После применения мер проверьте:
- Стабильность работы под нагрузкой (тесты с
stress-ngили реальной нагрузкой). - Логи на отсутствие новых записей OOM Killer.
- Корректность работы защищённых процессов (не потребляют ли они слишком много памяти в ущерб другим).
Если проблема остаётся, рассмотрите архитектурные изменения: шардирование, кэширование, использование более эффективных алгоритмов обработки данных.
Запомните: OOM Killer — это последний рубеж защиты системы. Лучшая стратегия — не допускать ситуаций, когда он срабатывает, через мониторинг и грамотное планирование ресурсов.