Fehlersuche bei einer CrashLooping-Anwendung

KubernetesBeginner
Jetzt üben

Einführung

In den vorangegangenen Übungen wurden Protokolle (Logs), Ereignisse (Events), kubectl exec, Port-Forwarding und Probes eingeführt. Eine CrashLooping-Anwendung kombiniert mehrere dieser Fähigkeiten. Der Pod startet, der Container wird beendet, Kubernetes startet ihn neu – und Sie müssen die Beweise sichern, bevor Sie Änderungen vornehmen.

In dieser Übung stellen Sie eine Anwendung bereit, die beendet wird, weil eine erforderliche Umgebungsvariable fehlt. Sie beobachten das Neustartverhalten, lesen aktuelle und vorherige Protokolle, untersuchen Ereignisse, identifizieren die fehlende Konfiguration im Deployment, beheben das Deployment und überprüfen, ob die Anwendung wieder erreichbar ist.

Vorbereiten eines Namespaces für die CrashLoop-Fehlersuche

In diesem Schritt bereiten Sie einen sauberen Namespace für die CrashLoop-Fehlersuche vor.

Beginnen Sie im Verzeichnis /home/labex/project. Sie werden das Anwendungsmanifest in diesem Verzeichnis erstellen und aktualisieren.

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

Überprüfen Sie, ob der Kubernetes API-Server erreichbar ist. Falls die VM gerade erst gestartet wurde und der Node noch nicht bereit ist, warten Sie einige Sekunden und führen Sie den Befehl erneut aus.

kubectl get nodes --request-timeout=5s

Die erwartete Ausgabe sieht in etwa so aus:

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

Warten Sie, bis der Cluster-Networking-Pod bereit (Ready) ist. Die reparierte Anwendung wird später über einen Service getestet.

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

Erwartete Ausgabe:

pod/calico-node-... condition met

Erstellen Sie einen Namespace namens ckad-prep-crashloop und legen Sie ihn als aktuellen Namespace fest:

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

Bestätigen Sie den aktuellen Namespace:

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

Erwartete Ausgabe:

ckad-prep-crashloop

Bereitstellen und Beobachten eines CrashLoops

In diesem Schritt stellen Sie eine Anwendung bereit, die wiederholt beendet wird.

Das Skript des Containers benötigt die Umgebungsvariable APP_MESSAGE. Wenn die Variable fehlt, gibt das Skript eine klare Fehlermeldung aus und beendet sich mit dem Status 1. Kubernetes startet den Container daraufhin neu, was das CrashLoop-Verhalten erzeugt, das Sie untersuchen müssen.

Erstellen Sie die Datei 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

Wenden Sie das Manifest an:

kubectl apply -f crash-demo.yaml

Erwartete Ausgabe:

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

Geben Sie Kubernetes einen kurzen Moment Zeit, um den fehlerhaften Container neu zu starten, und untersuchen Sie dann den Pod:

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

Die erwartete Ausgabe zeigt einen Pod, der nicht fehlerfrei ist. Der genaue Status kann je nach Zeitpunkt Error oder CrashLoopBackOff lauten:

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

Speichern Sie den Pod-Namen und untersuchen Sie den Container-Status. Mit JSONPath können Sie die relevanten Felder direkt auslesen, ohne das gesamte Objekt durchsuchen zu müssen. Der erste Wert ist die Anzahl der Neustarts. Der zweite Wert ist der Grund für das Warten (Waiting Reason), der leer sein kann, wenn Sie den Pod genau in dem Moment erwischen, in dem sich der letzte fehlerhafte Container im Status Error befindet, anstatt im CrashLoopBackOff zu warten.

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"}'

Die erwartete Ausgabe enthält eine Neustart-Anzahl größer als 0. Je nach Zeitpunkt kann der Grund leer sein oder einen Wert wie CrashLoopBackOff anzeigen:

2 

oder:

... CrashLoopBackOff

Protokolle und Ereignisse lesen, um die Ursache zu finden

In diesem Schritt verwenden Sie Protokolle, vorherige Protokolle und Ereignisse, um zu identifizieren, warum der Container beendet wird.

Wenn ein Container schnell neu startet, zeigt kubectl logs möglicherweise den aktuellen Container-Versuch an, während kubectl logs --previous den vorherigen, beendeten Container-Versuch ausliest. Bei der CrashLoop-Fehlersuche ist --previous oft der Befehl, der den nützlichen Startfehler aufdeckt.

Lesen Sie die aktuellen Protokolle:

kubectl logs "$CRASH_POD" --container web

Die erwartete Ausgabe enthält:

startup failed: APP_MESSAGE is required

Lesen Sie die Protokolle des vorherigen Containers. Wenn Kubernetes das Protokoll des zuletzt beendeten Containers noch gespeichert hat, zeigt dies denselben Startfehler eines früheren Versuchs. In manchen schnellen CrashLoop-Fällen meldet die Laufzeitumgebung möglicherweise, dass das vorherige Protokoll nicht abgerufen werden kann. Das lehrt dennoch einen wichtigen Punkt bei der Fehlersuche: Verwenden Sie --previous, wenn es verfügbar ist, aber vergleichen Sie es immer mit den aktuellen Protokollen und Pod-Ereignissen.

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

Die erwartete Ausgabe enthält oft:

startup failed: APP_MESSAGE is required

Es ist auch akzeptabel, eine Meldung ähnlich der folgenden zu sehen:

unable to retrieve container logs for ...

Untersuchen Sie die Ereignisse für den Pod. Ereignisse zeigen, wie Kubernetes den fehlerhaften Container neu startet.

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

Die erwartete Ausgabe enthält Ereignisse wie BackOff oder Started:

Warning  BackOff  ...  Back-off restarting failed container

Untersuchen Sie nun die Umgebungskonfiguration des Deployments. Die env-Liste fehlt, was die Protokollmeldung erklärt.

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

Die erwartete Ausgabe ist leer:


Deployment reparieren und Wiederherstellung überprüfen

In diesem Schritt beheben Sie das Problem mit der fehlenden Umgebungsvariable und überprüfen, ob sich die Anwendung erholt.

Die Fehlermeldung nannte die fehlende Konfiguration: APP_MESSAGE. Anstatt den Container-Befehl zu ändern, setzen Sie die erforderliche Umgebungsvariable im Deployment. Kubernetes erstellt ein neues ReplicaSet, da sich die Pod-Vorlage ändert.

Setzen Sie die Umgebungsvariable:

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

Erwartete Ausgabe:

deployment.apps/crash-demo env updated

Warten Sie auf den Rollout:

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

Erwartete Ausgabe:

deployment "crash-demo" successfully rolled out

Untersuchen Sie den neuen Pod. Er sollte den Status Running und Ready haben:

kubectl get pods -l app=crash-demo

Die erwartete Ausgabe enthält:

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

Erstellen Sie einen Client-Pod und testen Sie den 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

Erwartete Ausgabe:

crashloop-fixed

Bestätigen Sie abschließend, dass das Deployment nun die Umgebungsvariable enthält:

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

Erwartete Ausgabe:

APP_MESSAGE=crashloop-fixed

Zusammenfassung

Sie haben eine CrashLooping-Anwendung mithilfe derselben Beweisquellen debuggt, die bereits in Stufe 5 eingeführt wurden. Sie haben den fehlerhaften Pod-Status beobachtet, aktuelle und vorherige Container-Protokolle gelesen, Ereignisse untersucht, eine fehlende Umgebungsvariable im Deployment identifiziert, die Pod-Vorlage mit kubectl set env korrigiert und die wiederhergestellte Anwendung über ihren Service überprüft.