n1ro°
RU

Ошибки и коды · Инструкция

Docker OOMKilled: как найти нехватку памяти и исправить лимит

Docker контейнер OOMKilled — это конкретный сценарий, когда Linux не смог удовлетворить запрос на память и завершил.

Редакция N1RO · Проверено

Короткий ответ

Если Docker-контейнер завершился из-за памяти, сначала подтвердите OOM по `docker inspect`, событиям Docker и журналу ядра, затем сравните реальный пик потребления с `--memory`. Не отключайте OOM killer как универсальное решение: безопаснее устранить рост памяти или назначить обоснованный лимит и запас RAM хосту.

Как доказать, что контейнер убил OOM killer

Сначала посмотрите состояние контейнера через `docker inspect`: важны признак OOM, код выхода, время завершения и restart count. Затем проверьте `docker events` за нужный интервал — Docker публикует отдельное событие `oom`. На Linux-хосте дополнительно изучите системный журнал ядра, потому что именно ядро принимает решение о принудительном завершении процессов при нехватке памяти. Если контейнер быстро перезапускается по restart policy, сохраните timestamp первого сбоя: после нескольких циклов старт/стоп исходный OOM легко потерять среди обычных логов.

Совет: Сопоставляйте timestamp OOM с графиком памяти приложения. Текущий `docker stats` после рестарта уже не показывает пик перед падением.

Hard limit, reservation и swap — не одно и то же

Параметр `--memory` задаёт жёсткий потолок, тогда как memory reservation — мягкий ориентир при конкуренции за ресурсы. `--memory-swap` имеет смысл только вместе с `--memory` и определяет общий объём RAM плюс swap, доступный контейнеру. Swap может дать временный буфер на пике, но он заметно медленнее RAM, поэтому постоянный свопинг часто превращает явный crash в тяжёлую деградацию latency. Для JVM, Node.js и других runtime с собственным heap limit важно оставить пространство не только под heap, но и под native allocations, библиотеки, стеки потоков, буферы и файловый кэш.

Предупреждение: Увеличивать container limit почти до всей RAM сервера опасно: так проблема переносится с одного контейнера на весь хост.

Почему не стоит отключать OOM killer

Docker прямо предупреждает, что обход OOM-защиты способен ухудшить устойчивость узла. Если отключить OOM killer без корректного memory limit, контейнер может вытеснить память у Docker daemon и системных процессов, после чего ядро начнёт убивать уже их. Даже с лимитом `--oom-kill-disable` не исправляет утечку и не уменьшает потребление: приложение может зависнуть под давлением памяти или уйти в swap. В production OOM лучше рассматривать как измеряемый сигнал ёмкости — установить алерт до лимита, определить рабочий набор памяти, найти рост heap/кэшей и подтвердить изменение нагрузочным тестом.

Важно: Не маскируйте проблему настройкой OOM priority до того, как измерили реальное потребление приложения.

Как отличить низкий лимит от утечки

Если контейнер стабильно падает при одинаковом уровне нагрузки и память быстро упирается в заданный limit, лимит может быть ниже нормального рабочего пика. Если же RSS или heap монотонно растут часами или днями и после каждого увеличения лимита OOM просто наступает позже, гораздо вероятнее утечка или неограниченный кэш. Если OOM происходят у разных контейнеров одновременно, смотрите уже на суммарную память хоста, swap и overcommit. Диагностика должна привести к числу: типичное потребление, p95/p99 пик и безопасный запас, а не к случайному значению `-m 4g`.

Как подобрать memory limit после диагностики

После подтверждённого OOM не выбирайте новый лимит на глаз. Снимите несколько нагрузочных циклов и определите стабильное потребление после прогрева, обычный пик и редкие всплески. Для managed runtime отдельно сравните heap с RSS процесса: если heap ограничен 1 ГБ, контейнер всё равно может использовать больше за счёт native memory, mmap, библиотек, сетевых буферов и дочерних процессов. Затем оставьте запас до hard limit и ещё один запас на уровне хоста, потому что Docker daemon, kernel, filesystem cache и соседние контейнеры тоже требуют RAM. Если приложение масштабируется горизонтально, иногда безопаснее запустить два экземпляра с меньшим concurrency, чем один огромный контейнер без границ. После изменения лимита включите алерт ниже hard limit с запасом, выбранным по измеренному профилю нагрузки, и смотрите не только среднее, но и тренд. Если память растёт монотонно без возврата к базовому уровню после нагрузки, увеличение лимита — временная отсрочка, а не исправление. Тогда нужен profiler, heap dump или анализ кэшей и очередей внутри приложения.

Какие данные собрать до изменения лимита

Перед тем как менять `--memory`, сохраните доказательства исходного сбоя. `docker container inspect` показывает детальное состояние контейнера, а `docker events` публикует отдельное событие `oom`; эти данные полезно привязать к одному timestamp и имени контейнера. Если событие уже старое, учитывайте, что Docker CLI возвращает ограниченную историю событий, поэтому в production лучше отправлять events и метрики во внешний мониторинг. Дальше зафиксируйте лимит RAM, reservation, настройки swap, restart policy и фактическое потребление хоста в момент проблемы. Отдельно проверьте, не совпал ли OOM с ростом соседнего контейнера: даже корректный лимит одного сервиса не защищает узел, если остальные процессы вместе исчерпывают память. Для JVM, Node.js, Python workers и баз данных полезно записать внутренние метрики приложения рядом с RSS контейнера, потому что одна цифра `docker stats` не объясняет, какая часть памяти растёт. После исправления повторите тот же профиль нагрузки и сравните не только факт отсутствия OOM, но и возврат памяти к обычному уровню. Алерт ставьте ниже hard limit с запасом, выбранным по вашим измерениям, а не по универсальному проценту: рабочий headroom зависит от характера нагрузки и скорости роста памяти.

Пошаговый порядок действий

  1. Проверьте `docker inspect` и событие `oom` за момент сбоя.
  2. Сверьте текущие `--memory`, reservation и swap-настройки.
  3. Снимите график RSS/heap под обычной и пиковой нагрузкой.
  4. Проверьте свободную RAM и swap всего хоста.
  5. Устраните утечку/лишний concurrency либо скорректируйте лимит по измерениям.
  6. Повторите нагрузочный тест и убедитесь, что у хоста остаётся резерв.

Краткая таблица

  • Есть событие `oom`. ядро убило процесс. inspect + kernel log
  • Падение только на пике. пик выше лимита. график RSS/heap
  • OOM у разных контейнеров. не хватает RAM хосту. суммарное потребление
  • OOM возвращается после роста лимита. не устранён рост памяти. профилирование

Контрольный список

  • проверить утечку и кэш
  • ограничить concurrency
  • согласовать heap и container limit
  • оставить RAM для хоста
  • не использовать oom-kill-disable как лечение

Что учитывать

Docker прямо предупреждает, что обход OOM-защиты способен ухудшить устойчивость узла. Если отключить OOM killer без корректного memory limit, контейнер может вытеснить память у Docker daemon и системных процессов, после чего ядро начнёт убивать уже их. Даже с лимитом `--oom-kill-disable` не исправляет утечку и не уменьшает потребление: приложение может зависнуть под давлением памяти или уйти в swap. В production OOM лучше рассматривать как измеряемый сигнал ёмкости — установить алерт до лимита…

Источники и проверка

Фактическая часть сверена по первичным источникам (в т.ч. Resource constraints). Пример и формулировки — редакция N1RO на 2026-09-20.