vLLM у Python: посібник з продакшн-сервінгу LLM у 2026

Практичний посібник із vLLM для продакшн-сервінгу LLM у Python: установка, OpenAI-сумісний сервер, tensor parallelism, FP8/AWQ квантизація, моніторинг і типові граблі з польового досвіду.

vLLM Python: продакшн LLM-сервінг 2026

Оновлено: 2 червня 2026

vLLM — це Python-фреймворк для високопродуктивного інференсу великих мовних моделей, який завдяки PagedAttention та continuous batching дає у 5–24 рази вищу пропускну здатність порівняно з наївним Hugging Face Transformers і на 2026 рік є фактичним стандартом для self-hosted LLM-сервінгу. Чесно кажучи, я пройшов через TGI, TensorRT-LLM, Triton і власні саморобні сервери на FastAPI, і vLLM перемагає в більшості сценаріїв, де важливі latency під SLO і cost-per-token. Втім, він має свої підводні камені, про які демонстраційні туторіали мовчать.

  • vLLM 0.6.x на 2026 рік підтримує PagedAttention v2, continuous batching, prefix caching, speculative decoding та AWQ/GPTQ/FP8 квантизацію з коробки.
  • OpenAI-сумісний HTTP-сервер запускається однією командою vllm serve, і клієнти на openai SDK працюють без змін коду.
  • PagedAttention зменшує фрагментацію KV-кешу до 4% (проти 60–80% у наївних реалізаціях), що дозволяє вмістити у 2–4 рази більше одночасних запитів на одну GPU.
  • Tensor parallelism через --tensor-parallel-size N масштабує модель на N GPU; для моделей понад 70B параметрів додатково потрібен pipeline parallelism.
  • На продакшн-навантаженнях критично моніторити time-to-first-token, inter-token latency, GPU KV-cache utilization і queue depth, бо стандартні CPU/RAM-метрики тут марні.
  • vLLM не замінює оркестрацію: для autoscaling, A/B-роутингу та канаркових деплоїв все одно потрібен KServe, Ray Serve або власний шар поверх.

Що таке vLLM і чому він швидший за Hugging Face Transformers

vLLM, це open-source движок LLM-інференсу, який народився у Berkeley Sky Computing Lab у 2023 році й тепер підтримується спільнотою з понад 600 контриб'юторів. Ядро написане на Python з критичними CUDA-ядрами на C++/Triton. Головна ідея полягає в тому, щоб переосмислити, як обчислюється attention під час інференсу, адже саме там у наївних реалізаціях втрачається 60–80% пам'яті GPU на фрагментацію KV-кешу.

Класичний Hugging Face generate() працює у режимі static batching: ви збираєте батч із N запитів, чекаєте, поки усі N згенерують останній токен, і тільки тоді приймаєте наступний батч. Якщо один запит це 4-токенне «yes», а інший — есе на 2000 токенів, GPU простоюватиме 99% часу. vLLM натомість використовує continuous batching: щойно один запит завершується, на його місце з черги одразу підставляється наступний, на рівні окремих токенів, а не запитів. На реальних чат-навантаженнях це дає 5–10× приріст throughput; на завданнях з різнорідними довжинами до 24×.

Друге ноу-хау, це PagedAttention, описаний у оригінальній статті Kwon et al.. Замість того щоб резервувати безперервний блок пам'яті під максимальну довжину контексту для кожного запиту (як це робить наївна реалізація), vLLM розбиває KV-кеш на блоки фіксованого розміру (типово 16 токенів) і керує ними так, як операційна система керує сторінками віртуальної пам'яті. Це майже повністю усуває внутрішню фрагментацію. На типових навантаженнях я бачив падіння wasted-memory з 70% до 3–4%.

Встановлення та перший запуск vLLM у Python

vLLM 0.6.x вимагає Python 3.9–3.12, CUDA 12.1+ і Linux. Для macOS і Windows офіційної підтримки GPU немає: на Mac можна запускати тільки CPU-режим, що для будь-чого серйознішого за дебаг марно. Базова установка:

# Створіть ізольоване середовище, бо vLLM конфліктує
# з версіями torch і flash-attn у інших проєктах
python -m venv .venv-vllm
source .venv-vllm/bin/activate

pip install --upgrade pip
pip install vllm==0.6.4

# Перевірте, що CUDA видно
python -c "import torch; print(torch.cuda.is_available(), torch.cuda.get_device_name(0))"

Найпростіший inline-приклад, це оффлайн-генерація без HTTP-сервера. Корисно для batch-обробки, оцінки якості моделей або генерації синтетичних даних:

from vllm import LLM, SamplingParams

llm = LLM(
    model="meta-llama/Llama-3.1-8B-Instruct",
    dtype="bfloat16",
    gpu_memory_utilization=0.90,  # vLLM зарезервує 90% VRAM під ваги + KV-cache
    max_model_len=8192,           # обріжте контекст, якщо вам не потрібні всі 128k
)

sampling = SamplingParams(
    temperature=0.7,
    top_p=0.95,
    max_tokens=512,
)

prompts = [
    "Поясни PagedAttention двома реченнями.",
    "Напиши SQL-запит для топ-5 клієнтів за виручкою.",
]

outputs = llm.generate(prompts, sampling)
for out in outputs:
    print(out.prompt)
    print("→", out.outputs[0].text)
    print("---")

Як підняти OpenAI-сумісний HTTP-сервер

Це, мабуть, найцінніша фіча vLLM для продакшну: одна команда, і у вас є HTTP API, повністю сумісний з ендпоінтами /v1/chat/completions та /v1/completions OpenAI. Будь-який клієнт, написаний під openai SDK, працює без змін; досить підмінити base_url:

# Запуск сервера на одній GPU
vllm serve meta-llama/Llama-3.1-8B-Instruct \
  --host 0.0.0.0 \
  --port 8000 \
  --dtype bfloat16 \
  --max-model-len 8192 \
  --gpu-memory-utilization 0.90 \
  --enable-prefix-caching \
  --api-key sk-internal-secret-token

Клієнтський код на чистому Python, це той самий openai SDK, що ви вже використовуєте для GPT-4:

from openai import OpenAI

client = OpenAI(
    base_url="http://localhost:8000/v1",
    api_key="sk-internal-secret-token",
)

resp = client.chat.completions.create(
    model="meta-llama/Llama-3.1-8B-Instruct",
    messages=[
        {"role": "system", "content": "Ти data engineer."},
        {"role": "user", "content": "Дай схему таблиці orders для e-commerce."},
    ],
    temperature=0.3,
    max_tokens=400,
    stream=True,
)

for chunk in resp:
    delta = chunk.choices[0].delta.content or ""
    print(delta, end="", flush=True)

За кадром vLLM запускає FastAPI-сервер на uvicorn, який приймає HTTP-запити, ставить їх у асинхронну чергу, і LLM-движок безперервно вибирає з неї запити для додавання до поточного батча. Streaming через SSE підтримується нативно: клієнт бачить перший токен через 50–150 мс замість того, щоб чекати кілька секунд на повну відповідь. У моєму досвіді саме streaming є найбільшим UX-стрибком при міграції з прихованого OpenAI API на self-hosted: ви контролюєте мережеву топологію та маєте суттєво нижчий p99 TTFT.

PagedAttention, continuous batching і prefix caching

Усі три механізми вмикаються переважно прапорцями і дають дуже різний ефект залежно від типу навантаження. Розберімося, коли який має значення.

Continuous batching

Увімкнений за замовчуванням, відключити його окремо не можна, це і є серце vLLM. На відміну від динамічного батчингу в TorchServe (де ви налаштовуєте batch_size і max_batch_delay), у vLLM батч переоцінюється на кожному кроці декодера. Параметр, який реально на нього впливає, це --max-num-seqs (типово 256). Якщо ваші запити короткі (chat), збільшуйте до 512–1024. Якщо вони довгі (RAG з 32k контекстом), зменшуйте до 32–64, інакше KV-cache закінчиться і запити почнуть preempt-итись.

Prefix caching

Прапорець --enable-prefix-caching вмикає кешування KV-блоків системного промпта та шаблонної частини. Якщо у вас 200 однакових системних промптів на 1500 токенів (типово для RAG або агентів), то vLLM рахує attention для них рівно один раз. Я бачив 3–4× прискорення TTFT після увімкнення префікс-кешу на проді з агентами, які мали ідентичний 2k-токенний system prompt.

Speculative decoding

З версії 0.6 у vLLM є нативна підтримка spec decoding через draft-моделі (--speculative-model) або n-gram-проспекцію. На моделях типу Llama-3.1-70B з 1B-draft-моделлю реалістично отримати 1.5–2× прискорення на single-stream latency, але throughput при високому навантаженні може навіть впасти. Це trade-off, який треба заміряти на вашому трафіку.

Tensor parallelism і робота з кількома GPU

Llama-3.1-70B у bfloat16 займає 140 GB лише на вагах, тож в одну A100 80GB вона не влізе. Рішення це tensor parallelism: розрізаємо матриці шарів навпіл (або на 4/8 частин) між GPU і виконуємо all-reduce після кожного шару. У vLLM це одна опція:

vllm serve meta-llama/Llama-3.1-70B-Instruct \
  --tensor-parallel-size 4 \
  --dtype bfloat16 \
  --max-model-len 16384 \
  --enable-prefix-caching

Важливо: --tensor-parallel-size має бути дільником кількості heads у моделі (для Llama-3.1 це 64, тож 1, 2, 4 або 8 валідні). vLLM піднімає multiprocessing-воркери (один на GPU) і використовує NCCL для комунікації. Якщо у вас GPU без NVLink (наприклад PCIe-карти у дешевших хмарах), all-reduce стає вузьким місцем; на L40S без NVLink я бачив падіння throughput на 30–40% порівняно з H100 SXM.

Для дуже великих моделей (понад 200B параметрів, як Llama-3.1-405B або DeepSeek-V3) одного tensor parallelism замало, додається pipeline parallelism через --pipeline-parallel-size. У моїй практиці комбінація TP=8, PP=2 на 16 H100, це робоча конфігурація для 405B на bfloat16. Альтернатива, це FP8-квантизація, до якої перейдемо нижче.

Квантизація: AWQ, GPTQ, FP8 і коли що обирати

Якщо вам не потрібні всі 16 біт точності (а у 99% задач не потрібні), квантизація вдвічі або вчетверо зменшує VRAM-витрати й часто прискорює інференс. vLLM підтримує всі поширені формати; вибір залежить від моделі та GPU.

ФорматБіт на вагуЯкість vs FP16ПрискоренняКоли обирати
FP16 / BF1616baseline1.0×Достатньо VRAM, потрібна максимальна точність
FP8 (W8A8)8−0.1 до −0.5%1.5–1.8× на H100H100/H200, продакшн, баланс якість/швидкість
AWQ (W4A16)4 (ваги)−0.5 до −1.5%1.2–1.5×Обмежений VRAM, A100/L40S
GPTQ (W4A16)4 (ваги)−1 до −2%1.1–1.4×Старі моделі, для яких немає AWQ-варіанту
INT8 (W8A8)8−1 до −2%1.3–1.5×GPU без FP8 (A100, V100)

На практиці у 2026 році я майже завжди обираю FP8, якщо стоять H100: підтримка нативна в hopper-архітектурі, втрати точності зазвичай у межах шуму benchmark-ів. Для оренди дешевшого compute на L40S/A100, це AWQ. GPTQ використовую тільки для legacy-моделей без AWQ-варіанту на Hugging Face Hub. Запуск AWQ-моделі тривіальний:

vllm serve casperhansen/llama-3.1-70b-instruct-awq \
  --tensor-parallel-size 2 \
  --quantization awq \
  --max-model-len 8192

Як vLLM порівнюється з TGI, TensorRT-LLM і Ollama

Питання «що використовувати?» залежить від того, де ви на спектрі від «один розробник з gaming-GPU» до «продакшн на 1000 RPS». Я працював зі всіма чотирма і маю чіткі шаблони використання. Зокрема, схожі компроміси я описав у статті про XGBoost, LightGBM і CatBoost: там теж немає універсального переможця, тільки правильний інструмент під задачу.

vLLM vs TGI (Hugging Face Text Generation Inference)

TGI має кращу out-of-the-box інтеграцію з HF Hub і офіційний Docker-образ від Hugging Face. Throughput близький до vLLM, але vLLM швидше отримує підтримку нових моделей (DeepSeek, Qwen-3, Llama-4 типово з'являються у vLLM на 1–2 тижні раніше). Якщо ваш стек, це інші продукти HF (Inference Endpoints, Spaces), TGI логічніший. Інакше vLLM.

vLLM vs TensorRT-LLM

TensorRT-LLM від NVIDIA дає на 10–25% вищий throughput на H100 та помітно нижчий p99 latency завдяки оффлайн-компіляції engine. Але ціна, це багатоденне компілювання engines під кожну конфігурацію (модель, batch size, max-seq-len), нестабільність API між версіями TRT і фактична прив'язка до NVIDIA-стеку. На моєму останньому проєкті ми витратили 6 тижнів на TRT-LLM міграцію, виграли 18% throughput і втратили двох інженерів через супровід. Для більшості команд, не варто.

vLLM vs Ollama

Ollama, це чудовий інструмент для локальної розробки і демо. У продакшн його ставити не треба: серіальний інференс, відсутність continuous batching, обмежений моніторинг. Якщо ви бачите Ollama в архітектурній діаграмі продакшн-сервісу, це червоний прапор.

Що моніторити у vLLM-продакшні

Стандартні Kubernetes-метрики (CPU, RAM) для LLM-сервісу майже марні. GPU на 100%, це норма; RAM рідко є вузьким місцем. Реальні SLO для LLM сервісу формулюються через інші метрики, і саме на них треба мати алерти. Це аналогічно тому, як для класичних ML-моделей моніторять не системні ресурси, а drift і latency. Я писав про це в контексті сучасних scikit-learn ML-пайплайнів.

vLLM з коробки експортує Prometheus-метрики на ендпоінті /metrics. Найважливіші:

  • vllm:time_to_first_token_seconds, це TTFT, найкритичніша UX-метрика для чат-продуктів. SLO зазвичай p99 < 500 мс.
  • vllm:time_per_output_token_seconds, це ITL (inter-token latency). Визначає сприйману швидкість генерації; SLO p99 < 50 мс комфортно для чату.
  • vllm:gpu_cache_usage_perc, заповненість KV-cache. Якщо постійно >90%, у вас preemption, треба зменшити max-num-seqs або додати реплік.
  • vllm:num_requests_waiting, глибина черги. Стабільно >0 означає, що ви недо-провіжнені.
  • vllm:num_preemptions_total, лічильник вивантажень запитів через брак пам'яті. Не повинно зростати лінійно у часі.

Окремо я завжди логую довжини промптів і відповідей у percentile-форматі: це швидко покаже, чи зміна трафіку (наприклад, новий клієнт почав слати 30k-токенні документи) ламає вашу capacity-модель.

Типові помилки і як їх уникнути

За два роки експлуатації vLLM у проді зібралася стабільна колекція граблів. Найчастіші:

  1. OOM на першому ж сплеску трафіку, хоча на навантажувальних тестах все було добре. Причина, gpu_memory_utilization розраховано без буфера на драйвер і ваш sidecar. Лікування: ставте 0.88–0.92, не вище.
  2. «Слабка» якість після квантизації. Часто проблема не в квантизації, а в тому, що ви забули вказати --chat-template або використовуєте дефолтний, який не збігається з training-template моделі. Спершу перевірте якість на FP16 з тим самим темплейтом.
  3. Drift у latency через 2 тижні після релізу. Зазвичай, заповнення префікс-кешу різноманітними системними промптами через мульті-тенантність. Рішення: hash-routing запитів за system_prompt на різні репліки.
  4. Streaming працює локально, але ламається через nginx/ALB. SSE-з'єднання часто закриваються проксі через ідл-таймаут. Поставте proxy_buffering off в nginx або відповідні налаштування на ALB.
  5. Гайди радять --enforce-eager для дебагу, і люди забувають вимкнути на проді. Це повністю вимикає CUDA graphs і ріже throughput на 20–30%. Перевірте свою команду запуску.

Для глибшого занурення у патерни деплою рекомендую почати з офіційної документації vLLM та release notes на GitHub: ядро еволюціонує дуже швидко, і кожен мінорний реліз приносить як нові фічі, так і ребрейкінги внутрішнього API.

Поширені запитання

vLLM кращий за llama.cpp для продакшну?

Так, якщо у вас є GPU. llama.cpp оптимізований під CPU та Apple Silicon з GGUF-квантизаціями і відмінний для edge/локальних кейсів, але на серверних GPU vLLM дає у 5–15 разів вищий throughput через PagedAttention і continuous batching. Для продакшн-сервісу з декількома GPU вибір однозначний на користь vLLM.

Скільки GPU-пам'яті потрібно для запуску Llama-3.1-70B?

У bfloat16, мінімум 140 GB лише на ваги, тобто 2× A100 80GB або 2× H100 80GB з tensor parallelism. З AWQ-квантизацією на 4 біти ваги вміщуються в одну A100 80GB, але реалістично потрібно дві, щоб залишити VRAM під KV-cache при максимальному контексті 16k+. З FP8 на H100 одна H100 80GB цілком справляється з робочим max_model_len 8192.

Чи можна запускати vLLM без GPU?

Є експериментальна CPU-збірка (VLLM_TARGET_DEVICE=cpu), але вона у 50–200 разів повільніша за GPU-варіант і призначена тільки для smoke-тестів CI. Для серйозного інференсу на CPU краще використовувати llama.cpp або OpenVINO. AMD MI300, Intel Gaudi 2/3 та AWS Inferentia 2 підтримуються через окремі бекенди vLLM.

Як vLLM обробляє декілька LoRA-адаптерів одночасно?

Через прапорці --enable-lora та --max-loras N ви можете тримати в пам'яті N різних LoRA-адаптерів і вибирати потрібний у запиті полем model. vLLM батчить запити з різними LoRA в один проход через базову модель, що дешевше, ніж тримати окрему репліку на кожен fine-tune. Реалістично N = 4–16 на одну GPU без помітної просадки latency.

Що краще використовувати з vLLM, Ray Serve чи Kubernetes напряму?

Для команд, які вже на K8s, простіше деплоїти vLLM як звичайний Deployment з GPU-resources і HPA на queue-depth метрику. Ray Serve має сенс, якщо у вас гетерогенний інференс-стек (vLLM + embedding-моделі + reranker в одному пайплайні) і вам потрібен composition. KServe, це компроміс із готовими CRD для канаркових деплоїв і traffic-splitting, який я б порекомендував командам, що хочуть «продакшн з коробки».

Arjun Krishnamurthy
Про Автора Arjun Krishnamurthy

ML engineer focused on getting models out of notebooks and into production. Has war stories about every serving framework.