Model serving concept page for /concepts/model-serving — REST inference, vLLM continuous batching with PagedAttention, INT4 quantization, and offline batch scoring on spot GPUs. Includes ADR comparing Triton vs vLLM vs Ray Serve vs KServe/BentoML.
Modern продукты 2026-го — это inference на каждый запрос. ChatGPT-style чат, copilot в IDE, RAG-поиск в docs-портале, image generation, voice-агент, fraud-detection на каждую транзакцию, рекомендации на каждый scroll. Train ML model раз в неделю — это половина работы; serv-ить её 24/7 под нагрузкой с предсказуемой latency и приемлемой ценой — вторая половина, и она дороже. Команда, которая не понимает model serving, либо разорится на API-биллах OpenAI/Anthropic, либо сожжёт месяцы инженерных усилий на самохост, который работает в 5-10 раз медленнее, чем мог бы.
Второй мотив — GPU экономика принципиально отличается от CPU. CPU-сервер стоит $50-200/месяц, легко autoscale-ится, простаивает дёшево. A100 80GB — $1500-3000/месяц on-demand в облаке, H100 — $4000-8000. Простаивающий GPU — это прямой убыток $30-100/час. Все классические web-задачи (latency, throughput, autoscaling, caching) применимы к GPU-серверам, но с поправкой «единица работы = forward pass на десятки миллиардов параметров», а ошибка в архитектуре умножается на цену железа. Тот же chatbot, наивно построенный на голом model.predict() в FastAPI-handler'е, vs тот же chatbot на vLLM с continuous batching — разница в 5-10 раз по throughput на одном и том же GPU.
Третий — inference frameworks стремительно эволюционируют. 2022: все писали FastAPI обёртки. 2023: пришёл vLLM с PagedAttention и положил наивный подход. 2024: continuous batching стал стандартом, появилась speculative decoding. 2025-2026: квантизация в INT4 стала production-ready, KV cache стал first-class concern, OpenAI-compatible API стал интерфейсным стандартом. Команда, которая запускает inference на стеке двухлетней давности, переплачивает в 10-30 раз относительно state-of-the-art.
Без понимания model serving невозможно проектировать ::concept{slug="mlops-pipeline"}, ::concept{slug="feature-store"}-зависимые real-time scoring системы, RAG-pipeline'ы с разумной latency, и оценивать build-vs-buy решения для LLM-фич в продукте.
Model serving — это веб-сервер, у которого единица работы = forward pass, единица ресурса = GPU-секунда, и весь classical web-stack (load-balancing, autoscale, cache, retry, circuit-breaker) применяется поверх GPU-специфичных оптимизаций: continuous batching, KV cache, quantization, tensor parallelism.
Ключевая интуиция — GPU дорогой и не любит idle. Цель архитектуры — держать GPU utilization 70-90% непрерывно. Всё остальное (batching, caching, autoscaling, model selection) — это техники для достижения этой цели.
Главные оси trade-off'ов одной таблицей:
| Ось | Дёшево/медленно | Дорого/быстро |
|---|---|---|
| Точность модели | Mini/Haiku/distilled 8B | GPT-4o, Claude Opus, Llama 405B |
| Прецизия | INT4 quantized | FP16/BF16 native |
| Batching | continuous, batch=32+ | batch=1 (на каждый user) |
| Cache | aggressive (KV + semantic) | none |
| Hardware | spot A10/T4 batch | on-demand H100 cluster |
| Топология | self-host vLLM | OpenAI/Anthropic API |
Каждая ось имеет sweet spot для конкретного use case. Chat UX требует FP16+ для качества, но aggressive batching и KV cache для дешевизны. Batch scoring 100M юзеров overnight — INT4, spot GPUs, нет real-time SLA, цена в 100 раз меньше. Universal answer не существует.
На канвасе — production-grade hybrid inference stack, разрезанный на пять логических слоёв.
Сверху слева — Clients. Три типичных клиента: chat UI (streaming, нужен sub-200ms TTFT), mobile app (REST, batch tolerable до 1s), batch scorer (overnight cron, нет real-time SLA). Они принципиально разные по требованиям и идут по разным путям через систему.
Сверху справа — API gateway + routing. gateway (Envoy) терминирует HTTPS, auth + rate limit. cache (Redis) — semantic-hash response cache: хэш считается по embedding промпта, hit при cosine similarity > 0.97. На типичном chat-трафике cache hit rate 20-40% — это чистая экономия GPU. router — sticky-routing по user_id для переиспользования KV cache на vLLM workers (если тот же user уже в активном batch — его запрос на тот же worker, чтобы system prompt prefix уже сидел в кэше).
Слева в центре — vLLM cluster. Два vLLM workers по 1×A100 каждый, держат Llama-3.3-70B INT4 (35 GB на worker, fits в 80GB A100 с большим запасом для KV cache). kv-cache — paged KV memory pool (PagedAttention, как virtual memory для attention K/V). autoscaler — KEDA, скейлит по GPU utilization + pending requests (НЕ по CPU — это типичная ошибка).
Справа в центре — Triton cluster. Multi-framework сервер для не-LLM моделей: CV (image classification, object detection), embeddings (BERT/sentence-transformers), tabular (XGBoost, LightGBM в ONNX). Triton умеет dynamic batching, ensembles (DAG of models), versioning через model repository в S3. Один Triton сервит несколько разных моделей с разных фреймворков — единая operational unit для всего «зоопарка».
Снизу слева — Batch scoring. Отдельный путь для offline workloads: Spark MLlib для tabular pre-processing + feature engineering, Ray Data для GPU batch inference на spot A10 instances ($0.30/h vs $1.00 on-demand), результаты пишутся в warehouse. Никаких real-time SLA, можно толерировать spot eviction, dramatically дешевле online-пути.
Снизу справа — Observability. Prometheus собирает inference-specific метрики (TTFT — time to first token, tokens/sec, GPU utilization, batch size distribution, p99 latency per model). eval — quality gate: автоматический регрессионный suite на каждый model update, ловит quantization-induced quality drops до того, как они дойдут до пользователя.
ADR-001 живёт на группе edge — главный архитектурный вопрос: «какой serving stack выбрать». Triton vs vLLM vs Ray Serve vs KServe vs BentoML — это не «лучший vs худший», а матрица «что serv-ишь × какая команда × какие constraints». Подробный разбор ниже.
1. REST inference (rest-inference) — happy path online-запроса. Mobile app шлёт POST /v1/chat/completions, gateway делает auth+rate-limit, lookup в semantic cache (промах в этом сценарии). Router выбирает vLLM cluster и vllm-1 по sticky-routing. Запрос становится в continuous batch (слот 7 из 16, batch уже бежит), KV cache переиспользует system prompt prefix (уже paged), аллоцирует новые pages для user input. Ответ стримится клиенту (TTFT 180ms, throughput 45 tok/s), метрики уезжают в Prometheus, ответ async-пишется в cache для возможного hot replay. Учит: «online inference — это streaming pipeline, не request-response; cache + sticky routing + continuous batching — три рычага дешевизны».
2. Continuous batching (continuous-batch) — наглядная демонстрация, как vLLM выжимает 3-5× больше throughput на том же GPU. T=0: Request A (long, 500 токенов ожидаемо), batch slot 0, GPU util ~30%. T=20ms: Request B (short, 50 токенов), присоединяется к тому же batch на следующем token-step, не ждёт окончания A. T=50ms: Request C, slot 2. T=600ms: B завершился, slot 1 освобождён mid-batch. T=620ms: Request D мгновенно занимает освобождённый slot. T=2.1s: A завершился. GPU utilization удерживается на 80%+ непрерывно. Учит: «без continuous batching = 30% GPU util, с ним = 80%+, разница ~3× в стоимости. Это главная причина, почему наивный model.generate() в FastAPI handler'е — экономическая катастрофа».
3. Quantization (quantization) — экономическая магия INT4. До: Llama-3.3-70B FP16 = 140GB, требует 2-4× A100 80GB (TP=2 минимум), $8-16/час, batch limited до 8 (KV cache съедает память), throughput ~40 req/min, $1.20 per 1M tokens. После: INT4 (AWQ/GPTQ) = 35GB, fits в 1×A100 80GB, $4/час, batch до 32, throughput ~160 req/min, $0.08 per 1M tokens. 15× cheaper per token, latency same or better (smaller memory fetch). Ключевая защита: eval gate ДО prod — regression suite на 500 промптов, проверка MMLU/HumanEval/etc, опубликовать дельты, gate на acceptable threshold (например MMLU drop < 2%). Без eval gate: INT4 может тихо сломать edge cases (математика, code, structured output). Учит: «quantization — bread-and-butter cost optimization, но всегда парится с eval suite + canary 5% на 24h перед full rollout».
4. Batch scoring (batch-scoring) — offline path. Cron в 02
Context. Выбор inference-фреймворка — это матрица «что serv-ишь × какая команда × какие constraints». Главные кандидаты:
Triton (NVIDIA Inference Server) — general-purpose multi-framework сервер. Умеет TF/PyTorch/ONNX/TensorRT/Python backends, dynamic batching, ensembles (DAG of models), model repository с версионированием, gRPC + HTTP + C-API. Силён когда зоопарк моделей разных типов: одна команда сервит CV (ResNet ONNX) + NLP (BERT TF) + tabular (XGBoost ONNX) + custom Python — Triton даёт единый control plane, единый metrics endpoint, единый GPU pool с приоритизацией. Слаб для современных LLM: continuous batching либо отсутствует, либо через TensorRT-LLM backend (extra setup), KV cache management — на ваших плечах, PagedAttention out-of-the-box нет.
vLLM — purpose-built для LLM serving. PagedAttention (virtual-memory-style для KV cache, fragmentation lite) даёт +24× throughput на LLM-ах vs naive serving, continuous batching на token-level (новый request влетает в текущий batch когда освобождается слот, не ждёт окончания других), OpenAI-compatible REST API (drop-in replacement для openai SDK), tensor parallelism across GPU, speculative decoding, AWQ/GPTQ/INT4 quantization. Слабость — ТОЛЬКО для LLM (decoder-only трансформеры в основном); CV, embeddings, classifiers — мимо.
Ray Serve — orchestration framework на Ray: composable Python deployments, multi-model pipelines (preprocess → embed → classify → postprocess как DAG), dynamic batching, autoscaling, A/B routing, traffic splitting, model multiplexing (1000s моделей на одном кластере, lazy load on demand). Силён когда ML-pipeline сложный (несколько моделей цепочкой с business-logic между) или multi-tenant SaaS (тысячи fine-tuned моделей per-tenant). Слаб для pure-throughput LLM serving — overhead Python/Ray actor system нетривиален vs vLLM raw.
KServe (Kubernetes-native) — control plane поверх любого backend, scale-to-zero, canary, multi-model serving, но требует K8s expertise и ставит дополнительный слой.
BentoML — DX-friendly Python-first packaging, хорош для prototype-to-prod одного-двух моделей.
Decision. Default по use case:
pending_requests + GPU memory.ВСЕГДА:
minReplicas >= 1 на critical path (cold start модели 30-120s);НИКОГДА:
model.predict() — будете годами догонять то, что Triton/vLLM дают из коробки.Context. Команда хочет LLM-фичу. Вариант A — позвать OpenAI/Anthropic API (Together.ai/Fireworks для open-source). Вариант B — самохост Llama/Mistral на vLLM cluster в своём облаке. Каждый имеет диапазон условий применимости.
Decision. API по умолчанию до 10M tokens/day, self-host выгоден от ~110M tokens/day на одном A100, при 50% utilization. Между этими точками — gray zone, решается по non-cost факторам: latency требования, compliance (data residency, PII handling), кастомизация (fine-tuning без раскрытия данных), control над moderation/safety фильтрами.
Trade-off.
Часто оптимум — hybrid: routing layer выбирает API для low-volume сложных запросов (frontier модели), self-host для high-volume простых (где Llama-3.3-70B справляется).
Context. Batching — главный рычаг GPU utilization. Три варианта: static, dynamic, continuous.
Decision. Для LLM — continuous batching без вариантов (vLLM/TGI/TensorRT-LLM). Для CV/embeddings — dynamic batching с timeout 5-20ms (Triton). Static — только для offline batch scoring.
Trade-off. Continuous batching даёт 3-5× throughput vs naive, но усложняет реализацию (требует token-level scheduler) — поэтому используем готовые фреймворки, не пишем сами. Dynamic batching trade-off: больше timeout = больше throughput, но больше latency tail. Sweet spot — 10ms для real-time, 50-100ms для batch-tolerable.
Context. Quantization — bread-and-butter cost optimization. FP32 → FP16 (×2 памяти, бесплатно по качеству), → INT8 (×4, 1-2% качества), → INT4 (×8, 5-10% качества; AWQ/GPTQ алгоритмы), → 1-bit (BitNet, экспериментально).
Decision. FP16/BF16 — must для inference (FP32 нет смысла, free win). INT8 — safe default для большинства production моделей. INT4 — для LLM 70B+ где экономия GPU критична, но обязательно с eval gate + canary. 1-bit — не для prod в 2026.
Trade-off. На каждый шаг вниз: ~2× memory savings + ~2× throughput (smaller mem fetch), но возможный quality drop. Без eval suite ты узнаешь о regression от пользователя через support ticket. Pair quantization с automated regression suite (MMLU, HumanEval, domain-specific evals), gate deployment на acceptable threshold, canary 5% × 24h перед full rollout.
OpenAI ChatGPT — TensorRT-LLM на десятках тысяч H100, KV cache sharing, speculative decoding. Custom MoE routing для GPT-4 family. Prompt caching — ставшая стандартом фича, originally OpenAI-specific.
Anthropic Claude — proprietary inference stack, prompt caching native (даёт 10× cost reduction для repeated context), streaming-first architecture, custom safety filters в gateway.
Together.ai / Fireworks / Anyscale — multi-tenant vLLM кластеры, OpenAI-compatible API. Маржа делается на эффективной утилизации железа: 70-90% GPU util vs 30% типичной команды-самохоста.
Cloudflare Workers AI — edge inference на CDN nodes (GPU at edge), ms latency для users worldwide. Workers Loadbalancer routes inference requests к ближайшему GPU.
GitHub Copilot — fine-tuned Codex/GPT variants, custom serving с KV cache reuse для open files в IDE. Сильная оптимизация под autocomplete pattern (короткий context, latency-critical).
Tesla FSD — onboard inference на Tesla custom chip, custom NN compiler. Latency budget ~50ms на kadr, no network involved.
Spotify — TF Serving + Ray для recommendations. Batched scoring для personalized playlists overnight + real-time inference для discovery.
Pinterest — Triton для multi-model serving (visual search, ad ranking, recommendations) на едином GPU pool с приоритизацией.
Uber Michelangelo — internal ML platform, TF Serving + custom routing. Real-time ETA models на каждый ride request, batch scoring для surge pricing.
model.predict() — годами догонять то, что vLLM/Triton дают из коробки. Никогда не пишите своё inference от нуля в prod.minReplicas >= 1 для critical paths, scale-to-zero только для cold tier.