Depurar uma Aplicação em CrashLoop

KubernetesBeginner
Pratique Agora

Introdução

Os laboratórios anteriores introduziram logs, eventos, kubectl exec, port-forwarding e probes. Uma aplicação em CrashLoop combina várias dessas habilidades. O Pod inicia, o container encerra, o Kubernetes o reinicia, e você precisa ler as evidências antes de alterar qualquer coisa.

Neste laboratório, você implantará uma aplicação que encerra porque uma variável de ambiente necessária está faltando. Você observará o comportamento de reinicialização, lerá os logs atuais e anteriores, inspecionará eventos, identificará a configuração ausente no Deployment, corrigirá o Deployment e verificará se a aplicação se torna acessível.

Preparar um Namespace para Depuração de CrashLoop

Nesta etapa, você preparará um namespace limpo para o exercício de depuração de CrashLoop.

Comece em /home/labex/project. Você criará e atualizará o manifesto da aplicação neste diretório.

mkdir -p /home/labex/project
cd /home/labex/project

Verifique se o servidor da API do Kubernetes está acessível. Se a VM acabou de iniciar e o nó ainda não está pronto, aguarde alguns segundos e execute o comando novamente.

kubectl get nodes --request-timeout=5s

A saída esperada inclui:

NAME         STATUS   ROLES           AGE   VERSION
labex-v135   Ready    control-plane   ...   v1.35...

Aguarde até que o Pod de rede do cluster esteja pronto (Ready). A aplicação corrigida será testada posteriormente através de um Service.

kubectl wait --namespace kube-system --for=condition=Ready pod -l k8s-app=calico-node --timeout=120s

Saída esperada:

pod/calico-node-... condition met

Crie um namespace chamado ckad-prep-crashloop e defina-o como o namespace atual:

kubectl create namespace ckad-prep-crashloop
kubectl config set-context --current --namespace=ckad-prep-crashloop

Confirme o namespace atual:

kubectl config view --minify --output 'jsonpath={..namespace}'; echo

Saída esperada:

ckad-prep-crashloop

Implantar e Observar um CrashLoop

Nesta etapa, você implantará uma aplicação que encerra repetidamente.

O script do container requer a variável de ambiente APP_MESSAGE. Se a variável estiver ausente, o script imprime um erro claro e encerra com status 1. O Kubernetes reiniciará o container, o que cria o comportamento de CrashLoop que você precisa depurar.

Crie o arquivo crash-demo.yaml:

cat > crash-demo.yaml <<'EOF'
apiVersion: apps/v1
kind: Deployment
metadata:
  name: crash-demo
spec:
  replicas: 1
  selector:
    matchLabels:
      app: crash-demo
  template:
    metadata:
      labels:
        app: crash-demo
    spec:
      terminationGracePeriodSeconds: 0
      containers:
      - name: web
        image: busybox:1.36
        command:
        - /bin/sh
        - -c
        - |
          if [ -z "${APP_MESSAGE:-}" ]; then
            echo "startup failed: APP_MESSAGE is required"
            exit 1
          fi
          mkdir -p /www
          echo "$APP_MESSAGE" > /www/index.html
          httpd -f -p 8080 -h /www
        ports:
        - containerPort: 8080
---
apiVersion: v1
kind: Service
metadata:
  name: crash-demo
spec:
  selector:
    app: crash-demo
  ports:
  - port: 80
    targetPort: 8080
EOF

Aplique o manifesto:

kubectl apply -f crash-demo.yaml

Saída esperada:

deployment.apps/crash-demo created
service/crash-demo created

Dê ao Kubernetes um breve momento para reiniciar o container com falha e, em seguida, inspecione o Pod:

sleep 12
kubectl get pods -l app=crash-demo

A saída esperada inclui um Pod que não está saudável. O status exato pode ser Error ou CrashLoopBackOff dependendo do tempo:

NAME                          READY   STATUS             RESTARTS   AGE
crash-demo-...                0/1     CrashLoopBackOff   ...        ...

Armazene o nome do Pod e inspecione o estado do seu container. O JSONPath permite ler os campos específicos que importam sem percorrer todo o objeto. O primeiro valor é a contagem de reinicializações. O segundo valor é o motivo da espera, que pode estar vazio se você capturar o Pod enquanto o último container com falha estiver em estado Error em vez de aguardar em CrashLoopBackOff.

CRASH_POD="$(kubectl get pods -l app=crash-demo -o jsonpath='{.items[0].metadata.name}')"
echo "$CRASH_POD"
kubectl get pod "$CRASH_POD" -o jsonpath='{.status.containerStatuses[0].restartCount}{" "}{.status.containerStatuses[0].state.waiting.reason}{"\n"}'

A saída esperada inclui uma contagem de reinicializações maior que 0. Dependendo do tempo, o motivo pode estar em branco ou mostrar um valor como CrashLoopBackOff:

2 

ou:

... CrashLoopBackOff

Ler Logs e Eventos para Encontrar a Causa

Nesta etapa, você usará logs, logs anteriores e eventos para identificar por que o container encerra.

Quando um container está reiniciando rapidamente, o kubectl logs pode mostrar a tentativa atual do container, enquanto o kubectl logs --previous lê a tentativa anterior do container encerrado. Para depuração de CrashLoop, o --previous é frequentemente o comando que expõe o erro de inicialização útil.

Leia os logs atuais:

kubectl logs "$CRASH_POD" --container web

A saída esperada inclui:

startup failed: APP_MESSAGE is required

Leia os logs do container anterior. Se o Kubernetes tiver mantido o log do último container encerrado, isso mostrará o mesmo erro de inicialização de uma tentativa anterior. Em alguns casos de CrashLoop rápido, o runtime pode, em vez disso, informar que não consegue recuperar o log anterior. Isso ainda ensina um ponto importante de depuração: use --previous quando estiver disponível, mas sempre compare-o com os logs atuais e os eventos do Pod.

kubectl logs "$CRASH_POD" --container web --previous

A saída esperada frequentemente inclui:

startup failed: APP_MESSAGE is required

Também é aceitável ver uma mensagem semelhante a:

unable to retrieve container logs for ...

Inspecione os eventos do Pod. Os eventos mostram o Kubernetes reiniciando o container com falha.

kubectl describe pod "$CRASH_POD" | sed -n '/Events:/,$p'

A saída esperada inclui eventos como BackOff ou Started:

Warning  BackOff  ...  Back-off restarting failed container

Agora, inspecione a configuração de ambiente do Deployment. A lista env está ausente, o que explica a mensagem de log.

kubectl get deployment crash-demo -o jsonpath='{.spec.template.spec.containers[0].env}'; echo

A saída esperada está vazia:


Corrigir o Deployment e Verificar a Recuperação

Nesta etapa, você corrigirá a variável de ambiente ausente e verificará se a aplicação se recupera.

A mensagem de erro nomeou a configuração ausente: APP_MESSAGE. Em vez de alterar o comando do container, defina a variável de ambiente necessária no Deployment. O Kubernetes criará um novo ReplicaSet porque o template do Pod mudou.

Defina a variável de ambiente:

kubectl set env deployment/crash-demo APP_MESSAGE=crashloop-fixed

Saída esperada:

deployment.apps/crash-demo env updated

Aguarde o rollout:

kubectl rollout status deployment/crash-demo --timeout=120s

Saída esperada:

deployment "crash-demo" successfully rolled out

Inspecione o novo Pod. Ele deve estar em estado Running e Ready:

kubectl get pods -l app=crash-demo

A saída esperada inclui:

NAME                          READY   STATUS    RESTARTS   AGE
crash-demo-...                1/1     Running   0          ...

Crie um Pod cliente e teste o Service:

kubectl run crash-client --image=busybox:1.36 --restart=Never --command -- sleep 3600
kubectl wait --for=condition=Ready pod/crash-client --timeout=120s
kubectl exec crash-client -- wget -qO- http://crash-demo

Saída esperada:

crashloop-fixed

Finalmente, confirme que o Deployment agora inclui a variável de ambiente:

kubectl get deployment crash-demo -o jsonpath='{.spec.template.spec.containers[0].env[0].name}{"="}{.spec.template.spec.containers[0].env[0].value}{"\n"}'

Saída esperada:

APP_MESSAGE=crashloop-fixed

Resumo

Você depurou uma aplicação em CrashLoop usando as mesmas fontes de evidência introduzidas anteriormente no Estágio 5. Você observou o status do Pod com falha, leu os logs atuais e anteriores do container, inspecionou eventos, identificou uma variável de ambiente ausente no Deployment, corrigiu o template do Pod com kubectl set env e verificou a aplicação recuperada através do seu Service.