Контейнер получает статус OOMKilled, хотя на узле Kubernetes остаётся свободная память. Какое ограничение вызывает это?
Контейнер был остановлен из-за превышения назначенного ему лимита памяти, заданного на уровне cgroup. Свободная память на узле не отменяет этот лимит: контейнер ограничен своим изолированным бюджетом, а не всей доступной памятью хоста.
Контейнеры используют механизмы изоляции Linux, включая cgroups, чтобы ограничивать потребление CPU, памяти и других ресурсов отдельными группами процессов. Это решает проблему, при которой один сервис мог полностью занять ресурсы узла и нарушить работу остальных.
В Kubernetes cgroups стали основой практического разделения ресурсов между контейнерами. Благодаря этому планировщик может учитывать заявленные ресурсы, а операционная система — принудительно останавливать процесс, нарушивший установленный предел.
У контейнера могут быть заданы requests и limits памяти. Request используется главным образом планировщиком при размещении пода, а limit задаёт верхнюю границу потребления, которую контейнер не должен превышать.
Если приложение превысило лимит, ядро Linux может запустить OOM killer внутри соответствующей группы процессов. Поэтому контейнер может завершиться с причиной OOMKilled, даже когда на узле в целом ещё есть свободная память.
Неверная диагностика часто приводит к бесполезному увеличению ресурсов узла. Это не исправит ситуацию, если ограничение установлено на уровне самого контейнера: проблема находится в его cgroup, а не в общем объёме памяти хоста.
При запуске пода Kubernetes передаёт ограничения памяти контейнера в механизм cgroups. Процессы контейнера и связанные с ними выделения памяти учитываются в этой группе. Когда фактическое потребление достигает установленного лимита, ядро не обязано ждать исчерпания памяти всего узла — оно может завершить один или несколько процессов внутри группы.
Важно различать несколько случаев:
OOMKilled независимо от свободной памяти на узле.Для диагностики нужно сравнить потребление контейнера с его лимитом, проверить события пода, причины завершения контейнера и метрики cgroup. Также важно выяснить, не создаёт ли приложение кратковременный пик: лимит может быть превышен из-за всплеска памяти, даже если среднее потребление выглядит безопасным.
Увеличение лимита может устранить OOMKilled, но одновременно повысить риск давления по памяти на узле. Если лимит сделать слишком большим, планировщик будет опираться на requests, а фактическое суммарное потребление нескольких контейнеров может превысить безопасный объём узла. Поэтому requests должны реалистично отражать обычную потребность, а limits — допустимый пик с учётом ёмкости кластера.
Сервис обработки документов периодически завершался с OOMKilled. На узлах оставалось достаточно свободной памяти, а среднее потребление сервиса было значительно ниже лимита. Метрики показали кратковременный пик при одновременной обработке нескольких крупных файлов.
Рассматривались три варианта:
Выбрали третий вариант. Параллелизм ограничили, лимит увеличили с запасом, а суммарные requests проверили относительно ёмкости узлов. После этого пиковое потребление стало предсказуемым, а контейнеры перестали завершаться без неоправданного увеличения размера кластера.
request отличается от limit в контексте OOMKilled?Request — это ресурсная потребность, которую планировщик учитывает при выборе узла. Он не является жёсткой границей потребления: контейнер может использовать больше request, если на узле есть доступный ресурс.
Limit — это верхняя граница, передаваемая в механизм ограничения ресурсов. Превышение memory limit может привести к OOMKilled внутри cgroup даже при наличии свободной памяти на узле.
Нет. OOM-событие может быть связано с давлением по памяти на уровне узла. В этом случае kubelet или ядро могут выбрать процесс для завершения, чтобы восстановить работоспособность узла.
Для различения сценариев нужно проверить состояние контейнера, события пода, сообщения kubelet и метрики узла. Наличие OOMKilled в статусе контейнера указывает на факт убийства процесса из-за памяти, но для точной причины нужно установить, действовал ли контейнерный cgroup-лимит или имел место node-level OOM.
Более высокий лимит позволяет одному контейнеру потребить больше памяти. Если несколько подов одновременно достигнут своих лимитов, суммарное фактическое потребление может превысить физическую память узла и вызвать memory pressure или системный OOM.
Кроме того, слишком низкие requests при высоких limits создают разрыв между планированием и реальной нагрузкой: поды будут выглядеть дешёвыми для планировщика, но в пиковый момент потребуют значительно больше ресурсов. Поэтому лимиты нужно проверять нагрузочными тестами, а requests устанавливать по наблюдаемому базовому потреблению и требованиям к размещению.