Pular para o conteúdo
Abrir o Kubepier

Container · Reason: OOMKilled · exit code 137

OOMKilled

O container passou do limite de memória e o kernel o matou. Não é bug do Kubernetes: é o limite fazendo o que foi configurado para fazer.

publicado em 25 set 2026 7 min de leitura reproduzido num cluster k3s 1.35 de teste em 25 set 2026 verificado contra a documentação do Kubernetes: "Assign Memory Resources to Containers and Pods" e "Resource Management for Pods and Containers"

Sinal

Last State: Terminated, Reason: OOMKilled, Exit Code: 137. O pod costuma estar em CrashLoopBackOff.

Causa mais comum

limits.memory abaixo do pico real de uso, ou runtime que usa mais memória do que o limite comporta.

Correção rápida

Medir o pico de uso, subir o limite acima dele, alinhar o heap do runtime ao limite.

Comando que confirma

kubectl describe pod NOME | grep -A3 "Last State"

O que significa

Quando você define resources.limits.memory, o kubelet configura esse valor como teto no cgroup do container. Se o processo tentar usar mais do que isso, o kernel aciona o OOM killer e encerra o processo com SIGKILL, sinal 9. O container termina com exit code 137 (128 + 9) e o kubelet registra Reason: OOMKilled. Com a restartPolicy padrão (Always), o container é reiniciado; se estourar de novo, entra em CrashLoopBackOff. Fonte: documentação do Kubernetes, "Assign Memory Resources to Containers and Pods", seção "Exceed a Container's memory limit".

Um detalhe que confunde: o request de memória não protege contra OOMKilled. O request serve ao scheduler para escolher o nó; o limite é o que o kernel aplica. Um container com request 64Mi e limit 128Mi morre ao passar de 128Mi, e não de 64Mi. Sem limit definido, ele pode usar toda a memória livre do nó e, aí, quem age é a eviction por pressão no nó, que é outro erro (Evicted).

Causas comuns

Limite abaixo do pico realmorre sempre no mesmo ponto

O limits.memory foi copiado de um tutorial ou de outro serviço. A aplicação carrega cache, catálogo ou pool de conexões na subida e passa do teto em segundos.

Runtime que não cabe no limiteJVM, Node, .NET

Versões antigas da JVM (anteriores ao Java 10 e ao 8u191) dimensionam o heap pela memória do nó, não do container. Nas atuais, o heap respeita o limite, mas heap não é tudo: metaspace, threads e buffers diretos também ocupam memória. O Node dimensiona o heap por heurística própria, que pode não bater com o limite do container.

Vazamento de memóriaintervalo entre mortes encurta

Cache sem teto, listeners não removidos, conexões não fechadas. O gráfico de 30 dias sobe em serra: cresce até o limite, morre, recomeça. Subir o limite só espaça as mortes.

Pico em tarefa em lotemorre em horário fixo

Relatório da meia-noite, reindexação, importação de planilha. O uso médio cabe no limite; o pico do job não. Ou o job vai para um Job/CronJob com limite próprio, ou o limite acompanha o pico.

Sidecar ou init container sem limite adequadoOOMKilled num container que não é o principal

Agentes de log, proxies de service mesh e init containers têm os próprios limits. Um sidecar com pouca memória morre ao receber uma rajada de logs, e o pod inteiro parece instável.

Diagnóstico em 4 comandos

Saídas reais de um pod de teste que tenta alocar 150M com limite de 128Mi.

1 · o STATUS já diz, por alguns segundos

saída real
kubectl -n loja get pod checkout-oom
NAME           READY   STATUS      RESTARTS      AGE
checkout-oom   0/1     OOMKilled   3 (52s ago)   75s

Entre um reinício e outro o STATUS alterna com CrashLoopBackOff. O registro que não some é o do último término:

2 · confirme o motivo do último término

saída real, sem o containerID
kubectl -n loja get pod checkout-oom \
  -o jsonpath='{.status.containerStatuses[0].lastState}' | jq .
{
  "terminated": {
    "exitCode": 137,
    "finishedAt": "2026-09-25T22:55:22Z",
    "reason": "OOMKilled",
    "startedAt": "2026-09-25T22:55:22Z"
  }
}

3 · veja o limite configurado e o histórico de reinícios

saída real, resumida
kubectl -n loja describe pod checkout-oom | grep -E -A4 "Last State|Limits|Requests|Restart Count"
    Last State:     Terminated
      Reason:       OOMKilled
      Exit Code:    137
    Restart Count:  3
    Limits:
      cpu:     500m
      memory:  128Mi
    Requests:
      cpu:        100m
      memory:     64Mi

4 · os eventos não citam OOMKilled

saída real, resumida
kubectl -n loja describe pod checkout-oom | sed -n '/Events:/,$p'
  Normal   Started  29s (x4 over 70s)  kubelet  Container started
  Warning  BackOff  28s (x4 over 66s)  kubelet  Back-off restarting failed container checkout in pod checkout-oom_loja(...)

No nosso teste, os eventos do pod mostraram só BackOff: quem mata é o kernel, e o motivo fica no Last State. Filtrar eventos por "OOM" não encontra nada. Se aparecer Unhealthy seguido de Killing, é a liveness probe que também está reiniciando o container, e o último término pode trazer outro exit code (0 ou 143, conforme o processo trate o SIGTERM).

Para comparar com o uso real, kubectl top pod --containers mostra o consumo do instante (precisa do metrics-server); o pico está no seu Prometheus ou Grafana.

A correção

  1. Pegue o pico de memória de um período representativo (no Prometheus: max_over_time(container_memory_working_set_bytes[7d]), filtrando pelo container).
  2. Defina limits.memory acima do pico, com folga para crescimento. A documentação não prescreve um fator: escolha um e registre o motivo em comentário no manifesto.
  3. Suba requests.memory para o uso típico. Se request = limit em CPU e memória para todos os containers, o pod é da classe Guaranteed, a última a ser despejada por pressão no nó (documentação do Kubernetes, "Pod Quality of Service Classes").
  4. Faça o runtime caber no limite: JVM com -XX:MaxRAMPercentage, Node com --max-old-space-size, .NET com DOTNET_GCHeapHardLimitPercent.
deployment.yaml · pico medido no exemplo: 610Mi
containers:
  - name: checkout
    image: registry.loja.internal/checkout:2.14.0
    resources:
      requests:
        cpu: 250m
        memory: 640Mi
      limits:
        cpu: 500m
        memory: 800Mi
    env:
      - name: JAVA_TOOL_OPTIONS
        value: "-XX:MaxRAMPercentage=75.0"

avisoSubir o limite trata o sintoma. Se o pico cresce a cada semana, é vazamento de memória, e o próximo OOMKilled é questão de tempo. Compare o pico de uma semana com o de um mês.

Como aparece no Kubepier

O Kubepier mostra os eventos do pod com os avisos primeiro, com filtro por tipo, namespace e motivo. O diagnóstico por IA explica o erro a partir dos eventos e do log, em português, com a sua chave de API (Anthropic ou OpenAI). Abaixo, uma ilustração com dados fictícios.

Kubepier um produto
OOMKilled · 3×
Diagnóstico por IA · sua chave Anthropic ou OpenAI

Causa provável: o container tenta usar 150M com limite de 128Mi e o kernel o mata na subida. Evidência: Last State OOMKilled com exit code 137 e 3 reinícios em 75 s; os eventos mostram só BackOff. Correção sugerida: subir limits.memory acima do pico medido ou reduzir o que a aplicação carrega na memória.

Conhecer o KubepierFree · Pro R$ 49/mês · Team R$ 199/mês