Отладка приложения в состоянии CrashLoop

KubernetesBeginner
Практиковаться сейчас

Введение

В предыдущих лабораторных работах мы познакомились с логами, событиями, командами kubectl exec, пробросом портов (port-forwarding) и пробами (probes). Приложение, находящееся в цикле перезапуска (CrashLoop), требует комплексного применения этих навыков. Подина запускается, контейнер завершается, Kubernetes перезапускает его — вам необходимо собрать доказательства, прежде чем вносить какие-либо изменения.

В этой лабораторной работе вы развернете приложение, которое завершается из-за отсутствия обязательной переменной окружения. Вы пронаблюдаете за поведением при перезапуске, изучите текущие и предыдущие логи, проверите события, выявите недостающую конфигурацию в Deployment, а затем исправите его и убедитесь, что приложение стало доступным.

Подготовка пространства имен для отладки CrashLoop

На этом этапе вы подготовите чистое пространство имен (namespace) для упражнения по отладке CrashLoop.

Начните работу в директории /home/labex/project. В этой директории вы будете создавать и обновлять манифест приложения.

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

Убедитесь, что API-сервер Kubernetes доступен. Если виртуальная машина только что запустилась и узел еще не готов, подождите несколько секунд и выполните команду снова.

kubectl get nodes --request-timeout=5s

Ожидаемый вывод:

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

Дождитесь, пока под сетевого плагина кластера перейдет в состояние Ready. Исправленное приложение позже будет протестировано через Service.

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

Ожидаемый вывод:

pod/calico-node-... condition met

Создайте пространство имен ckad-prep-crashloop и установите его в качестве текущего:

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

Проверьте текущее пространство имен:

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

Ожидаемый вывод:

ckad-prep-crashloop

Развертывание и наблюдение за CrashLoop

На этом этапе вы развернете приложение, которое постоянно завершается.

Скрипт контейнера требует наличия переменной окружения APP_MESSAGE. Если переменная отсутствует, скрипт выводит понятную ошибку и завершается со статусом 1. Kubernetes перезапустит контейнер, что создаст поведение CrashLoop, которое вам нужно отладить.

Создайте файл 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

Примените манифест:

kubectl apply -f crash-demo.yaml

Ожидаемый вывод:

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

Дайте Kubernetes немного времени на перезапуск сбойного контейнера, а затем проверьте под:

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

Ожидаемый вывод включает под, который не является работоспособным. Точный статус может быть Error или CrashLoopBackOff в зависимости от времени выполнения:

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

Сохраните имя пода и проверьте состояние его контейнера. JSONPath позволяет прочитать конкретные поля, не просматривая весь объект целиком. Первое значение — это количество перезапусков. Второе значение — причина ожидания, которая может быть пустой, если вы застали под в момент, когда последний сбойный контейнер находится в состоянии Error, а не ожидает в 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"}'

Ожидаемый вывод включает количество перезапусков больше 0. В зависимости от времени, причина может быть пустой или содержать значение, например CrashLoopBackOff:

2 

или:

... CrashLoopBackOff

Чтение логов и событий для поиска причины

На этом этапе вы используете логи, предыдущие логи и события, чтобы определить, почему контейнер завершается.

Когда контейнер быстро перезапускается, kubectl logs может показать текущую попытку запуска, в то время как kubectl logs --previous считывает логи предыдущего завершенного контейнера. Для отладки CrashLoop флаг --previous часто является именно тем инструментом, который раскрывает полезную информацию об ошибке запуска.

Прочитайте текущие логи:

kubectl logs "$CRASH_POD" --container web

Ожидаемый вывод:

startup failed: APP_MESSAGE is required

Прочитайте логи предыдущего контейнера. Если Kubernetes сохранил лог последнего завершенного контейнера, вы увидите ту же ошибку запуска из более ранней попытки. В некоторых случаях быстрого цикла перезапуска среда выполнения может сообщить, что не может получить предыдущий лог. Это тоже важный урок отладки: используйте --previous, когда это возможно, но всегда сравнивайте результат с текущими логами и событиями пода.

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

Ожидаемый вывод часто содержит:

startup failed: APP_MESSAGE is required

Также допустимо увидеть сообщение, похожее на:

unable to retrieve container logs for ...

Проверьте события (events) для пода. События показывают, как Kubernetes перезапускает сбойный контейнер.

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

Ожидаемый вывод включает события, такие как BackOff или Started:

Warning  BackOff  ...  Back-off restarting failed container

Теперь проверьте конфигурацию окружения в Deployment. Список env отсутствует, что и объясняет сообщение в логах.

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

Ожидаемый вывод пуст:


Исправление Deployment и проверка восстановления

На этом этапе вы исправите отсутствие переменной окружения и убедитесь, что приложение восстановилось.

В сообщении об ошибке была указана отсутствующая конфигурация: APP_MESSAGE. Вместо изменения команды контейнера, установите требуемую переменную окружения в Deployment. Kubernetes создаст новый ReplicaSet, так как шаблон пода изменится.

Установите переменную окружения:

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

Ожидаемый вывод:

deployment.apps/crash-demo env updated

Дождитесь завершения развертывания (rollout):

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

Ожидаемый вывод:

deployment "crash-demo" successfully rolled out

Проверьте новый под. Он должен быть в состоянии Running и Ready:

kubectl get pods -l app=crash-demo

Ожидаемый вывод:

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

Создайте клиентский под и протестируйте 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

Ожидаемый вывод:

crashloop-fixed

Наконец, убедитесь, что Deployment теперь содержит переменную окружения:

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

Ожидаемый вывод:

APP_MESSAGE=crashloop-fixed

Резюме

Вы провели отладку приложения, находящегося в состоянии CrashLoop, используя те же источники данных, что были представлены ранее в этапе 5. Вы пронаблюдали статус сбойного пода, прочитали текущие и предыдущие логи контейнера, изучили события, выявили отсутствие переменной окружения в Deployment, исправили шаблон пода с помощью kubectl set env и проверили восстановленное приложение через его Service.