Если на сервере без видимых причин «упала» база данных, Java-приложение или веб-сервер, а в журнале самого приложения нет ошибок, первым делом проверьте, не завершил ли его OOM Killer. Ниже: как он работает, как найти его следы и как защитить важный сервис.
Что такое OOM Killer и как он работает
OOM Killer (Out Of Memory Killer) — механизм ядра Linux, который срабатывает, когда оперативная память и swap закончились и ядру больше неоткуда взять память. Чтобы система не зависла целиком, ядро выбирает один процесс и принудительно завершает его сигналом SIGKILL. Корректно завершиться процесс не успевает, поэтому в его собственных журналах обычно ничего нет.
Кого завершить, ядро решает по «очкам» oom_score от 0 до 1000: чем больше памяти занимает процесс, тем выше его очки и тем вероятнее, что выберут именно его. Поправку к очкам задаёт параметр oom_score_adj от −1000 до 1000. Значение −1000 полностью исключает процесс из кандидатов.
Как узнать, какой процесс был убит
Ядро записывает каждое срабатывание в свой журнал строкой вида Out of memory: Killed process 1234 (java). Ищем её в журнале ядра, ключ -T показывает время в обычном виде:
dmesg -T | grep -i 'killed process'
Журнал ядра хранится в памяти и очищается при перезагрузке. Если сервер уже перезагружали, ищите в системных журналах. В RHEL, CentOS и Astra Linux это обычно /var/log/messages, в Debian и Ubuntu — /var/log/syslog:
grep -i 'killed process' /var/log/messages
grep -i 'killed process' /var/log/syslog
В системах, где журналы ведёт только systemd-journald, то же самое можно найти так:
journalctl -k | grep -i 'killed process'
Какой процесс будет следующим
Утилита dstat показывает процесс с самыми высокими очками OOM в реальном времени. В новых дистрибутивах её может не быть: её заменили на dool или pcp-dstat.
dstat --top-oom
Очки конкретного процесса, вместо PID подставьте номер процесса:
cat /proc/PID/oom_score
Список из десяти процессов с самыми высокими очками без дополнительных утилит:
for p in /proc/[0-9]*; do printf '%s %s\n' "$(cat $p/oom_score 2>/dev/null)" "$(cat $p/comm 2>/dev/null)"; done | sort -rn | head
Как защитить важный сервис
Для службы systemd задайте OOMScoreAdjust в разделе [Service]. Удобнее всего через дополнительный файл настроек, чтобы изменение не пропало при обновлении пакета:
systemctl edit имя_сервиса.service
В открывшемся редакторе добавьте:
[Service]
OOMScoreAdjust=-1000
Сохраните файл и перезапустите службу. Проверить, что значение применилось:
systemctl restart имя_сервиса.service
cat /proc/$(systemctl show -p MainPID --value имя_сервиса.service)/oom_score_adj
Не ставьте −1000 всем подряд. Если исключить из кандидатов все крупные процессы, ядру останется завершать мелкие системные службы, а в крайнем случае система зависнет. Защищайте одну-две ключевые службы, например базу данных.
Что делать, чтобы OOM Killer не срабатывал
- Найти, кто съедает память:
topс сортировкой по памяти (клавиша M) илиps aux --sort=-rss | head. - Ограничить память приложению в его настройках: например, размер кучи Java (
-Xmx) илиshared_buffersиwork_memв PostgreSQL. - Ограничить службу средствами systemd параметром
MemoryMax=, чтобы она не забирала память у остальных. - Добавить swap как страховку от кратковременных пиков или увеличить оперативную память сервера.