November 11, 2025

Отладка OOM killer в контейнерах: memory.stat и pressure stall information

Работа с контейнерами похожа на управление многоквартирным домом, где каждая квартира должна получить свою долю ресурсов. Когда память заканчивается, система активирует механизм OOM killer, который решает, какой процесс остановить. Разберёмся, как отследить эти моменты и предотвратить неожиданные сбои.

Что происходит при нехватке памяти

Столкнулся недавно с интересной ситуацией: приложение в контейнере работало отлично несколько дней, а потом внезапно перезапускалось. Логи молчали, метрики показывали норму. Оказалось, OOM killer тихо делал свою работу. Механизм этот срабатывает, когда ядро Linux понимает, что памяти критически не хватает. В контейнерах ситуация усложняется наличием cgroups, которые накладывают дополнительные ограничения.

Каждый контейнер живёт в своей изолированной среде с лимитами памяти. Когда процессы внутри контейнера пытаются выделить больше памяти, чем разрешено, срабатывает защитный механизм. Интересная деталь: OOM killer выбирает жертву не случайно. У него есть система подсчёта очков, где учитывается потребление памяти, приоритет процесса и время его работы.

Memory.stat: окно в потребление памяти

Файл memory.stat стал моим надёжным помощником в расследовании проблем с памятью. Находится он в директории cgroup контейнера, обычно по пути /sys/fs/cgroup/memory/. Этот файл содержит детальную статистику использования памяти в реальном времени.

Вот что можно узнать из memory.stat:

  • cache: объём памяти под кэш страниц
  • rss: резидентная память процессов
  • mapped_file: память, отображённая на файлы
  • pgpgin/pgpgout: счётчики страниц, загруженных и выгруженных
  • inactive_anon/active_anon: неактивная и активная анонимная память

Читаю этот файл регулярно через простой скрипт мониторинга. Заметил закономерность: перед срабатыванием OOM killer значение rss приближается к лимиту, а cache резко падает. Система пытается освободить память, сбрасывая кэш, но если этого недостаточно, приходится завершать процессы.

Pressure Stall Information: новый взгляд на нагрузку

PSI (Pressure Stall Information) появился в ядре Linux 4.20 и изменил подход к мониторингу. Раньше приходилось гадать, насколько система перегружена. Теперь есть точные метрики задержек из-за нехватки ресурсов.

В контейнерах PSI доступен через файлы в /proc/pressure/. Три основных файла дают информацию о давлении на CPU, память и ввод-вывод. Формат простой: показывается процент времени, когда хотя бы один процесс (some) или все процессы (full) ждали ресурс.

Настроил алерты на основе PSI метрик. Когда some для памяти превышает 10% за минуту, получаю предупреждение. Это даёт время среагировать до того, как OOM killer начнёт свою работу. Практика показала: PSI предсказывает проблемы за 5-10 минут до критической ситуации.

Настройка oom_score_adj для защиты критичных процессов

У каждого процесса есть параметр oom_score_adj, который влияет на вероятность его завершения при нехватке памяти. Значение варьируется от -1000 до 1000. Отрицательные значения защищают процесс, положительные делают его более вероятной целью.

Научился использовать эту особенность для защиты критичных компонентов. Базе данных выставляю -500, веб-серверу 0, а воркерам обработки задач +200. Такая иерархия гарантирует, что при проблемах первыми остановятся наименее важные процессы.

Изменить oom_score_adj можно через запись в /proc/[pid]/oom_score_adj. В Docker это делается через параметр oom-score-adj при запуске контейнера. В Kubernetes используется QoS класс подов, который автоматически настраивает эти значения.

Практические инструменты мониторинга

Создал набор скриптов для отслеживания состояния памяти в контейнерах. Основной скрипт парсит memory.stat каждые 10 секунд и записывает в лог. Когда использование памяти превышает 80%, частота опроса увеличивается до раза в секунду.

Для визуализации использую Grafana с метриками из Prometheus. Настроил дашборд с графиками memory.stat и PSI. Особенно полезным оказался график соотношения rss к лимиту памяти. Когда линия приближается к 100%, жди неприятностей.

Ещё один полезный инструмент: скрипт, который при приближении к лимиту памяти автоматически собирает дамп heap самого прожорливого процесса. Это помогает понять, что именно съедает память, даже если процесс будет убит OOM killer.

Анализ логов и событий OOM

Когда OOM killer срабатывает, он оставляет следы в системных логах. В dmesg появляется подробная информация о состоянии памяти и выбранной жертве. Проблема контейнеров в том, что эти логи часто теряются при перезапуске.

Решил проблему настройкой централизованного сбора логов. Все события OOM killer отправляются в отдельный индекс Elasticsearch. Написал парсер, который извлекает ключевую информацию: какой процесс был убит, сколько памяти он использовал, какие были альтернативы.

Анализ собранных данных выявил интересные паттерны. Большинство OOM событий происходило в определённые часы, когда нагрузка росла. Некоторые контейнеры страдали от утечек памяти, постепенно увеличивая потребление до критического уровня.

Оптимизация и предотвращение проблем

После месяцев борьбы с OOM killer выработал стратегию предотвращения проблем. Первое правило: всегда оставляй запас памяти. Если приложению нужен 1 ГБ, выделяю 1.2 ГБ. Эти 20% служат буфером для пиковых нагрузок.

Второе открытие: swap в контейнерах работает не так, как ожидается. Многие отключают swap совсем, но правильно настроенный swap может дать время среагировать на проблему. Установил swappiness на 10 для продакшн контейнеров. Это заставляет систему использовать swap только в крайнем случае, но даёт дополнительное время до срабатывания OOM killer.

Внедрил автоматическое масштабирование на основе метрик памяти. Когда потребление превышает 70% на протяжении минуты, запускается дополнительная реплика контейнера. Когда падает ниже 40%, лишние реплики останавливаются. Это решение снизило количество OOM событий на 90%.

Регулярно провожу нагрузочное тестирование с постепенным увеличением нагрузки. Это помогает найти точку, где начинаются проблемы с памятью. Обнаружил, что многие Java-приложения неправильно определяют доступную память в контейнере. Решается установкой правильных JVM флагов для работы с cgroups.

Работа с OOM killer в контейнерах требует постоянного внимания и тонкой настройки. Memory.stat и PSI дают инструменты для понимания происходящего, но успех зависит от правильной интерпретации данных и своевременной реакции. Главное помнить: OOM killer это последняя линия защиты системы. Если он срабатывает регулярно, пора пересмотреть архитектуру и лимиты ресурсов.


https://fileenergy.com/linux/kak-nastroit-ssh-klyuchi-s-algoritmom-ed25519-i-dvukhfaktornuyu-autentifikatsiyu-cherez-google-authenticator-dlya-zashchity-servera-ot-atak-pereborom-parolej

https://www.digitalocean.com/community/tutorials/how-to-set-up-multi-factor-authentication-for-ssh-on-ubuntu-20-04