Урок 06 · Kubernetes на практике
Health-пробы
Что-то не запустилось или осталось непонятным? Задайте вопрос в комментариях к разбору этого урока в канале — разберёмся вместе.
В уроке 3 ReplicaSet чинил Pod, только когда тот исчезал целиком. Но что если процесс внутри контейнера жив, отвечает на TCP-уровне, а сам при этом завис намертво или ещё не успел прогреться после старта? Снаружи для Kubernetes это выглядит как совершенно здоровый Pod. /health-эндпоинт, который вы добавили ещё в уроке 1 «на будущее», — и есть ответ на эту проблему: он даёт кластеру способ спросить приложение напрямую, а не гадать по факту существования процесса.
Liveness и readiness — разные пробы, разные действия
Пробы бывают нескольких видов, но в этом уроке важны две, и путать их — самая частая ошибка новичков:
- Liveness-проба отвечает на вопрос «процесс вообще жив?». Если несколько проверок подряд проваливаются, kubelet перезапускает контейнер внутри того же Pod'а — это отдельный, более быстрый механизм самовосстановления, чем пересоздание всего Pod'а через ReplicaSet из урока 3.
- Readiness-проба отвечает на другой вопрос — «готов ли Pod принимать трафik прямо сейчас?». Если она проваливается, никто ничего не перезапускает — Kubernetes просто временно убирает Pod из списка адресов Service (урок 4), пока проба снова не станет успешной.
Итого: liveness лечит зависшие процессы перезапуском, readiness защищает пользователей от Pod'а, который жив, но не готов отвечать — например, ещё не подключился к базе при старте.
/health запускает две независимые реакции с разной скоростью и разными последствиями.Практика: сломайте сервис руками и понаблюдайте за реакцией
Понадобится Deployment из урока 3 (2 реплики) и Service из урока 4.
-
Обновите
main.pyиз урока 1: добавьте флаг, который можно включить снаружи, и эндпоинт, который его переключает.from fastapi import FastAPI, HTTPException app = FastAPI() UNHEALTHY = False @app.get("/health") def health(): if UNHEALTHY: raise HTTPException(status_code=500, detail="сломано специально") return {"status": "ok"} @app.post("/break") def break_health(): global UNHEALTHY UNHEALTHY = True return {"status": "теперь /health будет отвечать ошибкой"}Первую строку и функцию
app = FastAPI()замените целиком — остальной код (/и старый/health) из урока 1 не трогайте, просто допишите тело/healthи новый эндпоинт/break. -
Пересоберите образ и загрузите его в minikube:
docker build -t hello-fastapi:0.3 . minikube image load hello-fastapi:0.3 -
В
deployment.yamlобновите тег образа на0.3и добавьте обе пробы к контейнеру:containers: - name: hello-fastapi image: hello-fastapi:0.3 ports: - containerPort: 8000 envFrom: - configMapRef: name: hello-fastapi-config - secretRef: name: hello-fastapi-secret livenessProbe: httpGet: path: /health port: 8000 initialDelaySeconds: 5 periodSeconds: 5 failureThreshold: 3 readinessProbe: httpGet: path: /health port: 8000 initialDelaySeconds: 5 periodSeconds: 5 failureThreshold: 1Обе пробы стучатся в один и тот же
/health, но с разнымfailureThreshold: readiness реагирует на первую же неудачу, liveness ждёт три подряд — поэтому их эффекты будет видно по отдельности, с разницей в несколько секунд. -
Примените манифест и дождитесь обновления:
kubectl apply -f deployment.yaml kubectl rollout status deployment/hello-fastapi -
Получите адрес сервиса, как в уроке 4, и сломайте здоровье одной из двух реплик:
URL=$(minikube service hello-fastapi --url) curl -X POST "$URL/break"Реплик две, а Service выбирает, кому отдать запрос, — так что
/breakс большой вероятностью попадёт только в одну из них. Вторая продолжит как ни в чём не бывало.
/break запустите:
kubectl get pods --watch
Через несколько секунд у одного из Pod'ов READY кратковременно станет 0/1 — это сработала readiness-проба, и Service перестал слать туда трафик. Ещё через несколько секунд у того же Pod'а вырастет счётчик RESTARTS, а READY вернётся к 1/1 — это уже liveness: kubelet перезапустил контейнер, и вместе с новым процессом флаг UNHEALTHY сам сбросился в исходное состояние. Остановите наблюдение сочетанием Ctrl+C.
Проверьте себя
Readiness-проба несколько раз подряд возвращает ошибку. Что произойдёт?
Напишите команду, которая применит обновлённый deployment.yaml с пробами:
Напишите команду, которая будет непрерывно показывать изменения статуса Pod'ов в реальном времени: