Solução de Problemas: Por Que Meu Pod do Kubernetes Está Preso em Pending ou CrashLoopBackOff?

Pods do Kubernetes presos em `Pending` ou `CrashLoopBackOff` podem interromper implantações. Este guia abrangente desmistifica esses estados comuns, oferecendo solução de problemas prática e passo a passo. Aprenda a diagnosticar problemas como restrições de recursos, erros de pull de imagem, falhas de aplicação e configurações incorretas de probe usando comandos `kubectl`. Capacite-se com insights acionáveis e melhores práticas para resolver rapidamente problemas de Pod e manter um ambiente Kubernetes robusto e confiável, garantindo que suas aplicações estejam sempre em execução.

Solução de Problemas: Por Que Meu Pod do Kubernetes Está Preso em Pending ou CrashLoopBackOff?

Pending e CrashLoopBackOff parecem semelhantes quando você está esperando por um rollout, mas significam coisas muito diferentes. Pending geralmente significa que o Kubernetes não conseguiu colocar ou preparar o pod. CrashLoopBackOff significa que o contêiner iniciou, depois saiu, e o Kubernetes está atrasando a próxima reinicialização.

Essa diferença importa. Um pod pendente geralmente é um problema de agendador, imagem ou armazenamento. Um pod travando geralmente é um problema de aplicação, comando, probe, permissão ou memória. Comece com essa divisão e o caminho de solução de problemas fica muito mais curto.

Entendendo os Estados do Pod: Pending vs. CrashLoopBackOff

Antes de mergulhar na solução de problemas, é essencial entender o que esses dois estados significam.

Status do Pod: Pending

Um Pod no estado Pending significa que o Kubernetes aceitou o objeto Pod, mas ele não passou totalmente para um estado de contêiner em execução. Às vezes, ele não foi agendado em um nó. Às vezes, ele tem um nó atribuído, mas o pull da imagem, a anexação do volume ou a configuração do sandbox não foram concluídos.

Status do Pod: CrashLoopBackOff

Um Pod em CrashLoopBackOff significa que um contêiner dentro do Pod está repetidamente iniciando, travando e reiniciando. O Kubernetes implementa um atraso de back-off exponencial entre as reinicializações para evitar sobrecarregar o nó. Esse estado quase sempre aponta para um problema com a aplicação em execução dentro do próprio contêiner ou seu ambiente imediato.

Um caso sutil: um contêiner pode sair com o código 0 e ainda entrar em um loop de reinicialização se a carga de trabalho for supostamente um servidor de longa duração. Isso geralmente acontece quando um Deployment executa um comando único por engano, como um script de migração ou um comando shell que termina imediatamente.

Solucionando Problemas de Pods no Estado Pending

Quando um Pod está Pending, o primeiro lugar para olhar é o agendador e o nó no qual ele está tentando entrar. Aqui estão as causas comuns e as etapas de diagnóstico.

1. Recursos Insuficientes nos Nós

Uma das razões mais frequentes para um Pod estar Pending é que nenhum nó no cluster tem recursos disponíveis suficientes (CPU, memória) para satisfazer as requests do Pod. O agendador não consegue encontrar um nó adequado.

Etapas de Diagnóstico:

  1. Descreva o Pod: O comando kubectl describe pod é seu melhor amigo aqui. Ele geralmente mostrará eventos detalhando por que o Pod não pode ser agendado.

    kubectl describe pod <nome-do-pod> -n <namespace>
    

    Procure por eventos como "FailedScheduling" e mensagens como "0/3 nodes are available: 3 Insufficient cpu" ou "memory".

  2. Verifique os Recursos do Nó: Veja o uso atual de recursos e a capacidade de seus nós.

    kubectl get nodes
    kubectl top nodes # (requer metrics-server)
    

Solução:

  • Aumente a Capacidade do Cluster: Adicione mais nós ao seu cluster Kubernetes.
  • Ajuste as Solicitações de Recursos do Pod: Reduza as requests de CPU e memória no manifesto do seu Pod se elas estiverem muito altas.
    resources:
      requests:
        memory: "128Mi"
        cpu: "250m"
    
  • Esvazie Outros Pods: Esvazie manualmente Pods de prioridade mais baixa dos nós para liberar recursos (use com cuidado).

2. Erros de Pull de Imagem

Se o Kubernetes consegue agendar o Pod em um nó, mas o nó falha ao puxar a imagem do contêiner, o Pod permanecerá Pending.

Causas Comuns:

  • Nome/Tag de Imagem Incorreto: Erros de digitação no nome da imagem ou uso de uma tag inexistente.
  • Autenticação de Registro Privado: ImagePullSecrets ausentes ou incorretos para registros privados.
  • Problemas de Rede: Nó incapaz de alcançar o registro de imagem.

Etapas de Diagnóstico:

  1. Descreva o Pod: Novamente, kubectl describe pod é fundamental. Procure por eventos como "Failed" ou "ErrImagePull" ou "ImagePullBackOff".

    kubectl describe pod <nome-do-pod> -n <namespace>
    

    Exemplo de evento de saída: Failed to pull image "my-private-registry/my-app:v1.0": rpc error: code = Unknown desc = Error response from daemon: pull access denied for my-private-registry/my-app, repository does not exist or may require 'docker login'

  2. Verifique ImagePullSecrets: Verifique se imagePullSecrets estão configurados corretamente em seu Pod ou ServiceAccount.

    kubectl get secret <seu-image-pull-secret> -o yaml -n <namespace>
    

Solução:

  • Corrija Nome/Tag da Imagem: Verifique novamente o nome e a tag da imagem no manifesto de sua implantação.
  • Configure ImagePullSecrets: Certifique-se de ter criado um segredo docker-registry e vinculado-o ao seu Pod ou ServiceAccount.
    kubectl create secret docker-registry my-registry-secret \n      --docker-server=seu-registro.com \n      --docker-username=seu-usuario \n      --docker-password=sua-senha \n      --docker-email=seu-email -n <namespace>
    
    Em seguida, adicione-o à especificação do seu Pod:
    spec:
      imagePullSecrets:
      - name: my-registry-secret
      containers:
      ...
    
  • Conectividade de Rede: Verifique a conectividade de rede do nó ao registro de imagem.

Se você estiver usando um registro privado, verifique também o ServiceAccount. Muitas equipes anexam imagePullSecrets ao ServiceAccount padrão do namespace em vez de a cada Deployment:

kubectl get serviceaccount default -n <namespace> -o yaml

Se o segredo existir, mas o pull ainda falhar, confirme se o nome do host do registro no segredo corresponde exatamente ao nome do host na referência da imagem. registry.example.com/app:v1 e https://registry.example.com/app:v1 não são a mesma referência.

3. Problemas Relacionados a Volumes

Se seu Pod requer um PersistentVolumeClaim (PVC) e o PersistentVolume (PV) correspondente não pode ser provisionado ou vinculado, o Pod permanecerá Pending.

Etapas de Diagnóstico:

  1. Descreva o Pod: Procure por eventos relacionados a volumes.

    kubectl describe pod <nome-do-pod> -n <namespace>
    

    Eventos podem mostrar FailedAttachVolume, FailedMount ou mensagens semelhantes.

  2. Verifique o Status do PVC e PV: Inspecione o status do PVC e PV.

    kubectl get pvc <nome-do-pvc> -n <namespace>
    kubectl get pv
    

    Procure por PVCs presos em Pending ou PVs não vinculados.

Solução:

  • Garanta StorageClass: Certifique-se de que uma StorageClass esteja definida e disponível, especialmente se estiver usando provisionamento dinâmico.
  • Verifique a Disponibilidade do PV: Se estiver usando provisionamento estático, certifique-se de que o PV exista e corresponda aos critérios do PVC.
  • Verifique os Modos de Acesso: Certifique-se de que os modos de acesso (por exemplo, ReadWriteOnce, ReadWriteMany) sejam compatíveis.

Verifique também se o pod está agendado em uma zona onde o volume pode ser anexado. Em clusters em nuvem, um disco criado em uma zona de disponibilidade pode não ser anexado a um nó em outra. O evento geralmente menciona afinidade de nó de volume ou falha de anexação. Nesse caso, a correção pode ser restrições de agendamento, uma StorageClass diferente ou recriar o volume na zona correta.

4. Taints, Tolerations e Node Selectors

Um pod pode permanecer Pending mesmo quando o cluster tem bastante CPU e memória. O agendador também precisa respeitar as regras de posicionamento.

Exemplos comuns:

  • O pod tem um nodeSelector que não corresponde a nenhum nó.
  • O pod requer afinidade de nó muito restritiva.
  • Os únicos nós correspondentes têm taints, e o pod não tem toleration correspondente.
  • O namespace tem uma cota que bloqueia os recursos solicitados.

Verifique os eventos de agendamento primeiro:

kubectl describe pod <nome-do-pod> -n <namespace>

Em seguida, compare as regras de posicionamento do pod com os rótulos do nó:

kubectl get pod <nome-do-pod> -n <namespace> -o yaml
kubectl get nodes --show-labels
kubectl describe node <nome-do-no>

Se o evento disser que um taint não foi tolerado, agende o pod em outro lugar ou adicione uma toleration somente se essa carga de trabalho realmente pertencer àqueles nós. Não tolere cegamente todos os taints. Taints geralmente protegem nós especiais, nós GPU, nós de infraestrutura ou nós sob pressão.

Solucionando Problemas de Pods no Estado CrashLoopBackOff

Um estado CrashLoopBackOff indica um problema no nível da aplicação. O contêiner iniciou com sucesso, mas depois saiu com um erro, fazendo com que o Kubernetes o reinicie repetidamente.

1. Erros de Aplicação

A causa mais comum é a própria aplicação falhando ao iniciar ou encontrando um erro fatal logo após iniciar.

Causas Comuns:

  • Dependências/Configuração Ausentes: A aplicação não consegue encontrar arquivos de configuração críticos, variáveis de ambiente ou serviços externos dos quais depende.
  • Comando/Argumentos Incorretos: O command ou args especificado na especificação do contêiner está incorreto ou leva a uma saída imediata.
  • Erros de Lógica da Aplicação: Bugs no código da aplicação que a fazem travar na inicialização.

Etapas de Diagnóstico:

  1. Veja os Logs do Pod: Este é o passo mais crítico. Os logs geralmente mostrarão a mensagem de erro exata que fez a aplicação travar.

    kubectl logs <nome-do-pod> -n <namespace>
    

    Se o Pod estiver travando repetidamente, os logs podem mostrar a saída da tentativa falha mais recente. Para ver logs de uma instância anterior de um contêiner travando, use a flag -p (previous):

    kubectl logs <nome-do-pod> -p -n <namespace>
    
  2. Descreva o Pod: Procure por Restart Count na seção Containers, que indica quantas vezes o contêiner travou. Além disso, verifique Last State para Exit Code.

    kubectl describe pod <nome-do-pod> -n <namespace>
    

    Um código de saída 0 geralmente significa um desligamento gracioso, mas qualquer código de saída diferente de zero significa um erro. Códigos de saída comuns diferentes de zero incluem 1 (erro geral), 137 (SIGKILL, geralmente OOMKilled), 139 (SIGSEGV, falha de segmentação).

Solução:

  • Revise os Logs da Aplicação: Com base nos logs, depure o código da sua aplicação ou configuração. Certifique-se de que todas as variáveis de ambiente, ConfigMaps e Secrets necessários estejam montados/injetados corretamente.
  • Teste Localmente: Tente executar a imagem do contêiner localmente com as mesmas variáveis de ambiente e comandos para reproduzir e depurar o problema.

Se o pod tiver vários contêineres, sempre especifique o nome do contêiner:

kubectl logs <nome-do-pod> -c <nome-do-container> -n <namespace>
kubectl logs <nome-do-pod> -c <nome-do-container> -p -n <namespace>

Sem -c, você pode estar lendo os logs do sidecar enquanto o aplicativo principal é o que está travando.

2. Falha nas Sondas de Liveness e Readiness

O Kubernetes usa sondas de Liveness e Readiness para determinar a saúde e disponibilidade da sua aplicação. Se uma sonda de liveness falhar continuamente, o Kubernetes reiniciará o contêiner, levando ao CrashLoopBackOff.

Etapas de Diagnóstico:

  1. Descreva o Pod: Verifique as definições das sondas Liveness e Readiness e seu Last State na seção Containers.

    kubectl describe pod <nome-do-pod> -n <namespace>
    

    Procure por mensagens indicando falhas de sonda, como "Liveness probe failed: HTTP probe failed with statuscode: 500".

  2. Revise os Logs da Aplicação: Às vezes, os logs da aplicação fornecerão contexto sobre por que o endpoint da sonda está falhando.

Solução:

  • Ajuste a Configuração da Sonda: Corrija o path, port, command, initialDelaySeconds, periodSeconds ou failureThreshold da sonda.
  • Garanta a Saúde do Endpoint da Sonda: Verifique se o endpoint da aplicação visado pela sonda está realmente saudável e respondendo conforme o esperado. A aplicação pode estar demorando muito para iniciar, exigindo um initialDelaySeconds maior.

Para aplicações de inicialização lenta, considere um startupProbe. Ele dá à aplicação mais tempo para inicializar antes que a sonda de liveness comece a julgá-la. Isso é mais limpo do que definir um enorme initialDelaySeconds de liveness para cada reinicialização.

3. Limites de Recursos Excedidos

Se um contêiner consistentemente tenta usar mais memória do que seu memory.limit ou é limitado por CPU devido à excedência de seu cpu.limit, o kernel pode encerrar o processo, geralmente com um evento OOMKilled (Out Of Memory Killed).

Etapas de Diagnóstico:

  1. Descreva o Pod: Procure por OOMKilled na seção Last State ou Events. Um Exit Code: 137 geralmente indica um evento OOMKilled.

    kubectl describe pod <nome-do-pod> -n <namespace>
    
  2. Verifique kubectl top: Se metrics-server estiver instalado, use kubectl top pod para ver o uso real de recursos de seus Pods.

    kubectl top pod <nome-do-pod> -n <namespace>
    

Solução:

  • Aumente os Limites de Recursos: Se sua aplicação realmente precisa de mais recursos, aumente os limits de memory e/ou cpu no manifesto do seu Pod. Isso pode exigir mais capacidade em seus nós.
    resources:
      requests:
        memory: "256Mi"
        cpu: "500m"
      limits:
        memory: "512Mi" # Aumente isso
        cpu: "1000m"   # Aumente isso
    
  • Otimize a Aplicação: Perfile sua aplicação para identificar e reduzir seu consumo de recursos.

4. Problemas de Permissão

Os contêineres podem travar se não tiverem as permissões necessárias para acessar arquivos, diretórios ou recursos de rede de que precisam.

Etapas de Diagnóstico:

  1. Revise os Logs: Os logs da aplicação podem mostrar erros de permissão negada (EACCES).
  2. Descreva o Pod: Verifique o ServiceAccount sendo usado e quaisquer configurações de securityContext montadas.

Solução:

  • Ajuste securityContext: Defina runAsUser, fsGroup ou allowPrivilegeEscalation conforme necessário.
  • Permissões do ServiceAccount: Certifique-se de que o ServiceAccount associado ao Pod tenha os Roles e ClusterRoles necessários vinculados via RoleBindings e ClusterRoleBindings.
  • Permissões de Volume: Certifique-se de que os volumes montados (por exemplo, emptyDir, hostPath, ConfigMap, Secret) tenham as permissões corretas para o usuário do contêiner.

Uma Árvore de Decisão Rápida

Quando alguém disser "o pod está quebrado", execute estes comandos em ordem:

kubectl get pod <nome-do-pod> -n <namespace> -o wide
kubectl describe pod <nome-do-pod> -n <namespace>
kubectl logs <nome-do-pod> -n <namespace> --all-containers=true --tail=100
kubectl logs <nome-do-pod> -n <namespace> --all-containers=true -p --tail=100

Em seguida, ramifique:

  • Se não houver nó em kubectl get pod -o wide, concentre-se no agendamento: requests, taints, afinidade, cota e disponibilidade do nó.
  • Se houver um nó, mas o evento mencionar pull de imagem, concentre-se no nome da imagem, tag, autenticação do registro e acesso de rede do nó ao registro.
  • Se o evento mencionar montagem ou anexação, concentre-se em PVCs, PVs, StorageClass, modos de acesso e posicionamento de zona.
  • Se o pod iniciar e depois reiniciar, concentre-se em logs, código de saída, sondas, comando/args, configuração, segredos e limites de memória.

Esta ordem evita um erro comum: ler logs de aplicação para um pod que nunca iniciou um contêiner de aplicação.

Lendo Códigos de Saída Sem Reagir Exageradamente

Códigos de saída são pistas, não explicações completas.

  • 1 geralmente significa que a aplicação retornou um erro geral. Os logs são mais importantes que o número.
  • 2 pode apontar para erros de uso de linha de comando em muitos programas.
  • 126 geralmente significa que o comando existe, mas não pode ser executado.
  • 127 geralmente significa que o comando não foi encontrado.
  • 137 aparece comumente quando o processo recebe SIGKILL; no Kubernetes, isso geralmente está, mas nem sempre, conectado ao OOMKilled.
  • 143 significa que o processo recebeu SIGTERM, o que pode acontecer durante a terminação normal.

Se o código de saída for 137, verifique o Last State e os eventos do pod antes de assumir um vazamento de memória. Um drain de nó, evicção ou kill manual também pode encerrar um contêiner.

Etapas e Ferramentas Gerais de Diagnóstico

Aqui está uma lista de verificação rápida de comandos para executar ao enfrentar problemas de Pod:

  • Obtenha uma Visão Geral Rápida: Verifique o status de seus Pods.
    kubectl get pods -n <namespace>
    kubectl get pods -n <namespace> -o wide
    
  • Informações Detalhadas do Pod: O comando mais crucial para entender eventos, estados e condições do Pod.
    kubectl describe pod <nome-do-pod> -n <namespace>
    
  • Logs do Contêiner: Veja o que sua aplicação está relatando.
    kubectl logs <nome-do-pod> -n <namespace>
    kubectl logs <nome-do-pod> -p -n <namespace> # Instância anterior
    kubectl logs <nome-do-pod> -f -n <namespace> # Seguir logs
    
  • Eventos em Todo o Cluster: Às vezes, o problema não é com um Pod específico, mas um evento em todo o cluster (por exemplo, pressão no nó).
    kubectl get events -n <namespace>
    
  • Depuração Interativa: Se seu contêiner iniciar, mas travar rapidamente, você pode ser capaz de executar exec nele por um breve momento ou em um contêiner de depuração separado, se configurado.
    kubectl exec -it <nome-do-pod> -n <namespace> -- bash
    
    (Nota: Isso funciona apenas se o contêiner permanecer vivo tempo suficiente para anexar.)

Melhores Práticas para Evitar Problemas de Pod

Prevenção é sempre melhor que cura. Seguir estas melhores práticas pode reduzir significativamente incidentes de Pending e CrashLoopBackOff:

  • Defina Solicitações e Limites de Recursos Realistas: Comece com requests e limits razoáveis, depois ajuste-os com base na criação de perfil e monitoramento da aplicação.
  • Use Tags de Imagem Específicas: Evite tags latest em produção. Use tags imutáveis (por exemplo, v1.2.3, commit-sha) para reprodutibilidade.
  • Implemente Sondas Robusta: Configure sondas liveness e readiness que reflitam com precisão a saúde da sua aplicação. Considere os tempos de inicialização com initialDelaySeconds.
  • Logging e Monitoramento Centralizados: Use ferramentas como Prometheus, Grafana, ELK stack ou serviços de logging nativos da nuvem para coletar e analisar logs e métricas de Pod.
  • Controle de Versão para Manifestos: Armazene seus manifestos Kubernetes em um sistema de controle de versão (por exemplo, Git) para rastrear alterações e facilitar rollbacks.
  • Testes Completos: Teste suas imagens de contêiner e implantações Kubernetes em ambientes de desenvolvimento e staging antes de implantar em produção.
  • Desligamentos Gracisos: Certifique-se de que suas aplicações lidem com sinais SIGTERM para desligamentos graciosos, permitindo que liberem recursos antes da terminação.

O Que Geralmente Corrige Mais Rápido

Para Pending, kubectl describe pod é geralmente o caminho mais rápido porque os eventos do agendador e kubelet explicam o que o Kubernetes não conseguiu fazer. Para CrashLoopBackOff, logs anteriores são geralmente o caminho mais rápido porque o contêiner atual pode ser muito novo para mostrar a falha que causou o loop.

Depois de corrigir o problema imediato, procure a etapa de prevenção: requests com tamanho adequado, melhores tags de imagem, uma sonda de inicialização, uma verificação de segredo ausente no CI ou um runbook mais claro. O melhor incidente de pod é aquele que se torna mais fácil de reconhecer na próxima vez.