Почему 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
Рекомендуемые панели (группировка по реплике):
- Token throughput over time — главная линия. Падение = проблема.
- Latency percentiles (p50, p90, p99) для TTFT и TPOT отдельно. p99 показывает хвост — где пользователи реально страдают.
- KV cache usage % — заполненность. Если держится > 90% постоянно, не хватает capacity или контекст слишком большой.
- GPU utilization % — загрузка. Низкий utilization при высокой нагрузке = bottleneck не в GPU (в сети, в балансировщике, в CPU).
- Queue length — очередь. Растёт = не справляемся.
- Prefix cache hit rate — эффективность кэша. Падение = префиксы перестали повторяться (или балансировка сломалась).
- 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 недели мы работали без мониторинга. Результат:
- Мы не знали, что KV-cache hit rate упал с 34% до 12% после изменения балансировки — обнаружили случайно, когда throughput просел.
- Мы не видели, как одна реплика постепенно деградировала (темп. рост, замедление) — узнали, когда она упала.
- Мы не могли отличить «нагрузка выросла» от «что-то сломалось» — без метрик это две одинаковые проблемы.
После этого мы подняли 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.