English translation is not available yet. Showing Russian content.
Как вы делаете health check для LLM сервера с учетом модели (не только процесс)?
Краткий тезис
Health check LLM сервера — это не просто проверка, что процесс жив. Модель может быть загружена, но не отвечать из-за нехватки памяти, сбоя инференса или зависания. Правильный подход — разделить liveness (жив ли процесс), readiness (готова ли модель отвечать) и startup (загружается ли модель). Дополнительно нужен deep health check, который отправляет реальный запрос к LLM и проверяет, что ответ синтаксически корректен. В Kubernetes (K8s) это конфигурируется через probes с учётом времени загрузки модели.
1. Термин: Health Check (проверка работоспособности)
Health check — это эндпоинты (HTTP-ручки), которые опрашивает оркестратор (Kubernetes) или мониторинговая система, чтобы понять, что сервис работает корректно. В контексте LLM сервера стандартные проверки процесса недостаточны, потому что модель может быть загружена, но давать ошибки (например, OOM, deadlock при инференсе).
| Тип пробы | Назначение | В LLM сервере |
|---|---|---|
| Liveness | Процесс жив? | HTTP 200 на /health |
| Readiness | Готов принимать трафик? | Модель загружена и не перегружена |
| Startup | Загрузка завершена? | После загрузки модели переключается в success |
Термин «Probe» (зонд) — в K8s так называют HTTP-запросы или команды, выполняемые внутри контейнера для проверки состояния.
2. Liveness Probe — /health
Liveness отвечает на вопрос: «Жив ли процесс?». Возвращает 200 OK, если HTTP-сервер отвечает, даже если модель ещё не загружена. Это самая базовая проверка.
# FastAPI пример
@app.get("/health")
async def health():
return {"status": "alive"}
В K8s:
livenessProbe:
httpGet:
path: /health
port: 8000
initialDelaySeconds: 5
periodSeconds: 10
Термин «initialDelaySeconds» — сколько секунд ждать после старта контейнера, прежде чем запустить первую проверку. Для LLM сервера этот параметр должен быть больше времени загрузки модели (обычно 30-120 секунд).
3. Readiness Probe — /ready
Readiness отвечает: «Готова ли модель отвечать?». Возвращает 200 только после успешной загрузки модели и инициализации инференса.
model_loaded = False
@app.on_event("startup")
async def load_model():
global model_loaded
# загружаем модель (может быть долго)
model = load_llm(...)
model_loaded = True
@app.get("/ready")
async def ready():
if model_loaded:
return {"status": "ready"}
else:
return JSONResponse(status_code=503, content={"status": "not ready"})
Важность: если readiness probe возвращает не 200, K8s перестаёт направлять трафик на этот под. Это предотвращает таймауты у пользователей, пока модель грузится.
4. Startup Probe — /startup
Startup — медленная проба для длительной инициализации. Используется, когда модель загружается несколько минут. K8s не будет проверять liveness/readiness, пока startup не завершится успехом.
startupProbe:
httpGet:
path: /startup
port: 8000
initialDelaySeconds: 0
periodSeconds: 10
failureThreshold: 30 # ждём до 300 секунд (30*10)
startup_complete = False
async def startup_task():
# загрузка модели
await load_large_model()
startup_complete = True
@app.get("/startup")
async def startup():
if startup_complete:
return {"status": "startup done"}
return JSONResponse(status_code=503, content={"status": "startup in progress"})
Термин «failureThreshold» — количество неудачных проверок, после которых проба считается проваленной. Для LLM моделей (особенно 70B+ параметров) failureThreshold нужно увеличить до 30-60.
5. Custom Deep Health — /deep
Deep health check выходит за рамки HTTP-статуса. Он отправляет маленький промпт к модели (например, "Hello") и проверяет, что ответ пришёл, не пустой и не содержит ошибок. Это единственный способ убедиться, что инференс не сломался (например, из-за повреждённого кэша CUDA или тупиковой ситуации).
import asyncio
@app.get("/deep")
async def deep_health():
try:
response = await asyncio.wait_for(
model.generate("Hello", max_new_tokens=5),
timeout=10.0
)
if response and len(response) > 0:
return {"status": "ok", "sample_response": response[:100]}
else:
return JSONResponse(status_code=503, content={"status": "empty response"})
except Exception as e:
return JSONResponse(status_code=503, content={"status": "error", "detail": str(e)})
Рекомендация: не включать deep health в обычные probes K8s, так как он потребляет ресурсы и может занять секунды. Лучше использовать его как отдельный внешний мониторинг (например, Prometheus + Grafana).
6. Реализация в Kubernetes (пример Deployment)
apiVersion: apps/v1
kind: Deployment
metadata:
name: llm-server
spec:
replicas: 2
template:
spec:
containers:
- name: llm
image: my-llm-server:latest
ports:
- containerPort: 8000
startupProbe:
httpGet:
path: /startup
port: 8000
initialDelaySeconds: 0
periodSeconds: 15
failureThreshold: 20 # 300 секунд на загрузку
livenessProbe:
httpGet:
path: /health
port: 8000
initialDelaySeconds: 65 # после старта ждём загрузки + запас
periodSeconds: 30
readinessProbe:
httpGet:
path: /ready
port: 8000
initialDelaySeconds: 65
periodSeconds: 15
Термин «initialDelaySeconds: 65» — даёт время на загрузку модели (если startup probe ещё не завершился, liveness и readiness не применяются, но после завершения startup они активируются). Лучше полагаться на startup probe и выставить liveness/readiness с малым initialDelaySeconds, чтобы они не мешали.
7. Лучшие практики и мониторинг
- Метрики: в Prometheus собирать
health_status,ready_status,deep_health_latency_seconds,deep_health_errors_total. Использовать Grafana для дашборда. - Алерты: если
ready_status == 0более 5 минут → PagerDuty; если deep health ошибка → дежурному. - Таймауты: для deep health ставить тайм-аут 10–30 секунд, иначе он может повесить сам probe.
- Ресурсы: deep health вызывать каждые 60 секунд, а не каждые 5.
- Оптимизация: для моделей, которые кешируют KV-кеш, deep health может быть разрушительным (сброс кеша). Используйте отдельный процесс или теневой инференс.
| Проблема | Признак | Решение |
|---|---|---|
| Процесс жив, но модель не отвечает | /health 200, /ready 503 | Увеличить startup probe, проверить память |
| Инференс медленный, но не завис | Deep health latency > 10s | Поставить алерт по latency readiness |
| Модель выдаёт мусор | Deep health возвращает пустой/NaN ответ | Добавить проверку на корректность ответа |
8. Пример полной реализации на FastAPI
from fastapi import FastAPI, Response
import asyncio
import time
app = FastAPI()
model_loaded = False
startup_complete = False
@app.on_event("startup")
async def load_model():
global model_loaded, startup_complete
# симуляция загрузки (реальная загрузка может быть долгой)
await asyncio.sleep(30)
model_loaded = True
startup_complete = True
@app.get("/health")
async def health():
return {"status": "alive"}
@app.get("/ready")
async def ready():
if model_loaded:
return {"status": "ready"}
return Response(status_code=503, content="not ready")
@app.get("/startup")
async def startup():
if startup_complete:
return {"status": "done"}
return Response(status_code=503, content="starting")
@app.get("/deep")
async def deep_health():
try:
start = time.time()
# заглушка: реальный вызов модели
response = await mock_generate("Hello", max_tokens=5)
latency = time.time() - start
if response:
return {"status": "ok", "latency": latency}
else:
return Response(status_code=503, content="empty response")
except Exception as e:
return Response(status_code=503, content=str(e))
mock_generate — замените на реальный вызов вашей LLM (например, через Hugging Face pipeline или vLLM).
Пет-проект для закрепления
Задача: создать FastAPI сервер с полным набором health checks для LLM модели (можно имитировать загрузку через asyncio.sleep). Затем развернуть в minikube или kind с правильными probes и проверить, как под реагирует на сбои.
Инструменты: Python + FastAPI, Docker, Kubernetes (minikube), kubectl.
Шаги:
- Написать код сервера с эндпоинтами
/health,/ready,/startup,/deep. - Добавить задержку при старте (sleep 30 секунд).
- Собрать Docker образ.
- Написать Deployment с probes (startup timeout 40 сек, liveness/readiness с initialDelaySeconds 0, но с учётом startup).
- Запустить в minikube:
kubectl apply -f deployment.yaml. - Наблюдать статус:
kubectl get pods -w— увидите, как под сначала в состоянииInit:0/1(startup), затемRunningс0/1readiness, наконец1/1. - Имитировать сбой: удалить /ready или зациклить deep health, проверить, что K8s перезапускает под.
Ожидаемый результат: понимание поведения probes, умение настроить тайминги под конкретную модель.
Связь с другими вопросами
| Вопрос | Тема |
|---|---|
| 416 | Как деплоить LLM в production (контейнеризация) |
| 418 | Как мониторить LLM сервер в production |
| 215 | Как работает Kubernetes probes |
| 312 | Как управлять памятью GPU в LLM сервере |
| 410 | Как измерять latency и throughput LLM инференса |
| 220 | Как делать CI/CD для ML моделей |
Навигация
- Предыдущий: 416
- Следующий: 418
- Индекс: 00. Индекс разборов