Введение
В предыдущих лабораторных работах мы познакомились с логами, событиями, командами 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.


