English translation is not available yet. Showing Russian content.

Как вы делаете health check для LLM сервера с учетом модели (не только процесс)?

Краткий тезис

Health check для LLM сервера не должен ограничиваться проверкой, что процесс жив (liveness). Необходимо также проверять, что модель загружена и готова к инференсу (readiness), а для долгой загрузки — использовать startup probe. Дополнительно можно реализовать deep health check — отправку тестового запроса к модели, чтобы убедиться, что инференс работает корректно. Такой подход позволяет Kubernetes (или другому оркестратору) корректно управлять жизненным циклом сервера, избегая перезапусков во время загрузки и направляя трафик только на готовые поды.


1. Термин: Health check (проверка здоровья)

Health check — это механизм, позволяющий системе мониторинга или оркестратору (например, Kubernetes) определить, работает ли сервис корректно. Обычно реализуется в виде HTTP-эндпоинтов, возвращающих статус 200 при успехе и 5xx при ошибке.

Для LLM сервера health check должен учитывать не только состояние процесса, но и состояние модели: загружена ли она, не зависла ли, отвечает ли с приемлемой задержкой.


2. Проблема: Почему простого ping процесса недостаточно

LLM серверы имеют особенности:

  • Долгая загрузка модели (от секунд до минут) — процесс может быть запущен, но модель ещё не готова.
  • Высокая нагрузка на GPU/CPU — модель может зависнуть из-за ошибки в вычислениях или нехватки памяти.
  • Необходимость прогрева (warm-up) — первый запрос может быть медленным из-за инициализации кэшей.

Если проверять только процесс, оркестратор может:

  • Направить трафик на под, где модель ещё не загружена → ошибки 503 или таймауты.
  • Перезапустить под во время загрузки, считая его нездоровым (startup probe решает это).
  • Не заметить, что модель перестала отвечать (например, из-за deadlock в GPU).

Поэтому нужны разные типы проверок.


3. Kubernetes probes: liveness, readiness, startup

Kubernetes предоставляет три типа probes (зондов) для контейнеров:

ProbeНазначениеЧто проверяетДействие при failure
LivenessЖив ли контейнер?Процесс не зависПерезапуск контейнера
ReadinessГотов ли принимать трафик?Сервис может обработать запросУдаление из Service endpoints
StartupЗавершился ли старт?Приложение инициализировалосьЗадерживает liveness/readiness до успеха

Для LLM сервера:

  • Liveness — проверяет, что процесс HTTP-сервера отвечает (например, /health).
  • Readiness — проверяет, что модель загружена и готова (например, /ready).
  • Startup — даёт время на загрузку модели (например, до 10 минут), после чего начинает работать liveness.

4. Liveness probe: /health

Liveness probe — самая простая проверка. Эндпоинт /health должен возвращать HTTP 200, если процесс жив и HTTP-сервер обрабатывает запросы.

Реализация на FastAPI:

from fastapi import FastAPI
from fastapi.responses import JSONResponse

app = FastAPI()

@app.get("/health")
async def health():
    return JSONResponse(status_code=200, content={"status": "alive"})

Важно: не делать в liveness probe тяжёлых операций (проверка БД, вызов модели), иначе probe может сам вызвать проблемы. Liveness должен быть лёгким.


5. Readiness probe: /ready

Readiness probe проверяет, что модель загружена и готова к инференсу. Эндпоинт /ready возвращает 200, когда модель доступна, и 503 в противном случае.

Пример с глобальной переменной:

model_ready = False

@app.on_event("startup")
async def load_model():
    global model_ready
    # загрузка модели (может занимать минуты)
    model = await load_llm_model()
    model_ready = True

@app.get("/ready")
async def ready():
    if model_ready:
        return JSONResponse(status_code=200, content={"status": "ready"})
    else:
        return JSONResponse(status_code=503, content={"status": "not ready"})

Kubernetes будет периодически опрашивать /ready. Пока модель не готова, под не получит трафик.


6. Startup probe: /startup

Startup probe используется, когда приложению нужно много времени для инициализации. Он выполняется однократно при старте контейнера. Если startup probe не успевает за отведённое время, контейнер перезапускается.

Эндпоинт /startup может быть таким же, как /ready, но с большим initialDelaySeconds и failureThreshold. Или можно сделать отдельный эндпоинт, который возвращает успех только после полной загрузки.

@app.get("/startup")
async def startup():
    if model_ready:
        return JSONResponse(status_code=200, content={"status": "started"})
    else:
        return JSONResponse(status_code=503, content={"status": "loading"})

В Kubernetes манифесте:

startupProbe:
  httpGet:
    path: /startup
    port: 8000
  initialDelaySeconds: 5
  periodSeconds: 10
  failureThreshold: 60  # 10 минут на загрузку

7. Custom deep health check: /deep

Deep health check — это проверка, которая отправляет тестовый запрос к модели и проверяет, что ответ корректен. Позволяет выявить проблемы, которые не видны на уровне процесса или загрузки (например, модель зависла во время инференса, выдаёт мусор, или GPU memory leak).

Реализация:

import asyncio

@app.get("/deep")
async def deep_health():
    if not model_ready:
        return JSONResponse(status_code=503, content={"status": "model not ready"})
    try:
        # Отправляем простой тестовый запрос
        test_prompt = "Hello, are you working?"
        response = await asyncio.wait_for(
            model.generate(test_prompt, max_tokens=5),
            timeout=10.0
        )
        if response and len(response) > 0:
            return JSONResponse(status_code=200, content={"status": "healthy", "response": response})
        else:
            return JSONResponse(status_code=500, content={"status": "empty response"})
    except asyncio.TimeoutError:
        return JSONResponse(status_code=500, content={"status": "timeout"})
    except Exception as e:
        return JSONResponse(status_code=500, content={"status": "error", "detail": str(e)})

Важно: deep health check должен быть лёгким (короткий промпт, мало токенов), чтобы не создавать лишнюю нагрузку. Частота опроса — раз в 30–60 секунд, не чаще.


8. Реализация на Python (FastAPI) — полный пример

from fastapi import FastAPI, HTTPException
from fastapi.responses import JSONResponse
import asyncio

app = FastAPI()

model = None
model_ready = False

async def load_model():
    global model, model_ready
    # имитация загрузки
    await asyncio.sleep(30)
    model = "mock_llm"  # в реальности загрузка модели
    model_ready = True

@app.on_event("startup")
async def startup_event():
    asyncio.create_task(load_model())

@app.get("/health")
async def health():
    return {"status": "alive"}

@app.get("/ready")
async def ready():
    if model_ready:
        return {"status": "ready"}
    raise HTTPException(status_code=503, detail="Model not ready")

@app.get("/startup")
async def startup():
    if model_ready:
        return {"status": "started"}
    raise HTTPException(status_code=503, detail="Still loading")

@app.get("/deep")
async def deep():
    if not model_ready:
        raise HTTPException(status_code=503, detail="Model not ready")
    try:
        # тестовый запрос
        result = await asyncio.wait_for(
            asyncio.to_thread(model.generate, "test", max_tokens=5),
            timeout=10
        )
        return {"status": "healthy", "response": result}
    except Exception as e:
        raise HTTPException(status_code=500, detail=str(e))

9. Мониторинг и метрики

Помимо health check эндпоинтов, полезно собирать метрики:

  • Время загрузки модели — гистограмма.
  • Latency deep health check — если превышает порог, возможно, модель деградирует.
  • Количество ошибок на /ready, /deep.
  • GPU memory usage — можно добавить в /deep или отдельный эндпоинт /metrics для Prometheus.

Пример добавления метрик через Prometheus client:

from prometheus_client import Histogram, Gauge
import time

model_load_time = Gauge('model_load_seconds', 'Time to load model')
deep_check_duration = Histogram('deep_check_duration_seconds', 'Duration of deep health check')

10. Graceful shutdown

При завершении работы сервера (например, при масштабировании) нужно корректно обработать shutdown:

  • Перестать принимать новые запросы (readiness → 503).
  • Дождаться завершения текущих инференсов.
  • Выгрузить модель из GPU памяти.

В FastAPI можно использовать @app.on_event("shutdown"):

@app.on_event("shutdown")
async def shutdown_event():
    global model_ready
    model_ready = False
    # ждём завершения активных запросов
    await asyncio.sleep(5)
    # выгрузка модели
    del model

11. Специфика для агентных систем

В Agentic RAG сервер может обслуживать несколько моделей (LLM для генерации, эмбеддинг-модель, ранжировщик). Health check должен проверять каждую:

  • /ready — все модели загружены.
  • /deep — тестовый запрос ко всем моделям (или выборочно).

Также можно добавить проверку доступности внешних инструментов (векторная БД, API поиска) — это уже выходит за рамки health check модели, но важно для агента.


Пет-проект для закрепления

Задача Реализовать LLM сервер (на FastAPI) с полным набором health check эндпоинтов и развернуть его в Kubernetes с корректными probes.

Инструменты Python, FastAPI, Docker, Kubernetes (minikube или kind), библиотека transformers (для реальной модели, можно взять distilgpt2).

Шаги:

  1. Создать FastAPI приложение с эндпоинтами /health, /ready, /startup, /deep.
  2. Реализовать загрузку модели (например, pipeline("text-generation", model="distilgpt2")) в фоновом режиме.
  3. Добавить deep health check с коротким промптом и таймаутом.
  4. Обернуть приложение в Docker-образ.
  5. Написать Kubernetes манифест (Deployment + Service) с probes:
  6. Развернуть в minikube, проверить, что под переходит в Ready только после загрузки модели.
  7. Имитировать сбой (например, убить процесс внутри контейнера) — убедиться, что liveness перезапускает под.

Ожидаемый результат Работающий сервер, который корректно обрабатывает загрузку, перезапуски и направление трафика. Deep health check выявляет зависания модели.


Связь с другими вопросами

ВопросТема
245Как деплоить LLM сервер в production
246Мониторинг LLM сервера (latency, throughput)
247Graceful shutdown и обработка сигналов
248Rate limiting и защита от перегрузок
249A/B тестирование моделей
251Логирование запросов и ответов

Навигация