Развернуть LLM на своих серверах — это полдела. Удерживать её в продакшене, когда пользователи долбят запросами, а модель начинает тормозить, сыпать ошибками или выдавать "ответы" из пустоты — вот где начинается настоящий DevOps-ад. Я перерыл кучу дашбордов, починил десяток инференс-серверов и понял: есть всего пять метрик, которые реально решают. Остальное — шум.
⚡ Спойлер: одна из этих метрик — контекстное окно. В одном из моих проектов увеличение контекста с 64k до 85k токенов убило 73,5% всех ошибок. Просто потому, что модель перестала резать промты на полуслове. Подробнее — ниже.
Метрика №1: Queue Depth — глубина очереди, или сколько ещё ждать?
Queue Depth показывает, сколько запросов сейчас висят в ожидании обработки. Если значение стабильно больше 0 — ваш инференс-сервер перегружен. Если растёт — всё, пора бить тревогу.
Но почему это критично? Потому что LLM — не elasticsearch. Она не умеет параллелить всё подряд. Большинство движков (vLLM, llama.cpp, TensorRT-LLM) работают в режиме batching: собирают пачку промтов, прогоняют через GPU, отдают. Если очередь забивается, latency (время ответа) улетает в небо, а пользователи получают timeout.
В vLLM за queue depth отвечает параметр max_num_seqs. По умолчанию часто стоит 256, но для продакшена с высокой нагрузкой это мало. Я поднимал до 512 — помогло, но упёрся в VRAM.
Как мониторить: поставьте экспортёр Prometheus для вашего инференс-движка. Для vLLM есть встроенные метрики vllm:num_requests_waiting. Для llama.cpp — llamacpp:queue_size. Если у вас ещё нет мониторинга, почитайте наш гайд по Grafana+Prometheus — там всё расписано с нуля.
Метрика №2: Active Workers — кто сейчас шевелится?
Active Workers — количество процессов/потоков, которые прямо сейчас заняты инференсом. Если эта цифра равна максимальному числу воркеров — сервер загружен под завязку. Если при этом Queue Depth растёт — надо добавить GPU или оптимизировать батчинг.
Но есть нюанс: в vLLM воркеры — это не потоки ОС, а внутренние слоты для batch. Я видел кейс, когда Active Workers = 0, хотя нагрузка есть. Оказалось, модель крашилась в цикле, но healthcheck этого не видел. Пришлось добавить проверку на количество успешных генераций за минуту.
Совет: настройте алерт на active_workers == 0 дольше 30 секунд при ненулевой очереди. Это спасёт от "тихого" падения.
Кстати, про выбор движка и архитектуры: здесь я сравнивал стратегии развёртывания — от облачных API до локального хостинга. Для продакшена чаще всего выбирают vLLM из-за гибкого управления воркерами.
Метрика №3: Latency — или почему пользователь ушёл к ChatGPT?
Latency — время от отправки запроса до получения первого токена (TTFT) и полного ответа (TPOT — time per output token). Для чат-приложений критичен TTFT: если модель думает дольше 2 секунд, пользователь закрывает вкладку.
Но цифры зависят от модели. Llama 3.3 70B на двух A100 выдаёт TTFT ~300 мс при маленькой очереди. Если вы используете Qwen 2.5 72B 4-bit на одной A6000 — готовьтесь к 2-3 секундам. Главное — не абсолютные значения, а их стабильность. Резкие пики latency — признак проблем с памятью или батчингом.
| Метрика | Что показывает | Порог алерта |
|---|---|---|
| TTFT (Time to First Token) | Время до первого слова | > 3 сек |
| TPOT | Время на каждый последующий токен | > 50 мс/токен (для 7B) |
| P95 latency | Худший случай среди 95% запросов | > 5 сек |
Мониторить latency можно через встроенные метрики vLLM (vllm:request_ttft) или через OpenTelemetry. Если хотите готовый дашборд — в нашем гайде по мониторингу есть шаблон для Grafana.
Метрика №4: Context Window Utilization — вы обрезаете промты, и это дорого
Это та самая метрика, которая спасла 73,5% ошибок в моём кейсе. Context Window Utilization — какой процент от доступного контекстного окна реально используется моделью. Если вы видите, что utilisation часто превышает 90% — модель режет промты, теряет контекст, начинает галлюцинировать.
Как это выглядит на практике:
- Пользователь загружает длинный документ (100k токенов).
- Модель с окном 128k — нормально.
- Но если используется RoPE scaling или из-за ошибки конфига выставлено 64k — начинаются чудеса: ответы обрываются, теряются факты, растёт количество ошибок "context length exceeded".
В моём случае инференс-сервер возвращал 400 Bad Request: prompt too long для промтов длиннее 64k. Увеличил контекст до 85k (разрешил RoPE scaling) — и 73,5% ошибок исчезли. Оставшиеся 26,5% — это банальные таймауты из-за долгих генераций, их мы починили батчингом.
💡 Совет: всегда проверяйте реальное окно модели при загрузке. У Mistral Large 2 заявлено 128k, но если вы не включите соответствующие флаги в llama.cpp, модель будет работать с 8k. В статье про Ollama и альтернативы я показывал, как корректно выставлять контекст.
Как мониторить: добавьте в Prometheus метрику (total_prompt_tokens / max_context_tokens) * 100. Если среднее per-minute utilisation > 85% — планируйте апгрейд контекста или меняйте модель.
Метрика №5: GPU Memory Fragmentation — тихий убийца производительности
Фрагментация видеопамяти — это когда после нескольких тысяч запросов VRAM разбита на мелкие кусочки, и новый batch не влезает целиком, хотя свободной памяти номинально много. В результате — OOM или дикое замедление.
В vLLM используется memory pool (pre-allocated blocks), поэтому фрагментация минимальна. Но если вы используете llama.cpp с --no-mmap и без --numa, память может фрагментироваться. В TensorRT-LLM есть параметр workspace_size, который тоже можно подкрутить.
Как диагностировать:
- Сравните
free_memoryиmax_allocatable_block(доступно через nvidia-smi или /proc/meminfo внутри контейнера). - Если разница больше 20% — фрагментация.
- Решение: перезапуск сервера (менее 5 минут), либо включите aggressive defragmentation в vLLM (флаг
--enable-defragдоступен с версии 0.7.3).
Кстати, про выбор железа для self-hosted LLM: у нас есть подробный гайд по сборке станции за $15 000. Там я разбираю, какие GPU меньше склонны к фрагментации (спойлер: H100 с NVLink лучше, чем 4 RTX 4090).
Бонус: метрика-призрак — Quality Metrics
Технические метрики — это база. Но если модель генерирует чушь, а latency идеальное — вы всё равно получите негатив от пользователей. Добавьте метрики качества: долю ответов с повторами, тошнотой (perplexity), длину выхлопа. Можно использовать встроенный evaluator vLLM или внешний сервис типа LangFuse.
Мы в одном проекте прикрутили простой скрипт: после генерации считаем количество уникальных биграмм. Если оно < 30% от длины ответа — модель зациклилась, шлём алерт. Снизило ложные срабатывания поддержки на 40%.
Типичные ошибки и как их не допустить
- Забыли про healthcheck. LLM-сервер может висеть с zero active workers, но healthcheck по TCP порту проходит. Добавьте эндпоинт /health, который проверяет реальную генерацию короткого промта.
- Не ограничили max_concurrent_requests. Без лимита при резком всплеске Queue Depth может уйти в тысячи, и сервер упадёт.
- Используете дефолтные настройки контекста. Всегда проверяйте реальный размер окна (выше я уже указывал).
- Не смотрите на P99 latency. Средняя latency может быть 500 мс, но каждый сотый запрос длится 10 секунд — это убивает UX.
- Экономите на GPU памяти. Если модель занимает 90% VRAM, любая фрагментация приведёт к OOM. Оставляйте запас 15-20%.
Что в итоге?
Пять метрик — Queue Depth, Active Workers, Latency, Context Window Utilization, GPU Memory Fragmentation — это тот минимум, который должен быть на каждом дашборде. Но не останавливайтесь на них. Добавьте метрики качества — и ваша self-hosted LLM будет работать стабильнее, чем средний SaaS.
Кстати, я веду блог про self-hosted AI и DevOps. Если хотите погрузиться в тему глубже — почитайте мою статью про self-hosting в 2026: там я сравниваю затраты и объясняю, почему даже для небольших проектов иногда выгоднее своё железо.
🔥 Прогноз: к концу 2027 года большинство инференс-серверов будут иметь встроенные агенты автоскейлинга, которые анализируют эти метрики и динамически меняют batch size, context window и количество воркеров без участия инженера. Но пока что это делаем мы — руками.