GitHub EN PT

K8s em Exemplos: Debugging de Pods

Quando um Pod falha, siga este checklist: verifique o status, leia os eventos, inspecione os logs e depois entre no container. A maioria dos problemas se encaixa em três categorias: problemas com a imagem, falhas na aplicação ou falta de recursos.

terminal

Comece por aqui. O status já indica onde procurar. ImagePullBackOff = problema com a imagem. CrashLoopBackOff = a aplicação inicia mas quebra logo em seguida. Pending = problema de scheduling. Running mas não ready = algum probe está falhando.

$ kubectl get pods -o wide
NAME        READY   STATUS             RESTARTS   AGE   NODE
api-6f7d8   0/1     ImagePullBackOff   0          2m    worker-1
db-8k2x9    0/1     CrashLoopBackOff   5          10m   worker-2
web-3n4m5   0/1     Pending            0          5m    <none>
cache-9z8y  1/1     Running            0          1h    worker-1
terminal

A seção Events no final do output é sua melhor amiga. Ela mostra a sequência exata do que aconteceu. No caso de ImagePullBackOff, você verá o erro específico — tag errada, falha de autenticação no registry privado ou problemas de rede.

$ kubectl describe pod api-6f7d8 | tail -15
Events:
  Type     Reason     Age                From               Message
  ----     ------     ----               ----               -------
  Normal   Scheduled  3m                 default-scheduler  Assigned to worker-1
  Normal   Pulling    90s (x4 over 3m)   kubelet            Pulling image "api:v2.1"
  Warning  Failed     88s (x4 over 3m)   kubelet            Failed to pull image "api:v2.1": rpc error: code = NotFound
  Warning  Failed     88s (x4 over 3m)   kubelet            Error: ErrImagePull
  Normal   BackOff    75s (x6 over 3m)   kubelet            Back-off pulling image "api:v2.1"
  Warning  Failed     75s (x6 over 3m)   kubelet            Error: ImagePullBackOff
terminal

Para CrashLoopBackOff, a aplicação iniciou mas terminou em seguida. A flag —previous mostra os logs do container que acabou de morrer. Sem ela, você pode ver um log vazio porque o novo container ainda não teve tempo de escrever nada.

$ kubectl logs db-8k2x9 --previous
2024-01-15 10:30:00 Starting database...
2024-01-15 10:30:01 Loading config from /etc/db/config.yaml
2024-01-15 10:30:01 ERROR: Config file not found
2024-01-15 10:30:01 Exiting with code 1

$ kubectl logs db-8k2x9 -f
$ kubectl logs db-8k2x9 --since=5m
$ kubectl logs db-8k2x9 --tail=100
terminal

Pending significa que o scheduler não conseguiu alocar o Pod em nenhum node. Verifique os eventos para entender o motivo: CPU ou memória insuficiente, nenhum node atende às regras de affinity, ou todos os nodes têm taints que o Pod não tolera. Note que a coluna NODE mostra <none>.

$ kubectl describe pod web-3n4m5 | tail -10
Events:
  Type     Reason            Age   From               Message
  ----     ------            ----  ----               -------
  Warning  FailedScheduling  5m    default-scheduler  0/3 nodes are available:
           1 Insufficient cpu, 2 node(s) had taint
           {node-role.kubernetes.io/master: }, that the pod didn't tolerate.

$ kubectl describe nodes | grep -A5 "Allocated resources"
terminal

O Pod está Running mas algo não funciona? Entre nele. Verifique se o processo está escutando na porta certa, teste a conectividade de rede e confirme se os arquivos montados existem. Para Pods com múltiplos containers, use -c nome-container.

$ kubectl exec -it cache-9z8y -- sh

/ $ ps aux
PID   USER     COMMAND
1     redis    redis-server *:6379

/ $ netstat -tlnp
Proto Local Address   State       PID/Program
tcp   0.0.0.0:6379   LISTEN      1/redis-server

/ $ nslookup api-service
Server:    10.96.0.10
Address:   10.96.0.10:53
Name:      api-service.default.svc.cluster.local
Address:   10.100.45.23

/ $ wget -qO- http://api-service:8080/health
terminal

Não consegue fazer exec porque o container fica reiniciando? Use containers de debug efêmeros. Isso cria um novo container anexado aos namespaces do Pod, permitindo inspecionar o ambiente mesmo quando o container principal não consegue iniciar.

$ kubectl debug api-6f7d8 -it --image=busybox --target=api
Targeting container "api". If you don't see processes, try --share-processes
/ $

/ $ ls -la /etc/config/
total 12
drwxr-xr-x    2 root     root          4096 Jan 15 10:30 .
-rw-r--r--    1 root     root           156 Jan 15 10:30 database.conf

/ $ env | grep DATABASE
DATABASE_URL=postgres://db:5432/myapp
terminal

Debug no nível do node. Útil para verificar logs do kubelet, espaço em disco ou problemas de rede que afetam todos os Pods de um node. O Pod de debug recebe acesso aos namespaces de PID e rede do host.

$ kubectl debug node/worker-1 -it --image=busybox
Creating debugging pod node-debugger-worker-1-abc123

/ $ chroot /host

$ journalctl -u kubelet --since "10 minutes ago" | tail -50

$ df -h
Filesystem      Size  Used Avail Use% Mounted on
/dev/sda1       100G   95G    5G  95% /

$ crictl ps -a | grep -i exited
terminal

Para debug de rede, você precisa de ferramentas que a maioria das imagens não tem. A imagem netshoot vem com curl, dig, nmap, tcpdump e muito mais. Perfeita para testar conectividade de Services, DNS e network policies.

$ kubectl run debug --rm -it --image=nicolaka/netshoot -- bash

bash-5.1$ dig +short api-service.default.svc.cluster.local
10.100.45.23

bash-5.1$ nc -zv api-service 8080
Connection to api-service 8080 port [tcp/*] succeeded!

bash-5.1$ curl -v http://api-service:8080/health

bash-5.1$ mtr --report api-service

bash-5.1$ tcpdump -i eth0 port 8080 -w /tmp/capture.pcap
terminal

Uma visão geral de tudo que está acontecendo no cluster. Os eventos desaparecem após 1 hora por padrão. Você pode filtrar por namespace, tipo ou objeto específico. Os Warnings costumam ser os mais interessantes.

$ kubectl get events --sort-by='.lastTimestamp' | tail -20

$ kubectl get events --field-selector type=Warning

$ kubectl get events --field-selector involvedObject.name=api-6f7d8

$ kubectl get events -w
0s    Warning   FailedMount   pod/api-6f7d8   MountVolume.SetUp failed
0s    Normal    Pulled        pod/db-8k2x9    Container image pulled
terminal

Problemas de recursos causam OOMKills e throttling de CPU. O comando top mostra o consumo real (requer o metrics-server instalado). Compare os valores com os limits configurados. Um número alto de restarts geralmente indica que o Pod está sendo OOMKilled.

$ kubectl top pods --sort-by=memory
NAME        CPU(cores)   MEMORY(bytes)
api-6f7d8   250m         512Mi
db-8k2x9    100m         1024Mi
web-3n4m5   50m          128Mi

$ kubectl get pod db-8k2x9 -o jsonpath='{.status.containerStatuses[0].lastState.terminated.reason}'
OOMKilled

$ kubectl describe pod api-6f7d8 | grep -A2 Limits
    Limits:
      cpu:     500m
      memory:  256Mi
terminal

Precisa conectar diretamente a um Pod? O port-forward cria um túnel a partir do seu localhost. Muito útil para acessar bancos de dados, testar endpoints ou conectar debuggers. O túnel fica aberto até você pressionar Ctrl+C.

$ kubectl port-forward pod/db-8k2x9 5432:5432 &
Forwarding from 127.0.0.1:5432 -> 5432

$ psql -h localhost -p 5432 -U admin mydb

$ kubectl port-forward svc/api-service 8080:80

$ kubectl port-forward pod/api-6f7d8 8080:80 9090:9090

Índice | Use as setas do teclado para navegar