Resolva problemas de eventos OOM

Se as suas apps do Google Kubernetes Engine (GKE) estiverem a ter terminações inesperadas de contentores, instabilidade ou erros CrashLoopBackOff, pode dever-se a eventos de falta de memória (OOM). Os eventos OOM ocorrem quando um contentor ou um nó fica sem memória, o que faz com que o OOM Killer do Linux termine os processos para reclamar recursos.

Use esta página para identificar se ocorreu um evento de falta de memória ao nível do contentor ou do nó e aplique estratégias de mitigação eficazes para evitar uma recorrência.

Estas informações são importantes para os programadores de aplicações que precisam de garantir que as respetivas aplicações estão configuradas com pedidos e limites de memória adequados e que não têm fugas de memória. Também é importante para os administradores e operadores da plataforma que monitorizam a utilização de recursos dos nós e garantem que o cluster tem capacidade de memória adequada para suportar as cargas de trabalho implementadas. Para mais informações sobre as funções comuns e as tarefas de exemplo a que fazemos referência no conteúdo, consulte o artigo Funções e tarefas comuns do utilizador do GKE. Cloud de Confiance by S3NS

Causas comuns de eventos OOM

Normalmente, os eventos OOM ocorrem durante picos de carregamento ou tráfego, em que a utilização de memória da app aumenta e atinge o limite de memória configurado para o contentor.

Os seguintes cenários podem causar um evento OOM:

  • Limite de memória insuficiente: a definição resources.limits.memory no manifesto do pod é demasiado baixa para as exigências de memória típicas ou máximas da app.
  • Pedidos ou limites de memória indefinidos: se resources.limits.memory e resources.requests.memory não estiverem definidos, a utilização de memória do contentor não tem limites.
  • Carga elevada ou instável: os picos súbitos e extremos na carga podem sobrecarregar os recursos de um sistema, incluindo a memória, mesmo que os limites sejam normalmente adequados.
  • Fuga de memória: a app pode ter um defeito de código que faz com que não liberte a memória corretamente.

Os eventos OOM podem iniciar uma falha em cascata, porque restam menos contentores para processar o tráfego, o que aumenta a carga nos contentores restantes. Estes contentores também podem ser terminados.

Como o Kubernetes processa eventos de falta de memória

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

Num ambiente do Kubernetes, o OOM Killer opera em dois âmbitos diferentes: o grupo de controlo (cgroup), que afeta um contentor; e o sistema, que afeta todo o nó.

Eliminação por falta de memória ao nível do contentor

A eliminação por falta de memória ao nível do contentor ocorre quando um contentor tenta exceder o respetivo limite de memória predefinido. O Kubernetes atribui cada contentor a um cgroup específico com um limite de memória rígido. Quando a utilização de memória de um contentor atinge este limite, o kernel tenta primeiro recuperar memória nesse cgroup. Se o kernel não conseguir recuperar memória suficiente através deste processo, é invocado o OOM Killer do cgroup. Termina os processos nesse cgroup específico para aplicar o limite de recursos.

Quando o processo principal num contentor é terminado desta forma, o Kubernetes observa o evento e marca o estado do contentor como OOMKilled. O pod configurado restartPolicy determina o resultado:

  • Always ou OnFailure: o contentor é reiniciado.
  • Never: o contentor não é reiniciado e permanece num estado terminado.

Ao isolar a falha no contentor ofensivo, o OOM Killer impede que um único pod com falhas bloqueie todo o nó.

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

O comportamento de eliminação por falta de memória pode variar significativamente entre as versões do cgroup. Se não tiver a certeza de que versão de cgroup usa, verifique o modo cgroup dos nós do cluster.

  • Em cgroup v1, um evento OOM no cgroup de memória de um contentor pode levar a um comportamento imprevisível. O OOM Killer pode terminar qualquer processo nesse cgroup, incluindo processos secundários que não sejam o processo principal do contentor (PID 1).

    Este comportamento representa um desafio significativo para o Kubernetes. Uma vez que o Kubernetes monitoriza principalmente o estado do processo do contentor principal, permanece alheio a estas terminações de memória esgotada "parciais". O processo do contentor principal pode continuar a ser executado, mesmo que os processos secundários críticos tenham sido terminados. Este comportamento pode resultar em falhas subtis da app que não são imediatamente visíveis para o Kubernetes ou para os operadores, mas que continuam a ser visíveis no registo do sistema do nó (journalctl).

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

    Para ajudar a garantir a integridade da carga de trabalho num ambiente cgroup v2, o OOM killer impede eliminações parciais e garante um de dois resultados: todas as tarefas pertencentes a esse cgroup e aos seus descendentes são terminadas (tornando a falha visível para o Kubernetes) ou, quando a carga de trabalho não tem tarefas a usar demasiada memória, a carga de trabalho é deixada intacta e continua a ser executada sem terminações inesperadas de processos internos.

    Para cenários em que quer o comportamento cgroup v1 de terminar um único processo, o kubelet fornece a flag singleProcessOOMKill para cgroup v2. Esta flag dá-lhe um controlo mais detalhado, permitindo a terminação de processos individuais durante um evento OOM, em vez de todo o cgroup.

Eliminação por falta de memória ao nível do sistema

A eliminação por falta de memória ao nível do sistema é um evento mais grave que ocorre quando todo o nó, e não apenas um único contentor, fica sem memória disponível. Este evento pode ocorrer se a utilização de memória combinada de todos os processos (incluindo todos os pods e os daemons do sistema) exceder a capacidade do nó.

Quando este nó fica sem memória, o OOM Killer global avalia todos os processos no nó e termina um processo para reclamar memória para todo o sistema. Normalmente, o processo selecionado é de curta duração e usa uma grande quantidade de memória.

Para evitar situações graves de OOM, o Kubernetes usa a remoção de pressão do nó para gerir os recursos do nó. Este processo envolve a remoção de pods menos críticos de um nó quando os recursos, como a memória ou o espaço em disco, ficam criticamente baixos. Uma eliminação por falta de memória ao nível do sistema indica que este processo de despejo não conseguiu libertar memória com rapidez suficiente para evitar o problema.

Se o OOM Killer terminar o processo de um contentor, o efeito é normalmente idêntico a uma eliminação acionada por um cgroup: o contentor é marcado como OOMKilled e reiniciado com base na respetiva política. No entanto, se um processo crítico do sistema for terminado (o que é raro), o próprio nó pode ficar instável.

Investigue eventos de falta de memória

As secções seguintes ajudam a detetar e confirmar um evento de OOM, começando pelas ferramentas do Kubernetes mais simples e passando para uma análise de registos mais detalhada.

Verifique o estado do pod para eventos OOM visíveis

O primeiro passo para confirmar um evento OOM é verificar se o Kubernetes observou o evento OOM. O Kubernetes observa o evento quando o processo principal do contentor é terminado, o que é um comportamento padrão nos ambientes cgroup v2.

  • Inspeccione o estado do Pod:

    kubectl describe pod POD_NAME
    

    Substitua POD_NAME pelo nome do pod que quer investigar.

    Se ocorreu um evento OOM visível, o resultado é semelhante ao seguinte:

    ...
      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 vir OOMKilled no campo Reason, confirmou o evento. Um Exit Code de 137 também indica uma eliminação por falta de memória. Se o campo Reason tiver um valor diferente ou o pod ainda estiver em execução apesar dos erros da app, avance para a secção seguinte para uma investigação mais detalhada.

Pesquise registos de eventos OOM invisíveis

Uma eliminação por falta de memória é "invisível" para o Kubernetes se um processo secundário for eliminado, mas o processo do contentor principal continuar a ser executado (um cenário comum em ambientes cgroup v1). Tem de pesquisar os registos do nó para encontrar provas destes eventos.

Para encontrar eliminações por falta de memória invisíveis, use o Explorador de registos:

  1. Na Cloud de Confiance consola, aceda ao Explorador de registos.

    Aceda ao Explorador de registos

  2. No painel de consultas, introduza uma das seguintes consultas:

    • Se já tiver um pod que considere ter sofrido um evento OOM, 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 o seguinte:

      • POD_NAME: o nome do agrupamento que quer consultar.
      • CLUSTER_NAME: o nome do cluster ao qual o Pod pertence.
    • Para descobrir que 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. No resultado, localize eventos OOM pesquisando entradas de registo que contenham a string TaskOOM.

  5. Opcional: se pesquisou eventos de falta de memória para todas as cargas de trabalho do GKE e quiser identificar o pod específico que sofreu os eventos de falta de memória, conclua os seguintes passos:

    1. Para cada evento, tome nota do ID do contentor associado ao mesmo.
    2. Identifique as paragens do contentor procurando entradas de registo que contenham a string ContainerDied e que ocorreram pouco depois dos eventos OOM. Faça corresponder o ID do contentor do evento OOM à linha ContainerDied correspondente.

    3. Depois de encontrar a correspondência com container IDs, a linha ContainerDied inclui normalmente o nome do pod associado ao contentor com falhas. Este pod foi afetado pelo evento OOM.

Use o journalctl para obter informações em tempo real

Se precisar de realizar uma análise em tempo real do seu sistema, use journalctl comandos.

  1. Ligue-se ao nó através de SSH:

    gcloud compute ssh NODE_NAME --location ZONE
    

    Substitua o seguinte:

    • NODE_NAME: o nome do nó que quer examinar.
    • ZONE: a zona do Compute Engine à qual o seu nó pertence.
  2. Na shell, explore as mensagens do kernel do registo do sistema do nó:

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

    • Eliminação ao nível do contentor: a entrada do registo contém termos como memory cgroup, mem_cgroup ou memcg, que indicam que foi aplicada uma restrição de cgroup.
    • Eliminação ao nível do sistema: a entrada do registo é uma mensagem geral, como Out of memory: Killed process..., sem mencionar um cgroup.

Resolva eventos de falta de memória

Para resolver um evento de OOM, experimente as seguintes soluções:

  • Aumentar os limites de memória: esta é a solução mais direta. Edite o manifesto do Pod para fornecer um valor resources.limits.memory mais elevado que acomode o pico de utilização da app. Para mais informações sobre a definição de limites, consulte o artigo Gestão de recursos para pods e contentores na documentação do Kubernetes.
  • Adicione ou ajuste os pedidos de memória: no manifesto do Pod, verifique se o campo resources.requests.memory está definido para um valor realista para a utilização típica. Esta definição ajuda o Kubernetes a agendar o pod num nó com memória suficiente.
  • Escalar horizontalmente a carga de trabalho: para distribuir a carga de tráfego e reduzir a pressão de memória em qualquer Pod único, aumente o número de réplicas. Para que o Kubernetes dimensione proativamente a carga de trabalho, considere ativar a escala automática horizontal de pods.
  • Escalar os nós verticalmente: se muitos pods num nó estiverem perto dos respetivos limites, o próprio nó pode ser demasiado pequeno. Para aumentar o tamanho dos nós, migre as suas cargas de trabalho para um conjunto de nós com mais memória. Para que o Kubernetes dimensione proativamente os nós, considere ativar a escala automática vertical de pods.
  • Otimize a sua app: reveja a sua app para identificar e resolver fugas de memória e otimizar o código que consome grandes quantidades de memória durante picos de tráfego.
  • Elimine cargas de trabalho problemáticas: como último recurso para cargas de trabalho não críticas, elimine o pod para aliviar imediatamente a pressão sobre o cluster.

O que se segue?