Depurar una aplicación en CrashLoop

KubernetesBeginner
Practicar Ahora

Introducción

Los laboratorios anteriores introdujeron el uso de registros (logs), eventos, kubectl exec, reenvío de puertos (port-forwarding) y sondas (probes). Una aplicación en estado CrashLoop combina varias de esas habilidades. El Pod se inicia, el contenedor se cierra, Kubernetes lo reinicia y usted debe analizar la evidencia antes de realizar cualquier cambio.

En este laboratorio, desplegará una aplicación que se cierra debido a que falta una variable de entorno necesaria. Observará el comportamiento de reinicio, leerá los registros actuales y anteriores, inspeccionará los eventos, identificará la configuración faltante en el Deployment, corregirá el Deployment y verificará que la aplicación sea accesible.

Preparar un espacio de nombres para la depuración de CrashLoop

En este paso, preparará un espacio de nombres (namespace) limpio para el ejercicio de depuración de CrashLoop.

Comience en /home/labex/project. Creará y actualizará el manifiesto de la aplicación en este directorio.

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

Compruebe que la API de Kubernetes sea accesible. Si la máquina virtual acaba de iniciarse y el nodo aún no está listo, espere unos segundos y vuelva a ejecutar el comando.

kubectl get nodes --request-timeout=5s

La salida esperada incluye:

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

Espere hasta que el Pod de red del clúster esté listo (Ready). La aplicación corregida se probará más adelante a través de un Service.

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

Salida esperada:

pod/calico-node-... condition met

Cree un espacio de nombres llamado ckad-prep-crashloop y establézcalo como el espacio de nombres actual:

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

Confirme el espacio de nombres actual:

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

Salida esperada:

ckad-prep-crashloop

Desplegar y observar un CrashLoop

En este paso, desplegará una aplicación que se cierra repetidamente.

El script del contenedor requiere la variable de entorno APP_MESSAGE. Si la variable falta, el script imprime un error claro y se cierra con el estado 1. Kubernetes reiniciará el contenedor, lo que crea el comportamiento de CrashLoop que necesita depurar.

Cree 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 el manifiesto:

kubectl apply -f crash-demo.yaml

Salida esperada:

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

Dé a Kubernetes un breve momento para reiniciar el contenedor fallido y luego inspeccione el Pod:

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

La salida esperada incluye un Pod que no está en buen estado. El estado exacto puede ser Error o CrashLoopBackOff dependiendo del tiempo:

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

Guarde el nombre del Pod e inspeccione el estado de su contenedor. JSONPath le permite leer los campos específicos que importan sin tener que desplazarse por todo el objeto. El primer valor es el contador de reinicios. El segundo valor es el motivo de espera, que puede estar vacío si captura el Pod mientras el último contenedor fallido está en estado Error en lugar de esperar en 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"}'

La salida esperada incluye un contador de reinicios mayor a 0. Dependiendo del momento, el motivo puede estar en blanco o mostrar un valor como CrashLoopBackOff:

2 

o:

... CrashLoopBackOff

Leer registros y eventos para encontrar la causa

En este paso, utilizará los registros (logs), los registros anteriores y los eventos para identificar por qué se cierra el contenedor.

Cuando un contenedor se reinicia rápidamente, kubectl logs puede mostrar el intento actual del contenedor, mientras que kubectl logs --previous lee el intento anterior del contenedor terminado. Para la depuración de CrashLoop, --previous suele ser el comando que expone el error de inicio útil.

Lea los registros actuales:

kubectl logs "$CRASH_POD" --container web

La salida esperada incluye:

startup failed: APP_MESSAGE is required

Lea los registros del contenedor anterior. Si Kubernetes ha conservado el registro del último contenedor terminado, esto mostrará el mismo error de inicio de un intento anterior. En algunos casos de CrashLoop rápido, el tiempo de ejecución puede informar que no puede recuperar el registro anterior. Eso sigue enseñando un punto importante de depuración: use --previous cuando esté disponible, pero compárelo siempre con los registros actuales y los eventos del Pod.

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

La salida esperada a menudo incluye:

startup failed: APP_MESSAGE is required

También es aceptable ver un mensaje similar a:

unable to retrieve container logs for ...

Inspeccione los eventos del Pod. Los eventos muestran a Kubernetes reiniciando el contenedor fallido.

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

La salida esperada incluye eventos como BackOff o Started:

Warning  BackOff  ...  Back-off restarting failed container

Ahora inspeccione la configuración del entorno del Deployment. La lista env falta, lo que explica el mensaje en el registro.

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

La salida esperada está vacía:


Corregir el Deployment y verificar la recuperación

En este paso, corregirá la variable de entorno faltante y verificará que la aplicación se recupere.

El mensaje de error indicó la configuración faltante: APP_MESSAGE. En lugar de cambiar el comando del contenedor, establezca la variable de entorno necesaria en el Deployment. Kubernetes creará un nuevo ReplicaSet porque la plantilla del Pod cambia.

Establezca la variable de entorno:

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

Salida esperada:

deployment.apps/crash-demo env updated

Espere al despliegue (rollout):

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

Salida esperada:

deployment "crash-demo" successfully rolled out

Inspeccione el nuevo Pod. Debería estar en estado Running y Ready:

kubectl get pods -l app=crash-demo

La salida esperada incluye:

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

Cree un Pod cliente y pruebe el 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

Salida esperada:

crashloop-fixed

Finalmente, confirme que el Deployment ahora incluye la variable de entorno:

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

Salida esperada:

APP_MESSAGE=crashloop-fixed

Resumen

Ha depurado una aplicación en estado CrashLoop utilizando las mismas fuentes de evidencia introducidas anteriormente en la Etapa 5. Observó el estado del Pod fallido, leyó los registros del contenedor actual y anterior, inspeccionó los eventos, identificó una variable de entorno faltante en el Deployment, corrigió la plantilla del Pod con kubectl set env y verificó la recuperación de la aplicación a través de su Service.