Solucionar problemas de Pods e contêineres do Kubernetes

KubernetesBeginner
Pratique Agora

Introdução

Um status de Pod como Pending ou CrashLoopBackOff é um sintoma, não a causa raiz. Uma investigação eficaz segue uma ordem consistente de evidências: configuração do objeto, condições, eventos, estado de cada contêiner, logs e inspeção em execução.

Você diagnosticará duas falhas preparadas, corrigirá ambas e, em seguida, inspecionará um Pod saudável com múltiplos contêineres. O objetivo é desenvolver um método de investigação reutilizável, não memorizar uma correção específica.

Use a mesma sequência para cada incidente: comece com uma visão ampla, isole um objeto, leia seu estado atual e seus eventos, examine as evidências específicas do contêiner e, então, corrija o controlador responsável. Isso evita alterações aleatórias e ajuda a explicar por que uma correção funcionou.

Começar pelo estado do Pod

Nesta etapa, você identificará quais cargas de trabalho estão saudáveis e quais precisam de investigação. Comece com uma visão de todo o namespace. -o wide mantém a tabela conhecida, mas acrescenta colunas úteis, como o IP do Pod e o nó atribuído.

cd /home/labex/project/pod-troubleshooting
kubectl get deployments,pods -n practice-debug -o wide

O Pod saudável deve mostrar 2/2 Running. Um dos Pods de Deployment deve apresentar um problema ao baixar a imagem, enquanto o worker deverá reiniciar repetidamente.

Um resultado típico do ambiente de testes se parece com este (os sufixos dos Pods, as idades e as contagens de reinicialização serão diferentes):

NAME                                 READY   STATUS             RESTARTS   AGE
pod/broken-image-58758b4d69-c7j7g    0/1     ImagePullBackOff   0          33s
pod/crashy-worker-5557cfb974-xwpst   0/1     Error              2          33s
pod/web-with-sidecar                 2/2     Running            0          33s

Não se preocupe se o worker aparecer momentaneamente como Error em vez de CrashLoopBackOff. Ele é executado por pouco tempo, encerra-se e aguarda antes que o Kubernetes o inicie novamente; por isso, o estado exibido muda enquanto você o observa. O valor crescente de RESTARTS é a pista mais forte de que esse contêiner está falhando repetidamente.

Exiba os motivos de espera no nível dos contêineres sem ler todo o YAML do Pod:

kubectl get pods -n practice-debug -o custom-columns='POD:.metadata.name,PHASE:.status.phase,READY:.status.containerStatuses[*].ready,WAITING_REASON:.status.containerStatuses[*].state.waiting.reason,RESTARTS:.status.containerStatuses[*].restartCount'

[*] significa “cada item da lista”, portanto a mesma tabela também funciona para Pods com múltiplos contêineres. Use esta primeira visão para decidir qual comando de diagnóstico executar em seguida.

Por exemplo, WAITING_REASON=ImagePullBackOff aponta para a configuração da imagem ou para o acesso ao registro. O worker pode não mostrar nenhum motivo de espera enquanto sua tentativa mais recente estiver Terminated; por isso, uma única tabela é apenas o ponto de partida, não um diagnóstico completo.

Neste momento, não corrija nada. A falha de imagem e o worker que reinicia são sintomas diferentes e exigem evidências diferentes. Um bom primeiro comando reduz o espaço de investigação sem presumir a causa.

Diagnosticar uma falha ao baixar a imagem

Nesta etapa, você rastreará a falha da imagem desde o status até os eventos. Armazene o nome gerado do Pod para que os comandos posteriores apontem para o mesmo objeto:

IMAGE_POD=$(kubectl get pods -n practice-debug -l app=broken-image -o jsonpath='{.items[0].metadata.name}')
echo "$IMAGE_POD"
kubectl describe pod "$IMAGE_POD" -n practice-debug

Leia de baixo para cima. A seção Events deve mostrar que o Kubernetes não consegue baixar nginx:not-a-real-tag. Confirme diretamente qual imagem está configurada:

kubectl get pod "$IMAGE_POD" -n practice-debug -o jsonpath='{.spec.containers[0].image}{"\n"}'
kubectl events -n practice-debug --for "pod/${IMAGE_POD}"

Os eventos explicam a ação que falhou, enquanto a consulta JSONPath comprova qual imagem desejada causou o problema. Usar ambas as evidências evita confundir um problema temporário no registro com uma tag permanentemente inválida.

No ambiente testado, as linhas decisivas eram semelhantes a estas:

Image:          nginx:not-a-real-tag
State:          Waiting
  Reason:       ErrImagePull
Failed to pull image "nginx:not-a-real-tag": ... manifest unknown
Error: ImagePullBackOff

ErrImagePull registra uma tentativa de download malsucedida. ImagePullBackOff significa que o Kubernetes está aguardando mais tempo antes de repetir a mesma operação. A mensagem manifest unknown e a tag inválida relacionam o sintoma à sua causa.

Corrija o Deployment em vez de editar o Pod gerado. kubectl set image altera o modelo do Pod, fazendo com que o controlador do Deployment substitua o Pod com falha:

kubectl set image deployment/broken-image web=nginx:1.27-alpine -n practice-debug
kubectl rollout status deployment/broken-image -n practice-debug --timeout=60s

Ao final, o comando deverá exibir:

deployment.apps/broken-image image updated
deployment "broken-image" successfully rolled out

O Pod antigo é descartável; o modelo do Deployment representa o estado desejado durável. Após a atualização, deverá surgir um novo nome de Pod, e o Deployment deverá informar uma réplica disponível.

Ler logs atuais e anteriores

Nesta etapa, você diagnosticará um contêiner que inicia e depois é encerrado. Encontre o Pod e examine as informações de reinicialização:

WORKER_POD=$(kubectl get pods -n practice-debug -l app=crashy-worker -o jsonpath='{.items[0].metadata.name}')
kubectl get pod "$WORKER_POD" -n practice-debug
kubectl describe pod "$WORKER_POD" -n practice-debug

Para um contêiner que reinicia, kubectl logs normalmente lê a tentativa atual. --previous solicita ao ambiente de execução do contêiner a tentativa encerrada imediatamente anterior:

kubectl logs "$WORKER_POD" -n practice-debug -c worker
kubectl logs "$WORKER_POD" -n practice-debug -c worker --previous || \
  echo "The previous container log is no longer available; continue with the current log and describe output."

A saída atual ou anterior deverá revelar a própria mensagem da aplicação:

configuration missing

--previous é especialmente útil quando uma nova tentativa começa antes que você consiga inspecionar a que falhou. No entanto, trata-se de uma evidência histórica, não de um armazenamento garantido: o ambiente de execução local pode já ter removido o registro desse contêiner. Se aparecer unable to retrieve container logs, a mensagem alternativa é esperada. Combine o log atual disponível com describe, cujos campos Last State, Exit Code e Restart Count ainda comprovam que o processo foi encerrado repetidamente.

A opção -c worker identifica explicitamente o contêiner. Corrija o controlador substituindo o comando que falha por um loop de longa duração:

kubectl patch deployment crashy-worker -n practice-debug --type='json' -p='[{"op":"replace","path":"/spec/template/spec/containers/0/command","value":["sh","-c","echo worker-ready; while true; do sleep 30; done"]}]'
kubectl rollout status deployment/crashy-worker -n practice-debug --timeout=60s

O patch extenso pode parecer intimidador, então leia-o de dentro para fora: path seleciona o command do primeiro contêiner, op: replace indica o tipo de alteração e value fornece o novo conjunto de comandos. Você está alterando apenas um campo; o restante do Deployment permanece intacto.

O patch JSON substitui precisamente um campo no modelo de Pod do Deployment. Em produção, faça também a alteração equivalente no manifesto de origem.

Aguarde o status da atualização, pois o comando altera imediatamente o estado desejado, mas a substituição e a prontidão acontecem de forma assíncrona. Uma resposta bem-sucedida da API ainda não comprova que a carga de trabalho está saudável.

Direcionar comandos aos contêineres de um Pod com múltiplos contêineres

Nesta etapa, você inspecionará um Pod cujos contêineres têm responsabilidades diferentes. Liste primeiro os nomes:

kubectl get pod web-with-sidecar -n practice-debug -o jsonpath='{range .spec.containers[*]}{.name}{"\n"}{end}'

O range do JSONPath percorre a lista de contêineres. Leia os logs de cada contêiner separadamente:

kubectl logs web-with-sidecar -n practice-debug -c reporter --tail=5
kubectl logs web-with-sidecar -n practice-debug -c web --tail=5

--tail=5 limita a saída às últimas cinco linhas. Normalmente, o reporter exibe mensagens repetidas reporter-alive. O NGINX pode mostrar avisos de inicialização ou ainda não exibir logs de requisições; ambos os resultados são normais, pois ninguém enviou uma requisição HTTP.

Execute um comando dentro do contêiner web:

kubectl exec web-with-sidecar -n practice-debug -c web -- nginx -v

A imagem testada retornou:

nginx version: nginx/1.27.5

A versão exata do patch pode mudar se a imagem for atualizada; a evidência importante é que o comando foi executado com sucesso dentro do contêiner web.

O primeiro -- encerra as opções do kubectl. Tudo o que vem depois dele é o comando executado no contêiner selecionado.

Essa distinção é importante porque -c web é uma opção interpretada pelo kubectl, enquanto nginx -v é executado dentro do contêiner. Se você omitir -c em um Pod com múltiplos contêineres, o kubectl poderá escolher um padrão e mostrar evidências do processo errado.

Os contêineres de um mesmo Pod compartilham o namespace de rede. Assim, o reporter baseado em BusyBox também pode fazer uma requisição ao NGINX pelo endereço de loopback. Em wget -qO-, -q oculta as mensagens de progresso e -O- grava o corpo da resposta na saída padrão. O pipe envia esse corpo para head, que mantém apenas as primeiras linhas:

kubectl exec web-with-sidecar -n practice-debug -c reporter -- wget -qO- http://127.0.0.1 | head

A resposta deverá começar com <!DOCTYPE html>. Este é um padrão de diagnóstico reutilizável para desafios posteriores: executar um cliente HTTP pequeno dentro de um Pod preparado e observar o comportamento da aplicação sem expor uma nova porta externamente.

Confirmar a recuperação por vários ângulos

Nesta etapa, você verificará se a correção dos controladores produziu Pods substitutos saudáveis. Consulte a disponibilidade dos controladores e a prontidão dos Pods em conjunto:

kubectl get deployments,pods -n practice-debug
kubectl get pods -n practice-debug -o custom-columns='POD:.metadata.name,READY:.status.containerStatuses[*].ready,RESTARTS:.status.containerStatuses[*].restartCount,OWNER:.metadata.ownerReferences[0].name'

Por fim, verifique se os eventos recentes de aviso ainda descrevem falhas ativas:

--field-selector type=Warning solicita ao servidor da API apenas eventos do tipo Warning. Em seguida, --sort-by=.metadata.creationTimestamp ordena os resultados do mais antigo para o mais recente, permitindo distinguir falhas históricas do estado atual.

kubectl get events -n practice-debug --field-selector type=Warning --sort-by=.metadata.creationTimestamp

Avisos antigos podem permanecer após a recuperação. Considere as condições atuais dos Pods e a disponibilidade dos controladores como fonte da saúde atual, enquanto os eventos explicam como o sistema chegou a esse estado.

Após as duas correções, a saída do estado atual deverá ter este formato:

NAME                            READY   UP-TO-DATE   AVAILABLE
deployment.apps/broken-image    1/1     1            1
deployment.apps/crashy-worker   1/1     1            1

NAME                                 READY   STATUS    RESTARTS
pod/broken-image-...                 1/1     Running   0
pod/crashy-worker-...                1/1     Running   0
pod/web-with-sidecar                 2/2     Running   0

Você ainda poderá ver avisos antigos como Failed, BackOff ou até mesmo um aviso inicial FailedCreatePodSandBox ocorrido durante a inicialização do cluster. Compare os nomes dos objetos e os registros de data e hora: os avisos referem-se aos Pods substituídos, enquanto os novos Pods estão Running e ambos os Deployments estão disponíveis. Eventos são um histórico, não um painel de saúde em tempo real.

Sua conclusão final deve combinar as evidências atuais: ambos os Deployments estão disponíveis, todos os contêineres esperados estão prontos e nenhum Pod ativo permanece em estado de falha ao baixar a imagem ou de reinicialização contínua. A investigação termina com a verificação, não com o comando de correção.

Resumo

Você diagnosticou falhas de imagem e de inicialização usando condições, eventos, estado dos contêineres, logs atuais e logs anteriores. Corrigiu os modelos dos controladores em vez dos Pods gerados e aprendeu a direcionar logs e exec para contêineres específicos. No próximo desafio, você aplicará esse fluxo de trabalho baseado em evidências sem um roteiro passo a passo.