OOM Killer завершает процессы сайта: как подтвердить нехватку памяти Linux
Если сайт внезапно теряет рабочие процессы, а после перезапуска снова работает, нехватку памяти нужно подтвердить. Сообщение Killed, код завершения 137 и свободная RAM после сбоя не доказывают участие OOM Killer. Надёжная диагностика связывает запись о завершении с временем, PID, конкретной службой и областью ограничения памяти.
Запрос «oom killer сайт падает» описывает симптом. Практическая задача — выяснить, кто завершил процесс: ядро при общем дефиците памяти, механизм ограничения cgroup или пользовательская служба systemd-oomd. От этого зависит исправление. Ниже приведены команды только для чтения; даты, PID, названия служб и расчёты являются учебными примерами.
1. Не считайте любой SIGKILL доказательством OOM
В руководстве Bash завершение по сигналу представляется как 128 плюс номер сигнала. Поэтому 137 часто соответствует SIGKILL: 128 + 9. Однако тот же сигнал может отправить администратор, менеджер процессов или средство ограничения времени. Сам код также может быть возвращён программой.
Сначала установите, что именно сообщает источник: код выхода приложения, номер сигнала или заключение системы запуска. Фразы «процесс убит» и «процесс убит из-за памяти» имеют разную доказательную силу. Не переносите автоматическую подпись интерфейса на причину инцидента без подтверждения.
Для владельца сайта полезна простая заявка администратору: время отказа, затронутая функция, идентификатор экземпляра, сообщение о завершении и время восстановления. Если перестала работать обработка изображений, это точнее, чем просьба «проверить весь сервер». Заодно выясните, не выполнялись ли в этот момент развёртывание, плановый перезапуск или остановка зависшего задания.
2. Найдите запись ядра в ограниченном временном окне
На системе с journald начните с журнала ядра за несколько минут вокруг отказа. Укажите правильную загрузку системы и часовой пояс. В примере даты интерпретируются в часовом поясе сервера:
journalctl -k -b --since "2026-11-01 14:00:00" --until "2026-11-01 14:10:00" --no-pager
Параметры времени и выбора загрузки описаны в документации journalctl. Если после инцидента сервер перезагрузили, текущая загрузка уже не подходит: при наличии сохранённого журнала нужно выбрать предыдущую или конкретный идентификатор загрузки.
Ищите связанный блок сообщений OOM, содержащий выбранную жертву, PID, имя процесса и контекст ограничения. В реализации OOM Killer Linux сообщение о вызове механизма и сообщение о выбранном процессе формируются отдельно. Процесс, вызвавший распределение памяти, не обязательно совпадает с завершённым процессом.
Не сохраняйте только одну строку Killed process: окружающие записи помогают понять область дефицита. Если журнал недоступен из-за прав, ротации или отсутствия сохранения, отметьте пробел в доказательствах. Пустая выборка не опровергает OOM. В контейнере нужные сообщения ядра часто приходится запрашивать у администратора хоста, связывая их с конкретным контейнером.
3. Сопоставьте PID со службой и её запуском
Запись ядра должна относиться к тому экземпляру приложения, который обслуживал неуспешный запрос. Сопоставьте время завершения с журналом службы, её перезапуском и идентификатором задания. Для выбранной службы используйте то же временное окно:
journalctl -u site-worker.service -b --since "2026-11-01 14:00:00" --until "2026-11-01 14:10:00" --no-pager
systemctl show site-worker.service -p MainPID -p ControlGroup -p Result
Команда systemctl show показывает свойства и текущее состояние службы. После автоматического перезапуска MainPID уже относится к новому процессу. Его нельзя подставлять вместо PID из записи прошлого отказа. Номера процессов переиспользуются, поэтому совпадения одного числа недостаточно.
В контейнерной среде PID внутри контейнера может отличаться от PID хоста. Сохраните сопоставление через сведения среды запуска, cgroup и время, не угадывая по имени php-fpm или mysqld. На одном сервере таких процессов может быть несколько.
Если остановилась вся служба, проверьте её реакцию на потерю процесса. Настройка OOMPolicy в systemd влияет на судьбу остальных процессов службы. Последующая остановка соседнего процесса не обязательно является отдельным выбором жертвы ядром. В отчёте разделяйте исходное событие и действия менеджера службы после него.
Учебная последовательность может выглядеть так: в 14:03:12 ядро фиксирует OOM-убийство PID 1842; в 14:03:13 менеджер отмечает потерю рабочего процесса; в 14:03:15 запускается новый PID. Измерение свободной памяти в 14:04 относится уже к состоянию после завершения. Такой порядок объясняет, почему поздний снимок выглядит благополучно, но не отменяет установленную причину первоначального отказа.
4. Различите общий дефицит и ограничение cgroup
При глобальном OOM ядро пытается справиться с нехваткой доступной памяти в системе. При OOM внутри cgroup ограничение может сработать у отдельной службы или контейнера, пока на хосте ещё есть свободная память. Поэтому число гигабайт RAM сервера не определяет, сколько разрешено использовать конкретному приложению.
Уточните контекст в записи ядра: относится ли событие к ограничению группы, какая группа указана и какой процесс выбран. В сложной конфигурации дополнительно учитываются ограничения размещения памяти. Не выводите тип OOM только из текущего общего остатка.
Для systemd настройки MemoryMax и MemoryHigh имеют разный смысл. Первая задаёт жёсткое ограничение, при невозможности удержать которое возможен OOM внутри службы. Вторая ограничивает расход через замедление и освобождение памяти; само превышение memory.high не вызывает OOM Killer ядра. Связь этих настроек с cgroup описана в документации управления ресурсами systemd.
Проверьте также родительские группы: отсутствие собственного предела не отменяет ограничений выше по иерархии. Для обсуждения исправления нужны фактически применённые настройки, а не только строка из шаблона конфигурации, которую могли переопределить при запуске.
5. Прочитайте memory.events и правильно истолкуйте счётчики
Сначала установите, что используется cgroup v2, и найдите действительный путь службы. Следующий путь учебный; он не подходит автоматически для любого контейнера:
cat /sys/fs/cgroup/system.slice/site-worker.service/memory.current
cat /sys/fs/cgroup/system.slice/site-worker.service/memory.max
cat /sys/fs/cgroup/system.slice/site-worker.service/memory.events
cat /sys/fs/cgroup/system.slice/site-worker.service/memory.events.local
memory.current показывает текущий расход группы и потомков в байтах. memory.max содержит предел в байтах либо значение max. По официальной документации cgroup v2, ключи событий означают следующее:
| Ключ | Что означает рост |
|---|---|
| high | Ограничение и освобождение памяти после превышения высокого порога |
| max | Попытки расхода сверх жёсткой границы |
| oom | Достижение предела с угрозой неудачного выделения памяти |
| oom_kill | Процессы группы, завершённые OOM Killer ядра |
oom не равен количеству убийств. Рост oom_kill возможен и при глобальном OOM, поэтому он один не доказывает превышение именно собственного memory.max. При обычной конфигурации memory.events включает события потомков; memory.events.local показывает местные события.
Сравнивайте значения до и после инцидента для одной существующей группы. Счётчик без временной привязки не датирует событие. После удаления и создания cgroup прежняя история не сохраняется в новом объекте. Зафиксируйте исходные показания перед проверкой нагрузки и не сравнивайте незаметно разные экземпляры.
Учебный прирост oom_kill с 0 до 1 между двумя снимками подтверждает учтённое событие в этом интервале, но не даёт точной секунды. Чтобы связать его с жалобой пользователя, всё равно нужны журнал и соответствие процесса службе. Если за интервал произошло несколько отказов, не назначайте один счётчик причиной каждого из них автоматически.
6. Отдельно проверьте решения systemd-oomd
Служба systemd-oomd работает в пользовательском пространстве. Она использует cgroup v2 и сведения о давлении на память, чтобы действовать до OOM ядра. При срабатывании настроенной политики она выбирает группу и отправляет её процессам SIGKILL. Это описано в официальном руководстве systemd-oomd.
journalctl -u systemd-oomd.service -b --since "2026-11-01 14:00:00" --until "2026-11-01 14:10:00" --no-pager
Ищите решение о завершении группы, сопоставляйте её путь со службой и тем же временным интервалом. Такая остановка не требует предшествующей записи об убийстве от OOM Killer ядра. Отсутствие прироста счётчика ядра oom_kill также не исключает действие пользовательской службы.
Наличие установленного systemd-oomd ещё не доказывает его участие: важны активная конфигурация и журнал решения. Если обнаружена именно эта причина, проверяются политика давления, выбранная группа и состав её процессов. Не отключайте механизм наугад: сначала выясните, почему он увидел неприемлемую нагрузку и какие задачи оказались объединены в одну область управления.
7. Оцените память, swap и давление во времени
Текущий вывод free после убийства обычно показывает уже изменившуюся ситуацию: завершённый процесс мог освободить значительный объём. Такой снимок не восстанавливает состояние перед отказом. Для текущей картины можно прочитать небольшие системные отчёты:
cat /proc/meminfo
cat /proc/pressure/memory
В документации /proc MemAvailable определён как оценка памяти для новых приложений без использования swap. Это полезнее одной величины MemFree, но не является запасом отдельной cgroup. Там же различаются виртуальный размер процесса и резидентная память: большой виртуальный объём сам по себе не доказывает исчерпание RAM.
В интерфейсе PSI строка some отражает долю времени, когда хотя бы одна задача задерживается из-за ресурса, а full — когда одновременно задерживаются все не простаивающие задачи рассматриваемой области. Поля avg10, avg60 и avg300 показывают усреднённую долю времени задержки для окон 10, 60 и 300 секунд. Значения выражены в процентах времени, а не занятой RAM.
Свяжите историю этих значений с количеством параллельных задач, задержками и перезапусками. Swap оценивайте по нагрузке и доступным ограничениям: его наличие на хосте не означает неограниченную доступность службе. Частые обращения к хранилищу могут ухудшать время ответа, а просто большой занятый swap ещё не доказывает текущую аварийную ситуацию.
Для ограниченной службы отдельно уточните MemorySwapMax, который управляет пределом memory.swap.max. Разрешённый swap и лимит основной памяти — разные настройки. Высокий PSI показывает задержки из-за нехватки ресурса, но сам по себе не подтверждает убийство процесса. Поэтому в одной временной шкале должны быть три разных вида сведений: расход памяти, потери времени и события завершения.
8. Посчитайте запас на учебном примере
Предположим, журнал ядра и журнал службы подтвердили OOM внутри группы обработчиков. Её предел равен 2 GiB, то есть 2048 MiB. Измерения в тестовом окружении показывают базовый расход 384 MiB и примерно 320 MiB дополнительного пикового расхода на одно одновременно выполняемое задание.
| Параллельные задания | Расчёт расхода | Остаток до предела |
|---|---|---|
| 4 | 384 + 4 × 320 = 1664 MiB | 384 MiB |
| 5 | 384 + 5 × 320 = 1984 MiB | 64 MiB |
| 8 | 384 + 8 × 320 = 2944 MiB | Превышение на 896 MiB |
Если команда закладывает учебный резерв 256 MiB, расчёт допускает четыре задания: (2048 − 384 − 256) / 320 = 4,4, округление вниз даёт 4. Пять заданий оставляют меньше выбранного резерва. Сам резерв не является нормативом Linux: его выбирают по измерениям и характеру работы.
Это модель планирования, а не доказательство причины уже случившегося отказа. Дополнительный расход измеряется на уровне группы; простое сложение RSS процессов может повторно учитывать общие страницы. Профиль заданий, кеши и совместное использование памяти нарушают линейность. Четыре задания — кандидат для проверки, а не гарантированно безопасная настройка.
После уменьшения параллелизма проверьте очередь и время ожидания. Снижение расхода памяти не должно незаметно превратить короткий отказ в постоянную задержку обработки заказов или загрузок.
9. Объясните, почему расход вырос
Фраза «сервер убивает mysql php» не определяет виновника роста. Завершённый процесс мог стать выбранной жертвой общего дефицита, созданного несколькими службами. Сопоставьте изменения перед инцидентом: новую версию, импорт, обработку крупных изображений, число фоновых работников и параллельные запросы.
Различайте короткий пик и накопление. Если расход возвращается к прежнему уровню после завершения тяжёлого задания, сначала проверяются размер задания и одновременность. Если одинаковые циклы постепенно повышают базовый уровень, разработчику нужна проверка удерживаемых объектов, неограниченных кешей и возможной утечки. Один возрастающий график ещё не устанавливает конкретную ошибку кода.
Для базы данных и веб-работников собирайте отдельные профили, затем оценивайте совместный пик. Среднее потребление каждого процесса не описывает момент, когда все выполняют тяжёлую работу одновременно. Не умножайте один случайный снимок на максимальное число работников и не выдавайте результат за точный бюджет.
Сохраните минимальный воспроизводимый сценарий: тип входных данных, размер партии, параллелизм и версию приложения. Повторять нужно характер нагрузки, а не случайную последовательность запросов. Это делает проверку исправления сравнимой с наблюдавшимся отказом.
Размер входного файла также не равен требуемой памяти. При распаковке, декодировании изображения или формировании отчёта программа может одновременно держать исходные данные, промежуточные структуры и результат. Соберите несколько характерных вариантов входа, включая самый тяжёлый допустимый. Если проверять только маленький образец, можно подтвердить исправность простого случая и пропустить прежний путь к аварии.
10. Исправьте причину, сохранив управляемые ограничения
Выберите действие по подтверждённому механизму. Для всплеска параллельных заданий подходят ограничение одновременности и очередь. Для слишком большой партии — обработка порциями или потоковая схема. Для накопления — устранение удержания данных и ограничение кеша. Для корректной рабочей нагрузки, которой объективно не хватает ресурса, рассматривается увеличение ёмкости.
Повышение лимита cgroup требует проверки запаса хоста и соседних служб. Разрешённые нескольким приложениям объёмы не превращаются в физическую память. Изменение должно учитывать их совместные пики, а не только успешный отдельный запуск проблемного задания.
Не делайте все процессы неуязвимыми для OOM и не подменяйте диагностику отключением механизма. Настройка предпочтения жертвы не устраняет нехватку памяти. Аналогично безлимитное добавление swap не является универсальным ремонтом: нужно оценивать задержки, доступность хранилища и ограничения группы.
Перезапуск может временно вернуть работоспособность, но сам по себе не объясняет причину. Зафиксируйте, что именно изменено и какой эффект ожидается: меньший пик, отсутствие накопления или достаточный подтверждённый запас. Для каждого изменения назначьте владельца и критерий отмены, если оно ухудшает обработку запросов.
11. Проверьте результат на сопоставимой нагрузке
Сначала воспроизведите сценарий в подходящем тестовом окружении с ограничениями, сопоставимыми с рабочими. Сохраните исходные счётчики, версию, число работников и объём входных данных. Задайте границы нагрузки и условия остановки по задержкам, ошибкам и давлению на память, чтобы проверка оставалась управляемой.
Оценивайте несколько полных циклов тяжёлой операции, включая подготовку и завершение. Для подозрения на утечку нужен достаточно долгий повторяемый прогон: один удачный запрос её не исключает. Интервал сбора показателей должен позволять заметить ожидаемые пики; редкие снимки могут пропустить кратковременный расход.
- Нет новых подтверждённых OOM-убийств в ядре и решений systemd-oomd о завершении.
- Изменения счётчиков относятся к той же группе и объяснены.
- Пиковое потребление укладывается в согласованный бюджет с запасом.
- Задержки, ошибки и очередь остаются в принятых пределах.
- Штатная пользовательская функция достигает ожидаемого результата.
После неожиданного завершения проверьте незавершённые задания и данные. Процесс мог выполнить внешнее действие до остановки, поэтому автоматический повтор требует правил защиты от дублей. Восстановление PID и статуса active ещё не означает, что обработка заказов или сохранение загрузок восстановились полностью.
12. Сохраните доказательства и наблюдайте последствия
Заключение по инциденту должно содержать цепочку: время и экземпляр, запись о завершении, PID и служба, глобальная или групповая причина, применённые ограничения, изменение и результат проверки. Если часть журналов утрачена, обозначьте вывод как гипотезу и настройте сбор недостающих сведений перед следующим воспроизведением.
Для повседневной работы нужны история расхода памяти, давления и перезапусков, а также ответственный за реакцию. Отдельно сохраняйте последствия для пользователя: какие URL перестали отвечать и когда восстановились. Это помогает сопоставить техническое событие с фактическим перерывом работы сайта.
Подключите SiteGuard для внешнего наблюдения за HTTP-доступностью важных страниц. Причины OOM и состояние RAM проверяйте средствами сервера; доступный HTTP-ответ дополняет проверку восстановления приложения.