Solução de Problemas de Falhas em Pods Kubernetes: Um Guia Abrangente

Navegue pelas complexidades das falhas de Pods Kubernetes com este guia abrangente. Aprenda o processo estruturado para diagnosticar problemas comuns como CrashLoopBackOff, ImagePullBackOff e exaustão de recursos. Detalhamos como aproveitar ferramentas cruciais como `kubectl describe` e `kubectl logs --previous` para identificar a causa raiz, interpretar estados de saída de contêineres e implementar correções práticas para manter a disponibilidade e estabilidade confiável do aplicativo.

Solução de Problemas de Falhas em Pods Kubernetes: Um Guia Abrangente

A solução de problemas de falhas em pods Kubernetes envolve menos memorizar cada status e mais aprender onde o Kubernetes deixa pistas. Um pod quase nunca falha "silenciosamente". O scheduler, kubelet, runtime do contêiner, registro de imagem, plugin de volume e seu aplicativo deixam rastros em lugares diferentes. O truque é verificá-los na ordem certa para não gastar vinte minutos lendo logs de aplicação de um pod que nunca puxou sua imagem.

Geralmente começo com uma pergunta: o pod falhou antes do contêiner iniciar, durante a inicialização do contêiner ou depois que o aplicativo começou a executar? Essa única divisão mantém a investigação fundamentada. Pending geralmente aponta para problemas de agendamento, armazenamento ou configuração de imagem. ImagePullBackOff aponta para o caminho do registro, tag, credenciais ou egresso do nó. CrashLoopBackOff geralmente significa que o processo inicia e depois sai, embora o motivo possa ser configuração, um arquivo ausente, um comando incorreto, uma dependência com falha ou pressão de memória.


Os Três Pilares do Diagnóstico de Pods

A solução de problemas começa utilizando três comandos primários do kubectl para reunir todas as informações disponíveis sobre o Pod com falha.

1. Verificação de Status Inicial (kubectl get pods)

O primeiro passo é sempre determinar o estado atual do Pod e seus contêineres. Preste muita atenção às colunas STATUS e READY.

kubectl get pods -n my-namespace

Interpretando o Status do Pod

Status Significado Ação Inicial
Running Pelo menos um contêiner está em execução; isso nem sempre significa que o aplicativo está servindo tráfego. Verifique READY, reinicializações e eventos de prontidão.
Pending O Pod foi aceito pelo Kubernetes, mas os contêineres ainda não foram criados. Verifique eventos de agendamento ou status de pull de imagem.
CrashLoopBackOff O contêiner inicia, trava e o Kubelet tenta reiniciá-lo repetidamente. Verifique os logs do aplicativo (kubectl logs --previous).
ImagePullBackOff O Kubelet não consegue puxar a imagem do contêiner necessária. Verifique o nome da imagem, tag e credenciais do registro.
Error O Pod foi encerrado devido a um erro de runtime ou comando de inicialização com falha. Verifique logs e eventos describe.
Terminating/Unknown O Pod está sendo encerrado ou o host Kubelet não está respondendo. Verifique a saúde do nó.

2. Inspeção Profunda (kubectl describe pod)

Se o status for diferente de Running, o comando describe fornece contexto crucial, detalhando decisões de agendamento, alocação de recursos e estados dos contêineres.

kubectl describe pod [NOME_DO_POD] -n my-namespace

Concentre-se nestas seções na saída:

  • Containers/Init Containers: Verifique o State (especialmente Waiting ou Terminated) e o Last State (onde o motivo da falha é frequentemente registrado, por exemplo, Reason: OOMKilled).
  • Resource Limits: Verifique se Limits e Requests estão configurados corretamente.
  • Events: Esta é a seção mais crítica. Os eventos mostram o histórico do ciclo de vida, incluindo falhas de agendamento, problemas de montagem de volume e tentativas de pull de imagem.

Dica: Se a seção Events mostrar uma mensagem como "0/N nodes available", o Pod provavelmente está falhando ao agendar devido a recursos insuficientes (CPU, memória) ou regras de afinidade não atendidas.

Leia os eventos de baixo para cima quando quiser a pista mais recente, mas não ignore eventos mais antigos. Um pod pode ter mais de um problema. Por exemplo, uma implantação pode começar com FailedScheduling porque a memória solicitada é muito alta, e depois passar para ImagePullBackOff após um nó ser adicionado. Se você olhar apenas para o status final, pode perder a mudança que fez o problema avançar.

3. Revisando Logs (kubectl logs)

Para problemas de runtime do aplicativo, os logs fornecem o stack trace ou mensagens de erro explicando por que o processo foi encerrado.

# Verificar logs atuais do contêiner
kubectl logs [NOME_DO_POD] -n my-namespace

# Verificar logs de um contêiner específico dentro do Pod
kubectl logs [NOME_DO_POD] -c [NOME_DO_CONTÊINER] -n my-namespace

# Crucial para CrashLoopBackOff: Verificar os logs da execução *anterior* que falhou
kubectl logs [NOME_DO_POD] --previous -n my-namespace

Se o pod tiver sidecars, sempre inclua -c. Muitas investigações frustrantes vêm da leitura dos logs do sidecar saudável em vez do contêiner do aplicativo com falha. Para falhas de contêineres init, use o nome do contêiner init com -c também:

kubectl logs [NOME_DO_POD] -c [NOME_DO_CONTÊINER_INIT] -n my-namespace

Cenários Comuns de Falha de Pod e Soluções

A maioria das falhas de Pod se enquadra em alguns padrões reconhecíveis, cada um exigindo uma abordagem de diagnóstico direcionada.

Cenário 1: CrashLoopBackOff

Esta é a falha de Pod mais frequente. Significa que o contêiner está iniciando com sucesso, mas o aplicativo dentro do contêiner está saindo imediatamente (com um código de saída diferente de zero).

Diagnóstico:

  1. Use kubectl logs --previous para visualizar o traceback ou o motivo da saída.
  2. Use kubectl describe para verificar o Exit Code na seção Last State.

Causas e Correções Comuns:

  • Código de Saída 1/2: Erro geral do aplicativo, arquivo de configuração ausente, falha de conectividade com banco de dados ou travamento do aplicativo devido a entrada incorreta.
    • Correção: Depure o código do aplicativo ou verifique ConfigMaps/Secrets que estão sendo montados.
  • Dependências Ausentes: O script de ponto de entrada requer arquivos ou ambientes que não estão presentes.
    • Correção: Verifique o Dockerfile e o processo de construção da imagem.
  • Comando ou args incorretos: A imagem do contêiner é válida, mas o comando na especificação do Pod substitui o entrypoint da imagem incorretamente.
    • Correção: Compare o command e args do Deployment com o comando de inicialização esperado da imagem. Teste a mesma imagem localmente, se possível.
  • Reinicializações induzidas por probe: Uma sonda de liveness pode matar um aplicativo de inicialização lenta antes que ele termine de aquecer.
    • Correção: Aumente initialDelaySeconds, use um startupProbe ou aponte a sonda para um endpoint de saúde mais leve.

Um padrão prático é implantar uma cópia temporária com a mesma imagem, mas um comando inofensivo, e depois inspecionar o sistema de arquivos e o ambiente:

kubectl run debug-image \
  --image=registry.example.com/app:tag \
  --restart=Never \
  --command -- sleep 3600

kubectl exec -it debug-image -- /bin/sh

Isso não substitui a correção do Deployment, mas ajuda a responder perguntas simples rapidamente: o arquivo de configuração está realmente na imagem, o binário existe, o contêiner tem o shell esperado e as variáveis de ambiente estão presentes?

Cenário 2: ImagePullBackOff / ErrImagePull

Isso ocorre quando o Kubelet não consegue buscar a imagem do contêiner especificada na definição do Pod.

Diagnóstico:

  1. Verifique a seção Events do kubectl describe para a mensagem de erro específica (por exemplo, 404 Not Found ou authentication required).

Causas e Correções Comuns:

  • Erro de Digitação ou Tag Errada: O nome ou a tag da imagem estão incorretos.
    • Correção: Corrija o nome da imagem na especificação do Deployment ou Pod.
  • Acesso a Registro Privado: O cluster não tem credenciais para puxar de um registro privado (como Docker Hub, GCR, ECR).
    • Correção: Certifique-se de que um imagePullSecret apropriado seja referenciado na especificação do Pod e que o Secret exista no namespace.
# Exemplo de trecho de especificação de Pod para usar um pull secret
spec:
  containers:
  ...
  imagePullSecrets:
  - name: my-registry-secret

Verifique também onde o pull secret reside. Os secrets do Kubernetes têm namespace. Um secret chamado regcred no namespace default não ajudará um pod no namespace payments. Se a mesma imagem funciona em um namespace, mas falha em outro, compare as service accounts e image pull secrets antes de assumir que o registro está quebrado:

kubectl get serviceaccount default -n payments -o yaml
kubectl get secret regcred -n payments

Cenário 3: Status Pending (Travado)

Um Pod permanece no status Pending, geralmente indicando que não pode ser agendado em um Nó ou está aguardando recursos (como um PersistentVolume).

Diagnóstico:

  1. Execute kubectl describe e observe a seção Events.

Causas e Correções Comuns:

  • Exaustão de Recursos: O cluster não possui Nós com CPU ou Memória disponível suficiente para satisfazer as requests do Pod.
    • Correção: Aumente o tamanho do cluster ou reduza as solicitações de recursos do Pod (se viável).
    • Exemplo de Mensagem de Evento: 0/4 nodes are available: 4 Insufficient cpu.
  • Problemas de Vinculação de Volume: O Pod requer um PersistentVolumeClaim (PVC) que não pode ser vinculado a um PersistentVolume (PV) subjacente.
    • Correção: Verifique o status do PVC (kubectl get pvc) e certifique-se de que a StorageClass está funcionando.
  • Incompatibilidade de Seletor ou Afinidade: O pod solicita um rótulo de nó que não existe, ou uma regra de afinidade necessária exclui todos os nós.
    • Correção: Compare nodeSelector, nodeAffinity e rótulos de nó com kubectl get nodes --show-labels.
  • Taints não tolerados: Os nós estão disponíveis, mas repelem este pod porque ele não possui uma toleration correspondente.
    • Correção: Adicione a toleration pretendida ao pod ou remova o taint se ele não representar mais uma regra de posicionamento real.

Cenário 4: OOMKilled (Morto por Falta de Memória)

Embora isso geralmente resulte em um status CrashLoopBackOff, a causa subjacente é específica: o contêiner usou mais memória do que o limite definido em sua especificação, fazendo com que o sistema operacional host (via Kubelet) o encerrasse à força.

Diagnóstico:

  1. Verifique kubectl describe -> Last State -> Reason: OOMKilled.

Correções:

  1. Aumente os Limites: Aumente o limit de memória na especificação do Pod, fornecendo mais margem.
  2. Otimize o Aplicativo: Faça o perfil do aplicativo para reduzir seu consumo de memória.
  3. Defina Requests: Certifique-se de que as requests estejam próximas do uso real em estado estável para melhorar a confiabilidade do agendamento.
resources:
  limits:
    memory: "512Mi" # Aumente este valor
  requests:
    memory: "256Mi"

Tenha cuidado com "correções" de memória que apenas aumentam o limite. Se o aplicativo tiver um vazamento, um limite mais alto pode apenas atrasar a próxima falha e fazer o nó carregar mais risco. Observe a memória ao longo do tempo em seu sistema de métricas. Um padrão de dente de serra que retorna à linha de base após a coleta de lixo é diferente de uma subida constante até o OOM.

Cenário 5: CreateContainerConfigError e CreateContainerError

Esses status são fáceis de ignorar porque não soam como falhas de aplicativo. Eles geralmente significam que o kubelet não conseguiu montar a configuração do contêiner.

Causas comuns incluem:

  • Um ConfigMap ou Secret referenciado não existe no namespace.
  • Uma chave dentro de um ConfigMap ou Secret está com erro de digitação.
  • Um caminho de montagem de volume entra em conflito com outra montagem.
  • O contêiner referencia um contexto de segurança inválido.

A verificação mais rápida ainda é describe:

kubectl describe pod [NOME_DO_POD] -n my-namespace

Procure por mensagens de evento como secret "app-config" not found ou configmap "settings" not found. Em seguida, verifique o objeto:

kubectl get secret app-config -n my-namespace
kubectl get configmap settings -n my-namespace -o yaml

Este é um erro comum em pipelines de implantação. O manifesto do aplicativo é aplicado, mas a etapa de criação do secret falhou ou foi executada no namespace errado.

Cenário 6: Running mas Não Pronto

Um pod pode mostrar Running enquanto ainda está inutilizável. A coluna READY informa quantos contêineres estão prontos de acordo com suas sondas de prontidão. Um pod com 1/2 ou 0/1 pode estar vivo, mas removido dos endpoints do Service.

Verifique os endpoints quando o tráfego estiver falhando, mas os pods parecerem vivos:

kubectl get endpoints [NOME_DO_SERVICE] -n my-namespace
kubectl describe pod [NOME_DO_POD] -n my-namespace

Se a lista de endpoints estiver vazia, o problema pode ser uma sonda de prontidão, uma incompatibilidade de seletor do Service ou o aplicativo ouvindo em uma porta diferente da esperada pelo Service. Em incidentes reais, é aqui que as pessoas perdem tempo: elas continuam reiniciando pods, mesmo que os pods não sejam o motivo do tráfego estar ausente.


Prevenindo Falhas Futuras: Melhores Práticas

Aplicativos robustos exigem configuração cuidadosa para evitar armadilhas comuns de implantação.

Use Sondas de Liveness e Readiness

A implementação adequada de sondas permite que o Kubernetes gerencie inteligentemente a saúde do contêiner:

  • Sondas de Liveness: Determinam se o contêiner está saudável o suficiente para continuar em execução. Se a sonda de liveness falhar, o Kubelet reiniciará o contêiner (resolvendo travamentos suaves).
  • Sondas de Readiness: Determinam se o contêiner está pronto para servir tráfego. Se a sonda de readiness falhar, o Pod é removido dos endpoints do serviço, evitando solicitações com falha enquanto o contêiner se recupera.

Não aponte sondas de liveness para verificações de dependência profundas, a menos que você realmente queira que o Kubernetes reinicie o contêiner sempre que essa dependência tiver uma interrupção curta. Um banco de dados ficar indisponível por trinta segundos geralmente não é prova de que o processo está morto. Readiness é o melhor lugar para dizer: "não envie tráfego para este pod agora".

Imponha Limites e Solicitações de Recursos

Sempre defina requisitos de recursos para contêineres. Isso impede que os Pods consumam recursos excessivos (levando à instabilidade do Nó) e garante que o scheduler possa colocar o Pod em um Nó com capacidade suficiente.

Utilize Init Containers para Configuração

Se um Pod exigir uma verificação de dependência ou configuração de dados antes do aplicativo principal iniciar (por exemplo, aguardar a conclusão de uma migração de banco de dados), use um Init Container. Se o Init Container falhar, o Pod o reiniciará repetidamente, isolando claramente os erros de configuração dos erros de runtime do aplicativo.

Um Fluxo de Triagem Prático

Quando você estiver de plantão, use um caminho repetível:

  1. Verifique kubectl get pods -n <namespace> -o wide para ver status, reinicializações, idade e posicionamento do nó.
  2. Execute kubectl describe pod e leia Events, State, Last State, montagens e configurações de recursos.
  3. Puxe logs com kubectl logs, adicionando --previous para contêineres reiniciados e -c para pods com vários contêineres.
  4. Se o pod estiver Pending, inspecione agendamento, taints, rótulos de nó e PVCs antes de ler os logs do aplicativo.
  5. Se o pod estiver Running mas não recebendo tráfego, inspecione sondas de readiness, seletores de Service e endpoints.
  6. Se o pod foi OOMKilled, compare os limites com gráficos de memória reais antes de simplesmente aumentar o número.

Esta ordem impede que você pule direto para o aplicativo quando o Kubernetes ainda nem o iniciou.

Verificação Final

O hábito mais útil é separar sintomas de causas. CrashLoopBackOff é um sintoma. A causa pode ser um secret ausente, uma migração ruim, uma sonda de liveness ou um limite de memória. Pending é um sintoma. A causa pode ser solicitações de CPU, um PVC, um taint ou um seletor de nó. Deixe o status do pod dizer onde olhar, depois deixe eventos e logs dizerem o que mudou.