Настроить health checks для LLM
ТЕХНИЧЕСКОЕ ЗАДАНИЕ: Настроить health checks для LLM
1. Цель задачи
Научиться проектировать и реализовывать три типа health-эндпоинтов для LLM-сервиса (liveness, readiness, deep health) и настраивать соответствующие probes в Kubernetes. В результате микросервис корректно сообщает K8s о своём состоянии, что позволяет платформе автоматически перезапускать нездоровые поды и не направлять трафик на неподготовленные реплики.
Ключевой результат Эндпоинты /health, /ready, /deep работают в контейнере, K8s манифесты используют эти probes, и kubectl describe pod показывает Liveness: success, Readiness: success.
2. Исходные данные
| Что нужно | Откуда взять |
|---|---|
| LLM-сервис (минимальный) | Написать самостоятельно: Python + FastAPI + заглушка для LLM |
| Docker | Установленный Docker Desktop или Docker Engine |
| Kubernetes (локальный) | minikube, kind или Docker Desktop с K8s |
| Утилиты для тестов | curl, kubectl, k9s (опционально) |
Если нет реального K8s — симулируем:
- Установите kind (Kubernetes in Docker) одной командой: brew install kind (macOS) или choco install kind (Windows).
- Создайте кластер: kind create cluster --name health-check.
- После работы удалите: kind delete cluster --name health-check.
Если нет LLM — используем заглушку:
FastAPI приложение с эндпоинтом /generate, который отвечает {"response": "Hello, I'm an LLM stub."}. Имитация загрузки модели — задержка 2-3 секунды при старте.
3. Технологический стек
| Компонент | Инструменты | Назначение |
|---|---|---|
| Язык программирования | Python 3.11+ | Реализация LLM-сервиса |
| Веб-фреймворк | FastAPI + uvicorn | Эндпоинты health checks |
| Модель (заглушка) | random / постоянный ответ | Имитация работы LLM |
| Контейнеризация | Docker | Упаковка сервиса |
| Оркестрация | kind / minikube | Локальный Kubernetes |
| Нагрузочное тестирование | curl, hey, wrk | Проверка стабильности |
| Мониторинг (опционально) | kubectl, k9s | Наблюдение за probes |
4. Этапы выполнения
Этап 1: Создание LLM-сервиса с тремя эндпоинтами (45–60 минут)
Действия
-
Создайте структуру проекта
health-check-llm/ ├── app/ │ ├── __init__.py │ ├── main.py │ └── services/ │ ├── __init__.py │ └── llm_stub.py ├── Dockerfile ├── requirements.txt └── k8s/ └── deployment.yaml -
Реализуйте
services/llm_stub.py -
Реализуйте
main.pyfrom fastapi import FastAPI, HTTPException from services.llm_stub import LLMStub import time app = FastAPI() model = None start_time = None @app.on_event("startup") async def startup(): global model, start_time model = LLMStub() start_time = time.time() @app.get("/health") async def liveness(): # Просто отвечает 200, если процесс жив return {"status": "alive"} @app.get("/ready") async def readiness(): # Модель готова принимать запросы (прошло >3 сек с запуска) if model and time.time() - start_time > 3: return {"status": "ready"} raise HTTPException(status_code=503, detail="Model not ready yet") @app.get("/deep") async def deep_health(): if model and model.check_deep_health(): return {"status": "healthy", "model": "ok"} raise HTTPException(status_code=503, detail="Deep health check failed") -
Проверьте локально
uvicorn app.main:app --port 8080 curl http://localhost:8080/health curl http://localhost:8080/ready # первые 3 секунды — 503 curl http://localhost:8080/deep # после инициализации — 200
Ожидаемый результат этапа Локально запущенный FastAPI сервер отвечает на все три эндпоинта корректными кодами (200 или 503).
Этап 2: Контейнеризация и тестирование образа (30 минут)
Действия
-
Создайте Dockerfile
FROM python:3.11-slim WORKDIR /app COPY requirements.txt . RUN pip install --no-cache-dir -r requirements.txt COPY app/ . EXPOSE 8080 CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8080"] -
Создайте requirements.txt
fastapi==0.115.0 uvicorn==0.30.0 -
Соберите образ и проверьте
docker build -t health-check-llm:latest . docker run -d -p 8080:8080 health-check-llm:latest curl http://localhost:8080/health curl http://localhost:8080/ready # 503 в первые секунды docker stop <container>
Ожидаемый результат этапа Docker образ собран и локально отвечает корректно.
Этап 3: Развёртывание в Kubernetes с Probes (1–1.5 часа)
Действия
-
Создайте k8s/deployment.yaml
apiVersion: apps/v1 kind: Deployment metadata: name: llm-health spec: replicas: 1 selector: matchLabels: app: llm-health template: metadata: labels: app: llm-health annotations: prometheus.io/scrape: "true" prometheus.io/port: "8080" spec: containers: - name: llm image: health-check-llm:latest imagePullPolicy: IfNotPresent ports: - containerPort: 8080 livenessProbe: httpGet: path: /health port: 8080 initialDelaySeconds: 5 periodSeconds: 10 readinessProbe: httpGet: path: /ready port: 8080 initialDelaySeconds: 10 periodSeconds: 5 startupProbe: httpGet: path: /health port: 8080 initialDelaySeconds: 3 periodSeconds: 2 failureThreshold: 10 -
Загрузите образ в кластер (для kind нужно предварительно загрузить образ):
kind load docker-image health-check-llm:latest --name health-check -
Примените манифест
kubectl apply -f k8s/deployment.yaml -
Проверьте статус подов и probes
kubectl get pods # Ждём, пока STATUS станет Running kubectl describe pod <pod-name> | grep -A5 "Liveness\|Readiness\|Startup" -
Создайте Service для доступа
apiVersion: v1 kind: Service metadata: name: llm-health-svc spec: selector: app: llm-health ports: - port: 80 targetPort: 8080Примените и проверьте доступ через kubectl port-forward.
Ожидаемый результат этапа Под проходит liveness/readiness пробы, kubectl describe показывает Liveness: success и Readiness: success.
Этап 4: Проверка сценариев нездоровья (30–45 минут)
Действия
-
Сымитируйте сбой liveness — остановите процесс внутри контейнера:
kubectl exec -it <pod-name> -- /bin/sh # внутри контейнера kill 1 # выйдитеЧерез некоторое время (период liveness + порог) K8s перезапустит под.
Проверьте: kubectl get pods — увидите перезапуски (RESTARTS > 0). -
Сымитируйте readiness false — отключите модель (например, установите флаг):
- Добавьте временный эндпоинт
/failв приложение (на время эксперимента), который меняет глобальную переменнуюready = False. - Или просто остановите startup-задержку — в нашем случае
/readyвернёт 503, если старт не завершён. - Убедитесь, что Service не направляет трафик на неподготовленный под:
kubectl get endpoints— под должен бытьnot ready.
- Добавьте временный эндпоинт
-
Проверьте
/deepв реальном времени- Отправьте GET-запрос на
/deepчерез порт-форвард:kubectl port-forward svc/llm-health-svc 8888:80 curl http://localhost:8888/deep - Если глубокий чек фейлится (например, модель вернула пустую строку), probe можно настроить на
failureThreshold: 2.
- Отправьте GET-запрос на
Ожидаемый результат этапа Доказано, что K8s корректно перезапускает поды после сбоя liveness и исключает поды из сервиса при readiness false.
Этап 5: Настройка мониторинга проб (опционально, 20 минут)
Действия
- Установите Prometheus и Grafana (можно через
kube-prometheus-stack). - Добавьте аннотации к поду (уже есть в манифесте).
- Импортируйте дашборд с метриками
kube_pod_status_readyиprober_probe_total. - Создайте алерт на слишком частые перезапуски (RestartCount > 3 за 5 минут).
Ожидаемый результат этапа В Grafana отображаются статусы readiness и количество перезапусков.
5. Критерии приемки (Definition of Done)
- Реализованы эндпоинты
/health,/ready,/deepв FastAPI с корректными HTTP кодами (200 или 503). - Docker образ собран и запускается, все три эндпоинта отвечают.
- Манифест Deployment содержит livenessProbe, readinessProbe и startupProbe.
- Pod успешно проходит startup probe (<10 попыток) и readiness probe (<5 попыток).
- После принудительного убийства процесса внутри контейнера (kill 1) под перезапускается K8s (RESTARTS > 0).
-
/readyвозвращает 503 первые 3 секунды после старта, затем 200 (readiness delayed). - Service не направляет трафик на под, пока readiness probe не даст 200.
-
/deepпроверяет реальную функциональность модели (синтетический запрос). - Все файлы (исходники, Dockerfile, k8s манифесты) версионированы в Git.
6. Ожидаемый результат
Основные артефакты
- Репозиторий с кодом (FastAPI приложение).
- Docker образ
health-check-llm:latest. - Манифест
deployment.yamlс probes. - Подтверждение (скриншот или лог), что
kubectl describe podпоказываетLiveness: successиReadiness: success.
Опциональные результаты
- Дашборд Grafana с метриками проб.
- Pipeline CI/CD (например, GitHub Actions), который автоматически разворачивает в kind и прогоняет health-check тесты.
7. Возможные сложности и их решение
| Сложность | Решение |
|---|---|
| Kind не видит локальный образ | Использовать kind load docker-image <имя> перед apply |
| Startup probe затягивается, pod уходит в CrashLoopBackOff | Увеличить initialDelaySeconds и failureThreshold для startupProbe |
/ready постоянно 503 после старта | Проверить логи: возможно задержка >5 сек, увеличить initialDelaySeconds readinessProbe |
| Deep health check падает из-за тайм-аута | Сделать timeout в 5 секунд на генерацию; если модель не отвечает — 503 |
| В Windows/Mac Docker Desktop с K8s не включён | Отдельно установить minikube или kind |
8. Бюджет времени (оценка)
| Этап | Время |
|---|---|
| Этап 1: Создание сервиса | 50 мин |
| Этап 2: Контейнеризация | 30 мин |
| Этап 3: Развёртывание + Probes | 1 ч 15 мин |
| Этап 4: Проверка сценариев | 40 мин |
| Этап 5 (опционально): Мониторинг | 20 мин |
| Итого | ~3 ч 35 мин |
При первом выполнении рекомендуется заложить до 5 часов с учётом отладки.
9. Связанные вопросы из базы знаний
| Вопрос | Тема |
|---|---|
| 12 | Как настроить liveness probe в Kubernetes? |
| 45 | Какие порты открывать в Docker контейнере для FastAPI? |
| 87 | Чем отличается readiness от liveness probe? |
| 134 | Как эмулировать долгую загрузку модели при старте? |
| 211 | Как симулировать сбой контейнера для теста? |
| 278 | Что такое startup probe и когда её использовать? |
| 345 | Как сделать deep health check для LLM? |
| 512 | Как настроить Prometheus для мониторинга pod health? |
| 678 | Как в kind загрузить локальный Docker образ? |
| 799 | Как написать тест в pytest для health endpoints? |
10. Чек-лист самопроверки
- Я реализовал все три эндпоинта и протестировал их локально через curl.
- Docker образ собирается и при запуске корректно отвечает на
/health. - Манифест Deployment содержит все три probe с разумными задержками.
- Я развернул приложение в локальном кластере (kind/minikube) и проверил статус через
kubectl get pods. - Я принудительно убил процесс в контейнере и убедился, что pod перезапускается.