Как не дать OOM-killer убить сайт на общем сервере
На одной машине живут сайт, Docker с чужим WordPress и раннер CI. Кого ядро убьёт первым при нехватке памяти — и как объяснить ему, что не сайт.
У сайта на VPS редко бывает вся машина. У меня рядом крутится Docker с наследством от прошлого проекта и self-hosted раннер, который собирает сборку. Когда память кончается, ядро выбирает жертву — и по умолчанию выбор ему подсказывают не приоритеты владельца.
Как ядро выбирает жертву
OOM-killer считает для каждого процесса оценку и убивает того, у кого она выше. Оценка растёт с потреблением памяти. Node-процесс с сайтом, который держит кэш документов и рендерит страницы, в этом соревновании — крепкий кандидат.
Особенно неприятно, что сборка на раннере — короткий пик потребления. То есть сайт умирает в момент выкладки нового сайта.
1[Service]
2# Память, которую ядро не отбирает под давлением.
3MemoryMin=256M
4
5# Смещение оценки OOM: ниже — значит убивать позже.
6OOMScoreAdjust=-800
7
8Restart=always
9RestartSec=2
OOMScoreAdjust принимает значения от -1000 до 1000. Чем меньше значение, тем ниже вероятность, что процесс будет выбран.
Чем смотреть, а не догадываться
Факт убийства виден в журнале ядра: journalctl -k покажет строку про Out of memory и имя выбранного процесса. Если сайт падал по этой причине, там будет прямое подтверждение, а не догадка.
Потребление под нагрузкой честнее смотреть в момент сборки: запустить выкладку и параллельно следить за free. Средние значения в спокойный час ничего не скажут о пике.
