Что означает ошибка OOM Killer
OOM Killer (Out-Of-Memory Killer) — это механизм ядра Linux, который автоматически принудительно завершает один или несколько процессов, когда система полностью исчерпала оперативную память (RAM) и подкачку (swap). Это не ошибка в традиционном смысле, а аварийная мера безопасности, предотвращающая полный крах системы.
Типичное сообщение в логах (dmesg или journalctl -k):
[12345.678] Out of memory: Kill process 1234 (python3) score 500 or sacrifice child
[12345.679] Killed process 1234 (python3) total-vm:204800kB, anon-rss:150000kB, file-rss:0kB
Симптомы для пользователя:
- Неожиданное завершение приложений или служб.
- Сообщения вроде
Killedв консоли при запуске команд. - Резкий рост использования swap (видно в
free -h). - Система становится неотзывчивой, затем "оживает" после убийства процесса.
Причины возникновения
- Физическая нехватка RAM + swap. Запущено слишком много тяжелых приложений (виртуальные машины, базы данных, компиляция), которые в сумме требуют больше памяти, чем доступно.
- Утечки памяти в приложениях. Программа (например, на Python, Java, Node.js) постепенно потребляет всё больше RAM, не освобождая неиспользуемые объекты.
- Недостаточный swap-раздел или swap-файл. На системах с малым объёмом RAM (например, Raspberry Pi или облачные инстансы) swap может быть слишком мал или отсутствовать.
- Неправильные настройки overcommit. Параметр
vm.overcommit_memory=0(по умолчанию) позволяет процессам запрашивать больше памяти, чем есть физически, что может привести к внезапному исчерпанию. - Контейнеры Docker без лимитов. Контейнеры по умолчанию могут использовать всю память хоста. Если один контейнер "съедает" всё, OOM Killer убьёт процессы в других контейнерах или на хосте.
- Фоновые службы с утечками. Долгоживущие демоны (например, веб-серверы, агенты мониторинга) могут накапливать данные в памяти без очистки.
Способы решения
Способ 1: Анализ логов и идентификация "потребителя"
Первым делом нужно понять, какой процесс и почему был убит.
- Найдите запись в логах ядра:
dmesg -T | grep -i "killed process" # или journalctl -k --since "1 hour ago" | grep -i oom
В выводе будет PID и имя процесса (например,python3,java,mysqld). - Проверьте, какие процессы сейчас используют больше всего памяти:
# Сортировка по использованию RAM (в %) top -b -n 1 | sort -k10 -rn | head -20 # Или более наглядный htop (если установлен) htop - Если процесс уже завершён, посмотрите историю его потребления (если есть мониторинг) или логи приложения на предмет утечек.
💡 Совет: Для долгосрочного мониторинга настройте Grafana + Prometheus или используйте утилиты вроде
netdata, чтобы видеть графики использования памяти.
Способ 2: Временное увеличение swap-пространства
Если у вас мало swap, это первая и быстрая мера (особенно на VPS или Raspberry Pi).
- Проверьте текущий swap:
free -h swapon --show - Создайте swap-файл (например, 4 ГБ):
sudo fallocate -l 4G /swapfile sudo chmod 600 /swapfile sudo mkswap /swapfile sudo swapon /swapfile - Сделайте его постоянным (добавьте в
/etc/fstab):echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab - Настройте
vm.swappiness(опционально). Значение 10-30 рекомендуется для серверов:sudo sysctl vm.swappiness=10 echo 'vm.swappiness=10' | sudo tee -a /etc/sysctl.conf
Способ 3: Настройка параметров overcommit
Ядро Linux по умолчанию (vm.overcommit_memory=0) разрешает процессам запрашивать больше памяти, чем есть физически + swap. Это может привести к ситуации, когда OOM Killer сработает резко.
- Установите строгий режим (
vm.overcommit_memory=2), который запрещает overcommit сверх лимита:sudo sysctl vm.overcommit_memory=2 - Установите процент доступной памяти для overcommit (
vm.overcommit_ratio). Например, 80% от суммы RAM + swap:# Предположим, RAM=4G, swap=4G, всего 8G. 80% = 6.4G sudo sysctl vm.overcommit_ratio=80 - Сделайте настройки постоянными:
echo 'vm.overcommit_memory=2' | sudo tee -a /etc/sysctl.conf echo 'vm.overcommit_ratio=80' | sudo tee -a /etc/sysctl.conf
⚠️ Важно: Слишком низкий
overcommit_ratioможет привести к ошибкамCannot allocate memoryу приложений, которые запрашивают память, но не используют её сразу (например, JVM). Тестируйте в staging-среде.
Способ 4: Оптимизация конкретных приложений
Для Java-приложений (Tomcat, Spring Boot, Kafka)
Уменьшите максимальный размер кучи (-Xmx). Например, вместо -Xmx4g поставьте -Xmx2g, если процессу хватает.
# Пример systemd-юнита для Java-сервиса
[Service]
Environment="JAVA_OPTS=-Xmx2g -XX:+UseG1GC"
ExecStart=/usr/bin/java $JAVA_OPTS -jar app.jar
Для Python-приложений (Django, Flask)
- Используйте менеджеры процессов (Gunicorn, uWSGI) с ограничением рабочих процессов.
- Проверьте утечки через
tracemallocилиobjgraph. - Для long-running задач (Celery) настройте лимиты памяти в конфиге.
Для Node.js
Установите лимит через --max-old-space-size:
node --max-old-space-size=2048 server.js # 2 ГБ кучи
Способ 5: Настройка Docker-контейнеров
Если OOM Killer убивает процессы в контейнерах, нужно установить лимиты памяти для каждого контейнера.
- Запустите контейнер с явными лимитами:
docker run -d \ --name myapp \ --memory=2g \ --memory-swap=3g \ # swap = memory-swap - memory myimage:latest - Для docker-compose:
services: app: image: myapp:latest deploy: resources: limits: memory: 2G memory-swap: 3G - Проверьте лимиты запущенных контейнеров:
docker stats
Способ 6: Защита критичных процессов через oom_score_adj
OOM Killer вычисляет "оценку" (score) для каждого процесса (0-1000). Чем выше оценка, тем вероятнее убийство. Можно вручную понизить оценку для важных служб.
- Для уже запущенного процесса:
# Установить минимальный score (требуются права root) echo -1000 | sudo tee /proc/<PID>/oom_score_adj - Для systemd-службы (рекомендуется). Создайте дроп-ин в
/etc/systemd/system/<service>.d/oom.conf:[Service] OOMScoreAdjust=-900
Затем перезапустите службу:sudo systemctl daemon-reload && sudo systemctl restart <service>.
Профилактика
- Регулярный мониторинг. Настройте алерты (через Prometheus Alertmanager, Zabbix) на пороги использования памяти (например, 80% RAM + 70% swap).
- Аудит приложений на утечки. Для Java —
jcmd <PID> GC.heap_info, для Python —pip install memrayи профилирование. - Прогрессивное выделение swap. На серверах с большим RAM (>32 ГБ) swap может быть небольшим (4-8 ГБ), но он должен быть. На встраиваемых системах (Raspberry Pi) — 1-2 ГБ swap обязательны.
- Избегайте overcommit в production. Установите
vm.overcommit_memory=2иvm.overcommit_ratio=80на всех продакшн-серверах. - Лимитирование контейнеров. Всегда указывайте
--memoryи--memory-swapдля Docker-контейнеров в продакшене. - Тестирование нагрузкой. Используйте
stress-ngилиmemtesterдля проверки поведения системы при высокой нагрузке на память:stress-ng --vm 2 --vm-bytes 2G --timeout 60s