Как работает OOM-Killer
Out of Memory Killer (OOM Killer) — это механизм ядра Linux, который освобождает оперативную память при ее исчерпании за счет принудительного завершения некоторых запущенных процессов.
Очень полезная инфа, оригинал здесь — https://t.me/bashdays
Когда оперативная память в ос Linux заканчивается, происходят забавные вещи, ядро вызывает «кракена» OOM Killer. В свою очередь «кракен» прогоняет процесс по определенным правилам и убивает его.
Бытует ошибочное мнение, что OOM Killer берет самый жирный процесс и киляет его. Это не так. Создатели этого механизма знатно упоролись.
OOM Killer использует достаточно сложный анализ и присваивает каждому процессу очки негодности (badness). При исчерпании памяти, будет убит именно самый негодный процесс. Некий естественный отбор. Процесс убивается сигналом SIGKILL и не предоставляет приложению возможности корректно завершиться. Вызывается функция out_of_memory, а далее select_bad_process.
После убийства, в системном журнале появится строчка:
Out of Memory: Killed process 2256 (mysql) score 907 or sacrifice child
А это уже не шутки. Если к тебе стал приходить OOM Killer, значит в какой-то момент на сервере, что-то пошло не так и нужно разобраться.
Очки негодности могут принимать значение от -1000 до +1000. Соответственно чем выше это значение, тем вероятнее, что процесс будет убит. А процесс со значением -1000 будет по логике — бессмертным.
Посмотреть репутацию процесса, можно командой:
cat /proc/<PID>/oom_score
Где PID это идентификатор процесса, узнать можешь командой
ps ax|grep postfix
В результатах ты увидишь число, к примеру: 826, это и есть очки негодности.
Как вычисляются очки негодности?
За основу берется процент физической памяти используемой процессом и умножается на 10, таким образом получаем базовое значение. 1000 очков = 100%. После того к базовому значению начинают применяться различные модификаторы.
- Прибавляется половина очков от всех дочерних процессов, которые имеют собственную виртуальную память.
- Если приоритет процесса > 0, то очки умножаются на 2. Приоритет процесса может иметь значения от -20 до +20. В данном случае +20 это самый низкий приоритет.
- Очки делятся на коэффициент, связанный с процессорным временем, чем более активнее процесс и чем больше процессорного времени он использует, тем больше будет его коэффициент.
- Очки делятся на коэффициент, связанный с временем жизни процесса, чем больше времени прошло с момента запуска, тем выше будет это значение.
- Для процессов, запущенных от имени root, служебных учеток или процессов ввода-вывода очки делятся на 4.
- Для процесса, при выделении памяти у которого произошла ошибка out of memory и процессам имеющим с ним общую память, очки делятся на 8.
- Все что получилось выше умножается на 2^oom_adj. Где oom_adj это специальный коэффициент, имеющий значения от -16 до +15.
TL;DR Если подытожить, то OOM Killer убьет самый «жирный» процесс, который наименее активен в системе и имеет самое короткое время жизни.
Как обуздать OOM Killer? 🔪
Можно вообще его отключить! Но по понятным причинам, это все равно что отключить жизненно важный орган у человека. Какое-то время он еще проживет, но потом обязательно сдохнет в kernel panic.
Отключается командой:
sudo echo 1 > /proc/sys/vm/panic_on_oom
По умолчанию 0 (то есть включено). Я ни разу не встречал, чтобы кто-то отключил этот функционал, но достаточно много раз сталкивался с забиваем костылей в сами процессы.
Можно повысить или понизить репутацию для процесса, добавив в файл oom_adj значение от -17 до +15. Для повышения репутации выполняем команду:
sudo echo -6 > /proc/<PID>/oom_adj
Не забываем подставить в PID, идентификатор нужного процесса.
Для отключения OOM Killer для определенного процесса, выставляем репутацию -17. Теперь этот процесс в «домике» и его никакая собака не посмеет тронуть.
sudo echo -17 > /proc/<PID>/oom_adj
Но если ты рестартанешь процесс, у него изменится PID. А репутация, которую ты ему уже накрутил, может примениться к совсем другому процессу таким же PID.
Так же можно динамически определять PID процесса и менять ему репутацию. Закинуть это в крон и прыгать от счастья:
pgrep -f “/usr/sbin/sshd” | while read PID; do echo -17 > /proc/$PID/oom_adj; done
Но правильнее задать очки в самом systemd, в юните для сервиса, этот параметр:
[Service]
OOMScoreAdjust=-500
Собственно это база. По опыту скажу так — если к тебе пришел OOM Killer, сервак можно смело перезагружать. Нихрена там уже не работает, несмотря на всякие очки и коэффициенты. Перезагрузиться будет намного эффективнее, чем пытаться поднять что-то. Поднял, а потом уже по горячим следам и логам, дебаж.