Linux OOMKilledВысокая

OOMKilled в Kubernetes: причины и быстрое решение

Статья подробно разбирает ошибку OOMKilled в Kubernetes, объясняет её причины (превышение лимитов памяти) и предлагает несколько практических способов решения: от корректировки ресурсов до оптимизации кода приложения.

Обновлено 16 февраля 2026 г.
15-25 мин
Средняя
FixPedia Team
Применимо к:Kubernetes 1.20+K3sOpenShift 4.xЛюбое CNCF-совместимое распределение

Что означает ошибка OOMKilled

OOMKilled (Out Of Memory Killed) — это состояние контейнера в Kubernetes, означающее, что процесс внутри контейнера был принудительно завершён ядром Linux из-за превышения выделенного лимита памяти (cgroup memory limit). В выводе kubectl get pods pod обычно переходит в статус CrashLoopBackOff или Error, а в логах или событии kubectl describe pod вы увидите последнюю причину завершения: State: Terminated Reason: OOMKilled.

Это системная ошибка, а не ошибка приложения. Kubernetes (через контейнерный рантайм, например, containerd) просто выполняет политику лимитов ресурсов, установленную вами в манифесте.

Причины возникновения

  1. Слишком низкий лимит памяти (limits.memory). Самая частая причина. В манифесте указано, например, 512Mi, а приложение в пике требует 700Mi.
  2. Утечка памяти (memory leak) в приложении. Приложение постепенно потребляет всё больше RAM, пока не достигнет лимита.
  3. Несоответствие requests и limits. Если requests сильно ниже limits, pod может быть запланирован на узел с малым количеством свободной памяти, что приведёт к быстрому исчерпанию лимита.
  4. Неучтённые процессы внутри контейнера. Фоновые процессы (например, cron, sidecar-контейнеры) могут потреблять дополнительную память.
  5. Проблемы с настройкой JVM/рантайма. Для Java-приложений неправильно заданный -Xmx может привести к тому, что JVM попытается зарезервировать больше памяти, чем разрешено лимитом контейнера, и будет убита сразу.
  6. Использование памяти ядром (kernel memory) или page cache. В некоторых конфигурациях cgroup учитывается не только память пользовательского пространства (RSS), но и кэш. Интенсивная файловая операция может "съесть" лимит.

Способы решения

Способ 1: Увеличение лимита памяти в манифесте

Это прямое и часто самое быстрое решение.

  1. Найдите манифест, управляющий pod'ом (Deployment, StatefulSet, DaemonSet).
  2. В секции spec.template.spec.containers[].resources увеличьте значения limits.memory и, что важно, requests.memory.
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: myapp
    spec:
      template:
        spec:
          containers:
          - name: myapp-container
            image: myapp:latest
            resources:
              requests:
                memory: "512Mi"   # Увеличьте это значение
              limits:
                memory: "1Gi"     # Увеличьте это значение
    
  3. Примените изменения: kubectl apply -f deployment.yaml.
  4. Следите за перезапуском pod'а: kubectl rollout status deployment/myapp.

💡 Совет: Устанавливайте limits в 1.5-2 раза выше пикового потребления, которое вы оценили через kubectl top. requests должен быть близок к среднему потреблению для корректного планирования.

Способ 2: Оптимизация приложения и образа

Если вы не хотите бесконечно увеличивать лимиты, нужно уменьшать потребление.

  1. Профилирование памяти. Запустите контейнер с отладкой.
    • Для Java: jcmd <PID> VM.native_memory summary или jmap -histo.
    • Для Go: go tool pprof http://<pod-ip>:6060/debug/pprof/heap.
    • Для Python: tracemalloc или memory-profiler.
  2. Настройка JVM. Если приложение на Java, явно задайте максимальный размер heap через -Xmx, например, -Xmx800m. Убедитесь, что -Xmx + метапространство + стек потока < limits.memory.
  3. Уменьшение размера образа. Используйте multi-stage сборки и легковесные базовые образы (alpine, distroless). Меньше слоёв — меньше накладных расходов на память.
  4. Оптимизация кода и конфигурации. Уменьшите размеры кэшей (Redis, встроенные), настройте пуллы соединений, проверьте на утечки (открытые файловые дескрипторы, неосвобождаемые объекты).

Способ 3: Проверка и корректировка конфигурации узла и cgroup

Иногда проблема не в pod'е, а в конфигурации узла.

  1. Проверьте swap на узле. Kubernetes по умолчанию не рекомендует использовать swap. Если swap включён, система может "подтормаживать", а не убивать процессы, что сложнее диагностировать. Лучше отключить: sudo swapoff -a.
  2. Узнайте версию cgroup. stat -fc %T /sys/fs/cgroup/. Если cgroup2, убедитесь, что Docker/containerd корректно настроен для работы с ним. Некоторые старые версии рантаймов могут некорректно считать память в cgroup v2.
  3. Проверьте общую нагрузку на узел. Возможно, на узле запущено слишком много pod'ов с высокими лимитами, и физической памяти просто не хватает. Используйте kubectl top node. Рассмотрите увеличение узлов или настройку ResourceQuota.

Способ 4: Настройка oomScoreAdj (выживание при нехватке на узле)

Этот шаг не решит проблему внутри контейнера, но поможет критически важным pod'ам выжить, если память на узле закончится и ядро начнёт убивать процессы.

В манифесте pod'а добавьте securityContext:

spec:
  containers:
  - name: myapp
    # ...
  securityContext:
    oomScoreAdj: -999  # Минимальный приоритет на убийство (только для Linux)

Чем меньше значение oomScoreAdj (от -1000 до 1000), тем меньше шансов, что процесс будет убит первым. По умолчанию у контейнера 0. У kubelet и системных процессов — отрицательные значения.

⚠️ Важно: Если pod всё равно получает OOMKilled, это значит, что он превысил свой собственный limit, а не был жертвой общей нехватки на узле. oomScoreAdj здесь не поможет.

Профилактика

  1. Всегда устанавливайте и requests, и limits. Никогда не оставляйте limits неограниченными (unlimited).
  2. Настройте мониторинг. Используйте Prometheus + kube-state-metrics для сбора метрик kube_pod_container_resource_limits и container_memory_usage_bytes. Настройте алерты (Alertmanager) на приближение использования к лимиту (например, >85%).
  3. Проводите нагрузочное тестирование. Перед запуском в продакшене протестируйте приложение с инструментами вроде stress-ng или hey внутри контейнера, чтобы найти реальные пики потребления памяти.
  4. Используйте вертикальный автоскейлинг (VPA) в режиме Auto или Initial. VPA может автоматически анализировать историческое потребление и рекомендовать (или применять) оптимальные значения requests и limits. Внимание: VPA не должен использоваться вместе с HPA на CPU/Memory одновременно без осторожности.
  5. Регулярно обновляйте образы. Новые версии приложений и их зависимостей часто содержат исправления утечек памяти и оптимизации.

Часто задаваемые вопросы

В чём разница между requests и limits памяти, и как они влияют на OOMKilled?
Можно ли полностью отключить лимиты памяти, чтобы избежать OOMKilled?
Почему приложение в контейнере падает с OOMKilled, хотя на хосте много свободной RAM?
Какой инструмент лучше всего использовать для анализа утечек памяти в pod'е?

Полезное

Определите проблемный pod и проверьте его статус
Проанализируйте текущие лимиты памяти
Оцените фактическое потребление памяти
Корректируйте лимиты в манифесте
Оптимизируйте приложение внутри контейнера
Настройте OOM Score Adjustment (опционально)