Resolver problemas com eventos de OOM


Esta página ajuda a resolver problemas e eventos de falta de memória (OOM) no Google Kubernetes Engine (GKE). Aprenda a identificar as causas comuns de eventos de falta de memória (OOM), distinguir entre ocorrências no nível do contêiner e do nó e aplicar soluções.

Esta página é destinada a desenvolvedores de aplicativos que querem verificar se os apps foram implantados corretamente e a administradores e operadores de plataforma que querem entender a causa raiz dos eventos de falta de memória e verificar a configuração da plataforma. Para mais informações sobre papéis comuns e tarefas de exemplo que mencionamos no conteúdo do Google Cloud , consulte Funções e tarefas de usuário comuns do GKE Enterprise.

Causas comuns de eventos de OOM

Os eventos de OOM geralmente ocorrem durante picos de carga ou tráfego, quando o uso de memória do app aumenta e atinge o limite configurado para o contêiner.

Os seguintes cenários podem causar um evento de OOM:

  • Limite de memória insuficiente: a configuração resources.limits.memory no manifesto do pod está muito baixa para as demandas típicas ou máximas de memória do app.
  • Solicitações ou limites de memória indefinidos: se resources.limits.memory e resources.requests.memory não estiverem definidos, o uso de memória do contêiner será ilimitado.
  • Carga alta ou instável: picos repentinos e extremos na carga podem sobrecarregar os recursos de um sistema, incluindo a memória, mesmo que os limites sejam adequados.
  • Vazamento de memória: o app pode ter um defeito no código que faz com que ele não libere a memória corretamente.

Os eventos OOM podem iniciar uma falha em cascata, porque menos contêineres permanecem para processar o tráfego, aumentando a carga nos contêineres restantes. Esses contêineres também podem ser encerrados.

Como o Kubernetes processa eventos de falta de memória

O OOM Killer do Linux processa todos os eventos de OOM. O OOM Killer é um processo do kernel que é ativado quando o sistema está com pouca memória. O objetivo é evitar uma falha total do sistema encerrando processos estrategicamente para liberar recursos. O kernel usa um sistema de pontuação para selecionar qual processo interromper, com o objetivo de preservar a estabilidade do sistema e minimizar a perda de dados.

Em um ambiente do Kubernetes, o OOM Killer opera em dois escopos diferentes: o grupo de controle (cgroup), que afeta um contêiner, e o sistema, que afeta todo o nó.

Interrupção por falta de memória no nível do contêiner

Um encerramento por falta de memória no nível do contêiner ocorre quando um contêiner tenta exceder o limite de memória predefinido. O Kubernetes atribui cada contêiner a um cgroup específico com um limite de memória rígido. Quando o uso de memória de um contêiner atinge esse limite, o kernel primeiro tenta recuperar a memória dentro desse cgroup. Se o kernel não conseguir recuperar memória suficiente usando esse processo, o cgroup OOM Killer será invocado. Ele encerra processos nesse cgroup específico para aplicar o limite de recursos.

Quando o processo principal em um contêiner é encerrado dessa forma, o Kubernetes observa o evento e marca o status do contêiner como OOMKilled. O restartPolicy configurado do pod determina o resultado:

  • Always ou OnFailure: o contêiner é reiniciado.
  • Never: o contêiner não é reiniciado e permanece em um estado encerrado.

Ao isolar a falha no contêiner ofensivo, o OOM Killer impede que um único pod com falha cause a falha de todo o nó.

Como a versão do cgroup afeta o comportamento do OOM Killer

O comportamento de encerramento por falta de memória pode variar muito entre as versões do cgroup. Se você não tiver certeza de qual versão do cgroup usa, verifique o modo cgroup dos nós do cluster.

  • Em cgroup v1, um evento de falta de memória em um cgroup de memória de contêiner pode levar a um comportamento imprevisível. O OOM Killer pode encerrar qualquer processo dentro desse cgroup, incluindo processos filhos que não são o processo principal do contêiner (PID 1).

    Esse comportamento representa um desafio significativo para o Kubernetes. Como o Kubernetes monitora principalmente a integridade do processo principal do contêiner, ele não detecta essas interrupções parciais por falta de memória. O processo do contêiner principal pode continuar em execução, mesmo que processos filhos críticos tenham sido encerrados. Esse comportamento pode resultar em falhas sutis do app que não são imediatamente visíveis para o Kubernetes ou para os operadores, mas ainda são visíveis no registro do sistema do nó (journalctl).

  • O cgroup v2 oferece um comportamento mais previsível do OOM Killer.

    Para ajudar a garantir a integridade da carga de trabalho em um ambiente cgroup v2, o OOM killer impede encerramentos parciais e garante um de dois resultados: ou todas as tarefas pertencentes a esse cgroup e seus descendentes são encerradas (tornando a falha visível para o Kubernetes), ou, quando a carga de trabalho não tem tarefas usando muita memória, ela é deixada intacta e continua sendo executada sem encerramentos inesperados de processos internos.

    Para cenários em que você quer o comportamento cgroup v1 de encerrar um único processo, o kubelet fornece a flag singleProcessOOMKill para cgroup v2. Essa flag oferece um controle mais granular, permitindo a interrupção de processos individuais durante um evento de falta de memória, em vez de todo o cgroup.

Interrupção por falta de memória no nível do sistema

Um encerramento de OOM no nível do sistema é um evento mais grave que ocorre quando o nó inteiro, e não apenas um único contêiner, fica sem memória disponível. Isso pode acontecer se o uso combinado da memória de todos os processos (incluindo todos os pods e daemons do sistema) exceder a capacidade do nó.

Quando esse nó fica sem memória, o OOM Killer global avalia todos os processos no nó e encerra um processo para recuperar memória para todo o sistema. O processo selecionado geralmente é de curta duração e usa uma grande quantidade de memória.

Para evitar situações graves de falta de memória, o Kubernetes usa a remoção por pressão no nó para gerenciar os recursos do nó. Esse processo envolve a remoção de pods menos críticos de um nó quando os recursos, como memória ou espaço em disco, ficam muito baixos. Um encerrar OOM no nível do sistema indica que esse processo de remoção não conseguiu liberar memória com rapidez suficiente para evitar o problema.

Se o OOM Killer encerrar o processo de um contêiner, o efeito geralmente será idêntico a um encerramento acionado por cgroup: o contêiner será marcado como OOMKilled e reiniciado com base na política dele. No entanto, se um processo crítico do sistema for encerrado (o que é raro), o próprio nó poderá ficar instável.

Investigar eventos de OOM

As seções a seguir ajudam você a detectar e confirmar um evento de falta de memória, começando com as ferramentas mais simples do Kubernetes e passando para uma análise de registros mais detalhada.

Verificar o status do pod para eventos de OOM visíveis

A primeira etapa para confirmar um evento de falta de memória é verificar se o Kubernetes observou o evento. O Kubernetes observa o evento quando o processo principal do contêiner é encerrado, o que é um comportamento padrão em ambientes cgroup v2.

  • Inspecione o status do pod:

    kubectl describe pod POD_NAME
    

    Substitua POD_NAME pelo nome do pod que você quer investigar.

    Se um evento OOM visível ocorreu, a saída será semelhante a esta:

    ...
      Last State:     Terminated
        Reason:       OOMKilled
        Exit Code:    137
        Started:      Tue, 13 May 2025 19:05:28 +0000
        Finished:     Tue, 13 May 2025 19:05:30 +0000
    ...
    

Se você vir OOMKilled no campo Reason, isso significa que o evento foi confirmado. Um Exit Code de 137 também indica uma interrupção por falta de memória. Se o campo Reason tiver um valor diferente ou se o pod ainda estiver em execução apesar dos erros do app, siga para a próxima seção para investigar mais.

Pesquisar registros de eventos OOM invisíveis

Um encerramento por falta de memória é "invisível" para o Kubernetes se um processo filho for encerrado, mas o processo principal do contêiner continuar em execução (um cenário comum em ambientes cgroup v1 ). Você precisa pesquisar os registros do nó para encontrar evidências desses eventos.

Para encontrar encerramentos de OOM invisíveis, use o Explorador de registros:

  1. No console Google Cloud , acesse o Explorador de registros.

    Acessar o Explorador de registros

  2. No painel de consulta, insira uma das seguintes consultas:

    • Se você já tiver um pod que acredita ter sofrido um evento de falta de memória, consulte esse pod específico:

      resource.type="k8s_node"
      jsonPayload.MESSAGE:(("POD_NAME" AND "ContainerDied") OR "TaskOOM event")
      resource.labels.cluster_name="CLUSTER_NAME"
      

      Substitua:

      • POD_NAME: o nome do pod que você quer consultar.
      • CLUSTER_NAME: o nome do cluster a que o pod pertence.
    • Para descobrir quais pods ou nós sofreram um evento OOM, consulte todas as cargas de trabalho do GKE:

      resource.type="k8s_node"
      jsonPayload.MESSAGE:("ContainerDied" OR "TaskOOM event")
      resource.labels.cluster_name="CLUSTER_NAME"
      
  3. Clique em Executar consulta.

  4. Na saída, localize eventos de falta de memória pesquisando entradas de registro que contenham a string TaskOOM.

  5. Opcional: se você pesquisou eventos de falta de memória para todas as cargas de trabalho do GKE e quer identificar o pod específico que passou por esses eventos, siga estas etapas:

    1. Para cada evento, anote o ID do contêiner associado a ele.
    2. Identifique as interrupções do contêiner procurando entradas de registro que contenham a string ContainerDied e que ocorreram logo após os eventos OOM. Correlacione o ID do contêiner do evento OOM com a linha ContainerDied correspondente.

    3. Depois de corresponder o container IDs, a linha ContainerDied geralmente inclui o nome do pod associado ao contêiner com falha. Esse pod foi afetado pelo evento OOM.

Usar journalctl para informações em tempo real

Se você precisar fazer uma análise em tempo real do sistema, use os comandos journalctl.

  1. Conecte-se ao nó usando SSH:

    gcloud compute ssh NODE_NAME --location ZONE
    

    Substitua:

    • NODE_NAME: o nome do nó que você quer examinar.
    • ZONE: a zona do Compute Engine a que seu nó pertence.
  2. No shell, confira as mensagens do kernel no diário do sistema do nó:

    journalctl -k
    
  3. Analise a saída para distinguir o tipo de evento:

    • Eliminação no nível do contêiner: a entrada de registro contém termos como memory cgroup, mem_cgroup ou memcg, que indicam que um limite de cgroup foi aplicado.
    • Eliminação no nível do sistema: a entrada de registro é uma mensagem geral, como Out of memory: Killed process..., sem mencionar um cgroup.

Resolver eventos de falta de memória

Para resolver um evento de falta de memória, tente as seguintes soluções:

  • Aumente os limites de memória: essa é a solução mais direta. Edite o manifesto do pod para fornecer um valor resources.limits.memory maior que acomode o uso máximo do app. Para mais informações sobre como definir limites, consulte Gerenciamento de recursos para pods e contêineres na documentação do Kubernetes.
  • Adicionar ou ajustar solicitações de memória: no manifesto do pod, verifique se o campo resources.requests.memory está definido como um valor realista para o uso típico. Essa configuração ajuda o Kubernetes a programar o pod em um nó com memória suficiente.
  • Escalone horizontalmente a carga de trabalho: para distribuir a carga de tráfego e reduzir a pressão de memória em um único pod, aumente o número de réplicas. Para que o Kubernetes faça o escalonamento proativo da carga de trabalho, considere ativar o escalonamento automático horizontal de pods.
  • Escalonar verticalmente os nós: se muitos pods em um nó estiverem perto dos limites, o nó em si poderá ser muito pequeno. Para aumentar o tamanho dos nós, migre suas cargas de trabalho para um pool de nós com mais memória. Para que o Kubernetes faça o escalonamento proativo dos nós, considere ativar o escalonamento automático vertical de pods.
  • Otimize seu app: revise o app para identificar e resolver vazamentos de memória e otimizar o código que consome grandes quantidades de memória durante picos de tráfego.
  • Exclua cargas de trabalho problemáticas: como último recurso para cargas de trabalho não críticas, exclua o pod para aliviar imediatamente a pressão no cluster.

A seguir