Почему observability — не «luxury»

Без мониторинга вы не знаете, что происходит с инференсом: падает ли throughput, растёт ли latency, заполняется ли KV-cache. В production это значит, что вы обнаруживаете деградацию от пользователей, а не от дашборда. В нашем референс-деплое мы 2 недели работали без мониторинга — и это стоило дорого (см. ниже).

Ключевые метрики vLLM

Throughput (производительность)

  • Request throughput (запросы/с) — сколько запросов обработано в секунду.
  • Token throughput (токены/с) — сколько токенов сгенерировано в секунду (input + output). Это главная метрика: для LLM-инференса токены — единица работы.

Latency (задержки)

  • TTFT (Time to First Token) — время до первого токена. Определяет «отклик» для пользователя. Для interactive-нагрузок это критичная метрика SLA.
  • TPOT (Time per Output Token) — время генерации одного токена после первого. Определяет «скорость печати».
  • Total latency — полное время обработки запроса (TTFT + TPOT × num_output_tokens).

GPU и память

  • GPU memory usage — использование VRAM.
  • GPU utilization — загрузка вычислительных ядер (%).
  • KV cache usage — заполненность пула KV-блоков (%). Ключевая метрика: если KV-cache заполнена, новые запросы ждут, и latency растёт.

Scheduler

  • Queue length — сколько запросов в очереди. Растущая очередь = нехватка capacity.
  • Running requests — сколько запросов активно в батче.
  • Prefix cache hit rate — доля запросов, попавших в кэш префикса. Высокий hit rate = меньше prefill = выше throughput.

Настройка Prometheus

vLLM экспортирует метрики через /metrics endpoint в формате Prometheus. Подключите Prometheus-скрейпер:

scrape_configs:
  - job_name: vllm
    metrics_path: /metrics
    static_configs:
      - targets:
          - 'vllm-replica-1:8000'
          - 'vllm-replica-2:8000'
          - 'vllm-replica-3:8000'

Для GPU-метрик добавьте dcgm-exporter (Data Center GPU Manager) — он даёт utilisation, memory, temperature по каждой карте.

Grafana Dashboard

Рекомендуемые панели (группировка по реплике):

  1. Token throughput over time — главная линия. Падение = проблема.
  2. Latency percentiles (p50, p90, p99) для TTFT и TPOT отдельно. p99 показывает хвост — где пользователи реально страдают.
  3. KV cache usage % — заполненность. Если держится > 90% постоянно, не хватает capacity или контекст слишком большой.
  4. GPU utilization % — загрузка. Низкий utilization при высокой нагрузке = bottleneck не в GPU (в сети, в балансировщике, в CPU).
  5. Queue length — очередь. Растёт = не справляемся.
  6. Prefix cache hit rate — эффективность кэша. Падение = префиксы перестали повторяться (или балансировка сломалась).
  7. Running / waiting requests — активные vs ожидающие.

Алерты

Минимальный набор алертов:

  • TTFT p99 > SLA (например, > 3s для interactive) — пользовательский хвост.
  • KV cache usage > 90% в течение 5 минут — нехватка capacity.
  • Queue length > N — очередь растёт.
  • Replica down (up == 0) — реплика упала.
  • GPU memory > 95% — близко к OOM.

Что мы видели в референс-деплое

Честный урок: первые 2 недели мы работали без мониторинга. Результат:

  1. Мы не знали, что KV-cache hit rate упал с 34% до 12% после изменения балансировки — обнаружили случайно, когда throughput просел.
  2. Мы не видели, как одна реплика постепенно деградировала (темп. рост, замедление) — узнали, когда она упала.
  3. Мы не могли отличить «нагрузка выросла» от «что-то сломалось» — без метрик это две одинаковые проблемы.

После этого мы подняли Prometheus + dcgm-exporter + Grafana (один дашборд на 3 реплики). companion-сайт (полный Prometheus-стек на Vue/Nuxt) — отдельный проект, который мы отложили, но базовый мониторинг — обязательно.

Ключевые инсайты после подключения мониторинга:

  • KV cache usage — наша главная операционная метрика. Она раньше всех показывает, что capacity заканчивается.
  • Prefix cache hit rate — индикатор того, что балансировщик работает. Если он падает, проблема в routing, а не в железе.
  • TTFT p99 — то, что видят пользователи. Средний TTFT может быть нормальным, а p99 — катастрофическим (длинные промпты, холодный кэш).

Лог-базированная observability

Метрики отвечают «сколько и как быстро», но не «почему». Для отладки нужны логи с корреляцией:

  • Request ID: каждый запрос получает уникальный ID, который проходит через балансировщик → vLLM → logs. Это позволяет найти все логи одного запроса.
  • Структурные логи (JSON): vLLM и ваш балансировщик должны логировать в JSON с полями request_id, tenant, model, tokens_in, tokens_out, ttft_ms, tpot_ms, status. Это позволяет агрегировать и фильтровать.
  • Ключевые события: запрос принят, prefill завершён, первый токен, запрос завершён, ошибка, OOM, eviction из KV-cache.

Без request ID и структурных логов отладка инцидента — это гадание: вы видите, что p99 TTFT вырос, но не можете найти, какие запросы замедлились и почему.

Tracing для распределённой системы

Когда у вас балансировщик + несколько реплик, запрос проходит через несколько сервисов. Distributed tracing (OpenTelemetry) связывает spans:

[балансировщик: routing] → [реплика-1: prefill] → [реплика-1: decode] → [балансировщик: stream back]

Это показывает, где именно задержка: в routing (балансировщик медленный), в prefill (промпт длинный), или в decode (модель медленная). Для production с несколькими репликами tracing — единственный способ отличить «балансировщик медленный» от «GPU медленный».

Минимальная конфигурация: OpenTelemetry SDK в балансировщике + vLLM (vLLM поддерживает OTel-экспорт) + Jaeger/Tempo для хранения.

Вывод

Observability для vLLM — это метрики (token throughput, TTFT/TPOT p50/p90/p99, KV cache usage, queue length, prefix cache hit rate) + логи (request ID, JSON, ключевые события) + tracing (для нескольких реплик). Поднимите Prometheus + dcgm-exporter + Grafana до production, а не после первого инцидента. Две недели без мониторинга стоят дороже, чем день на его настройку.

Полная история — в части 5 серии «Наш опыт». Смежные материалы: Multi-tenant Serving, PagedAttention.

← Multi-tenant Serving Qwen 27B на 2×RTX 3090 →