vLLM Production Stack. Часть 1: Базовые возможности vLLM. DevOps.. DevOps. Kubernetes.. DevOps. Kubernetes. KV-кэш.. DevOps. Kubernetes. KV-кэш. llm.. DevOps. Kubernetes. KV-кэш. llm. vllm.. DevOps. Kubernetes. KV-кэш. llm. vllm. vllm-production-stack.. DevOps. Kubernetes. KV-кэш. llm. vllm. vllm-production-stack. Видеокарты.. DevOps. Kubernetes. KV-кэш. llm. vllm. vllm-production-stack. Видеокарты. Системное администрирование.

Оглавление

Для удобства навигации

Вступление

Меня зовут Андросов Михаил, я работаю в компании Сибинтек-Софт в роли MLOps-инженера и развиваю высокопроизводительный инференс-сервис (LLMaaS). В этой статье я покажу, с чего проще всего начать работу с vLLM и какие базовые возможности стоит включить в первую очередь.

Наверняка вы сталкивались с ситуацией, когда сложно начать работу с новым, пусть и популярным, продуктом. Особенно если он предоставляет богатый функционал.
Речь пойдёт про vLLM и его обвязку — vLLM Production Stack.

Я решил начать цикл статей, в котором буду постепенно разбирать возможности vLLM и компонентов vLLM Production Stack.
Данная статья посвящена базовым возможностям, запуску инференса и параметрам, которые легко и просто конфигурируются.

Тестовое окружение

Все примеры и наблюдения в статье получены на следующем окружении:

  • 2 bare-metal сервера с 8 GPU NVIDIA H200 141 GB каждый.

  • Astra Linux SE 8.

  • Kubernetes 1.30.

  • vLLM 0.17.0.

  • vLLM Production Stack 0.1.10.

Как устроены примеры в статье

В статье будет много примеров с переменными окружения, PVC, дополнительными аргументами и другими настройками. Чтобы не раздувать YAML-манифесты, я не буду повторять одни и те же поля в каждом примере. Поэтому часть опций может присутствовать в полном манифесте, даже если в конкретном фрагменте я её не показываю.

Если какие-то моменты будут конфликтовать друг с другом, я буду на это отдельно указывать.
Также в статье не будет показано, как устанавливать базовые компоненты: драйверы, Kubernetes, Nvidia Device Plugin, Helm и т.п.

Что такое vLLM

vLLM — это open-source движок и сервер для высокопроизводительного инференса LLM. Он поднимает OpenAI-совместимое API и поддерживает не только /v1/completions и /v1/chat/completions, но и Embeddings, Tokenizer, Pooling, Score, Re-rank, а также audio/transcriptions и audio/translations для совместимых моделей. Производительность достигается за счёт эффективной работы с KV-cache, динамического batching’а и других оптимизаций планировщика. Запуск возможен как на GPU, так и на CPU. vLLM подходит как для простого запуска одной модели, так и для production-сценариев.

vLLM Production Stack

vLLM Production Stack — это готовая Kubernetes-обвязка вокруг vLLM. Он поднимает группу vLLM-инстансов и роутер перед ними, а также даёт базу для маршрутизации запросов, наблюдаемости и масштабирования.

Какие модели можно запустить

Один из первых практических вопросов при работе с vLLM — поддерживается ли нужная вам модель. Особенно это актуально для новых релизов и нестандартных архитектур.
Поэтому рекомендую всегда проверять какие модели поддерживаются и просматривать рекомендации по развёртыванию той или иной модели.

Если модель вышла совсем недавно и очень хочется её запустить, пробуйте стартовать с образом nightly. Разработка vLLM идёт хорошими темпами, и сообщество старается быстро добавлять поддержку новых моделей.

Запуск моделей в разных режимах

Начнем с самых практических сценариев: от базового запуска текстовой модели до включения tool calling, мультимодальности и CPU-режима.

Скачивание модели

По умолчанию vLLM пытается скачать модель напрямую с Hugging Face. Если в вашем контуре нет интернета, модель нужно заранее скачать, перенести на сервер и примонтировать в контейнер через PV/PVC.

extraVolumeMounts:
  - mountPath: /models/Qwen3-8B
    name: model
    readOnly: true
extraVolumes:
  - name: model
    persistentVolumeClaim:
      claimName: pvc-qwen3-8b

Запуск Qwen3-8B

Для первого демонстрационного запуска возьму Qwen3-8B. — это удобная модель, чтобы показать базовый сценарий использования – локальное монтирование, запуск vLLM и первый запрос через OpenAI-совместимый API.

servingEngineSpec:
  enableEngine: true
  modelSpec:
    - name: qwen3-8b
      repository: vllm-openai
      tag: v0.17.0
      env:
        - name: HF_HUB_OFFLINE
          value: "1"
      modelURL: /models/Qwen3-8B
      extraVolumeMounts:
        - mountPath: /models/Qwen3-8B
          name: model
          readOnly: true
      extraVolumes:
        - name: model
          persistentVolumeClaim:
            claimName: pvc-qwen3-8b
      replicaCount: 1
      requestCPU: 8
      requestGPU: 1
      requestMemory: 16Gi
      vllmConfig:
        extraArgs:
          - --served-model-name
          - Qwen/Qwen3-8B
        gpuMemoryUtilization: 0.98

Пояснение по переменной HF_HUB_OFFLINE: при значении "1" попыток обращения к Hugging Face не будет вовсе.

После запуска Helm-чарта vLLM Production Stack, дожидаемся когда модель будет запущена.

Модель запущена

Модель запущена

Любым удобным способом пробросьте порт до Service инференса, например:

kubectl port-forward svc/vllm-production-stack-qwen3-8b-engine-service 9191:9191

Теперь можно выполнить первый запрос и убедиться, что модель отвечает.

curl -N http://localhost:9191/v1/chat/completions 
  -H "Content-Type: application/json" 
  --data-raw '{
    "model": "Qwen/Qwen3-8B",
    "messages": [
      { "role": "user", "content": "Напиши: Привет!" }
    ]
  }'
Ответ от модели

Ответ от модели

Qwen3-8B — это модель с режимом размышления, поэтому в ответе можно наблюдать блок <think></think>.
Все доступные OpenAI-совместимые эндпоинты указаны на официальном сайте.

Включение вызова инструментов (tool-calling)

Для поддержки вызова инструментов недостаточно того, что модель умеет это на уровне обучения. В vLLM нужно дополнительно включить server-side опции: автоматический выбор инструмента и parser, который умеет разбирать формат tool call именно для вашей модели.

Например, для Qwen3.5-35B-A3B это делается так:

    extraArgs:
      ...
      - --enable-auto-tool-choice
      - --tool-call-parser
      - qwen3_coder

Если этого не сделать и пытаться использовать модель в агентских сценариях, вы получите ошибку:

Ошибка сообщает, каких опций не хватает

Ошибка сообщает, каких опций не хватает

Подробнее можно ознакомиться в официальной документации vLLM по Tool Calling.

Режимы thinking и non-thinking

Для рассуждающих моделей поведение можно задавать не только в клиентском запросе, но и на уровне сервера через --default-chat-template-kwargs. Если клиент передаст chat_template_kwargs, то значения из запроса переопределят значения, заданные при старте сервера.

    extraArgs:
      ...
      - --default-chat-template-kwargs
      - '{"enable_thinking": false}'

Если клиент передаст chat_template_kwargs.enable_thinking=true, размышление включится именно для этого запроса. Значение, заданное при старте vLLM, будет переопределено.

Важно помнить, что эта опция работает не для всех моделей одинаково. Например, для DeepSeek параметр будет выглядеть иначе:

--default-chat-template-kwargs '{"thinking": false}'

Запуск мультимодальных моделей

Для запуска мультимодальных моделей необходимо собирать собственный образ vLLM поверх базового vllm/vllm-openai. Причина в том, что официальные образы не включают часть необязательных зависимостей.

Добавить нужные зависимости довольно просто:

FROM vllm/vllm-openai:v0.17.0
RUN uv pip install --system "vllm==0.17.0" && 
    uv pip install --system "vllm==0.17.0"

Для мультимодальных моделей нужно отдельно задавать tensorParallelSize, а при необходимости — увеличивать shmSize, потому что стандартных 20 GiB может быть недостаточно. Это требуется, чтобы в /dev/shm хватало места под mm_processor_cache — кэш промежуточной обработки изображений и других мультимодальных данных. Без этого кэша модель либо работает нестабильно, либо вообще падает: если tensorParallelSize не указан, доступной разделяемой памяти обычно не хватает, и экземпляр модели упадет уже на первом запросе.

      shmSize: "32Gi"
      vllmConfig:
        ...
        extraArgs:
          ...
        tensorParallelSize: 1

В официальном репозитории vLLM есть Python-скрипт, который показывает, как выполнять запросы к мультимодальным моделям.

Также для таких моделей часто приходится задавать дополнительные аргументы при старте vLLM. Например, для Qwen3-Omni-30B можно использовать параметр limit_mm_per_prompt, который задаёт максимальное количество данных каждого типа, разрешённое в одном сообщении.

Что бы учитывать эти и другие нюансы, всегда проверяйте vLLM Recipes и репозитории моделей.

Запуск моделей на CPU

Не всем моделям нужна GPU. Для embedding-моделей и небольших вспомогательных сервисов запуск на CPU может быть проще и дешевле в эксплуатации. Для этого у vLLM есть отдельный CPU backend и CPU-образы.

Проведу демонстрацию запуска модели Qwen3-Embedding-0.6B на CPU:

- name: qwen3-embedding-06b
  env:
    - name: VLLM_CPU_KVCACHE_SPACE
      value: "48"
  ...
  vllmConfig:
    extraArgs:
      - --served-model-name
      - Qwen3-Embedding-0.6B

Обязательно укажите переменную VLLM_CPU_KVCACHE_SPACE под KV-кэш и берите память с запасом.

В логах можно увидеть, что образ стартует на CPU и выделяет место под KV-кэш.

Хоть и написано про GPU KV cache, в реальности GPU не используется

Хоть и написано про GPU KV cache, в реальности GPU не используется


Стартовые опции

Ниже — стартовые опции vLLM, которые сильнее всего влияют на память, параллелизм и поведение сервера.

Таблица основных параметров

Для удобства сначала соберу ключевые параметры в одну таблицу, а ниже коротко разберу каждый из них.

Параметр

Что делает

Когда особенно важен

enablePrefixCaching

Разрешает переиспользование уже посчитанного KV-кэша для общего префикса

Когда у запросов часто совпадает начало промпта

enableChunkedPrefill

Разбивает длинный prefill на части и позволяет лучше смешивать его с decode

При длинных входах и смешанной нагрузке

maxModelLen

Ограничивает максимальную длину контекста в токенах

Когда нужен длинный контекст или нужно экономить память

dtype

Определяет тип чисел, в котором хранятся веса и выполняется часть вычислений

Когда нужно явно зафиксировать точность

tensorParallelSize

Разбивает модель на несколько GPU

Когда модель не помещается на одну GPU

maxNumSeqs

Ограничивает количество последовательностей, которые vLLM держит в активной пачке

При тюнинге параллелизма и задержек

gpuMemoryUtilization

Ограничивает долю VRAM, которую vLLM может использовать

Почти всегда при запуске модели

dtype

dtype — это тип чисел, в котором vLLM будет хранить веса и считать активации, то есть фактически точность вычислений.

По умолчанию используется auto, который выбирает FP16 или BF16 в зависимости от того, в какой точности модель была сохранена. Обычно я рекомендую этот параметр явно не задавать и оставлять значение по умолчанию.

gpuMemoryUtilization

gpuMemoryUtilization — это доля VRAM, которую vLLM может занять под инференс модели и KV-кэш.

Большее значение означает больше KV-кэша и выше потенциальный параллелизм, но также выше риск OOM. Меньшее значение безопаснее, но может снизить пропускную способность.

Чем меньше модель, тем больше памяти обычно можно отдавать под vLLM:

  • Модели 2B–8B — 0.98

  • Модели 30B–80B — 0.97

  • Модели 80B–140B — 0.96

  • Модели около 700B — 0.90

Это мои личные наблюдения, и в вашей ситуации цифры могут отличаться.

maxModelLen

maxModelLen — это максимальная длина контекста, с которой запускается vLLM. Этот параметр напрямую влияет на потребление памяти под KV-кэш.

В репозитории модели в файле config.json есть поле max_position_embeddings — это максимальная длина контекста в токенах, которую поддерживает модель.

Для модели Qwen3-8B максимальное значение maxModelLen будет 40960.

Иногда это значение можно расширить, но только если модель поддерживает RoPE Scaling.

RoPE scaling — это приём увеличения длины контекста на инференсе за счёт масштабирования rotary positional embeddings, то есть изменения частот позиционного кодирования без переобучения модели. В vLLM это включается параметром --rope-scaling.

Проверяйте репозиторий модели на Hugging Face: там обычно указывают, можно ли включать --rope-scaling.

Покажу, как это работает на примере Qwen3-8B:

- name: qwen3-8b
  env:
    - name: VLLM_ALLOW_LONG_MAX_MODEL_LEN
      value: "1"
  ...
  vllmConfig:
    extraArgs:
      - --served-model-name
      - Qwen/Qwen3-8B
      - --hf-overrides
      - '{"rope_scaling":{"rope_type":"yarn","factor":4.0,"original_max_position_embeddings":32768}}'
    maxModelLen: 131072

По логам можно увидеть, что контекст увеличен до 131072 токенов.

Доступен контекст запроса до 131072

Доступен контекст запроса до 131072

Если не указать правильный maxModelLen, vLLM завершится с ошибкой.

И помните: увеличение контекста через scaling не гарантирует сохранение качества на исходном уровне. Чем дальше вы уходите от штатного контекстного окна модели, тем важнее отдельно проверять качество на своих сценариях.

enablePrefixCaching

enablePrefixCaching — полезен на повторяющихся запросах, где у многих обращений совпадает начало промпта: system prompt, шаблон RAG, история диалога или общий длинный контекст. В этом случае движок переиспользует уже посчитанный KV-кэш для общего префикса, снижая стоимость prefill-фазы, уменьшая TTFT и повышая общую пропускную способность.

В некоторых режимах работы этот параметр имеет смысл отключать.

enableChunkedPrefill

enableChunkedPrefill — полезен при длинных входных запросах. Вместо того чтобы прогонять весь prefill одним большим куском, движок режет его на части и может перемешивать их с decode-запросами. Это помогает лучше балансировать нагрузку между prefill и decode.

Рекомендуется включать его всегда, когда это возможно.

Когда эти оптимизации не так важны

Эти оптимизации важны не всегда. Например, для Embedding и Reranker моделей отключенные enablePrefixCaching и enableChunkedPrefill обычно не являются проблемой: эти оптимизации ориентированы в первую очередь на генеративные сценарии.

maxNumSeqs

maxNumSeqs — это максимум последовательностей, которые vLLM может обработать за одну итерацию.

Проще говоря, это то количество клиентских запросов, которое движок попытается держать в активной пачке на одном шаге. Большее значение означает больше параллелизма, но и более сильное давление на память и кэш, а также рост хвостовой задержки.

Здесь нет универсальных рекомендаций. Сначала нужно подобрать модель, понять её сценарий работы — обработка документов, кодогенерация и т.д. — и уже потом подбирать значение с помощью нагрузочных тестов.

–max-num-batched-tokens

–max-num-batched-tokens — это максимум токенов суммарно по всем активным запросам, которые vLLM может обработать за одну итерацию.

Это то, насколько большую пачку токенов можно собрать за один шаг. Если увеличить значение, vLLM сможет сильнее объединять запросы в батч, обычно лучше загружая GPU, но требования к памяти также вырастут.

Здесь тоже нет универсальных рекомендаций. Без тестов идеальное значение не подобрать. На старте можно оставить дефолт, а уже затем пытаться выжимать максимум из конкретного сценария.

Что может помочь подобрать эти значения

Для подбора этих параметров в репозитории vLLM есть отдельный скрипт auto-tune, который позволяет подобрать эти значения под конкретную модель.

Сначала скрипт подбирает устойчивое gpu_memory_utilization, снижая его при OOM, затем перебирает комбинации maxNumSeqs × max-num-batched-tokens и выбирает лучшую по пропускной способности с учётом лимита по задержке, если он задан.

Единственный недостаток — нужно подготовить образ под этот скрипт, потому что он рассчитан на запуск из репозитория.

Запуск модели с квантизацией FP8

Многие модели выкладывают с более компактными весами FP8. Если у вас видеокарты NVIDIA серии Hopper и новее, рекомендую в первую очередь смотреть именно на такие варианты: это увеличивает бюджет KV-кэша и часто даёт рост производительности инференса.

Проведу эксперимент и запущу по одному экземпляру модели Qwen3.5-35B-A3B: с обычными весами BF16 и с квантованными весами FP8.

Даже по размеру файлов видно разницу: BF16-версия весит 71.9 GB, а FP8 — 37.5 GB.

При запуске я также укажу опцию --kv-cache-dtype fp8, чтобы KV-кэш тоже был в FP8.

# Модель в FP8 + KV-кэш в FP8
- name: qwen35-35b-a3b
  modelURL: /models/Qwen3.5-35B-A3B-FP8
  replicaCount: 1
  repository: vllm-openai
  tag: v0.17.0
  requestCPU: 8
  requestGPU: 1
  requestMemory: 32Gi
  vllmConfig:
    gpuMemoryUtilization: 0.97
    extraArgs:
      - --kv-cache-dtype
      - fp8

# Модель в BF16 + KV-кэш по умолчанию
- name: qwen35-35b-a3b-bf16
  modelURL: /models/Qwen3.5-35B-A3B
  replicaCount: 1
  repository: vllm-openai
  requestCPU: 8
  requestGPU: 1
  requestMemory: 32Gi
  tag: v0.17.0
  vllmConfig:
    gpuMemoryUtilization: 0.97

Доступность KV-кэша при запуске модели в BF16:

Доступность кэша BF16

Доступность кэша BF16

Доступность KV-кэша при запуске модели в FP8:

Доступность кэша FP8

Доступность кэша FP8

В моём тесте доступный бюджет KV-кэша вырос почти в 3 раза за счёт более компактных весов и FP8 KV-кэша.
Кроме этого, производительность инференса у FP8-модели оказалась выше, чем у BF16.

Вот тест, сделанный с помощью GuideLLM с одинаковыми параметрами (synchronous, prompt_tokens=256, output_tokens=128):

Тест модели в BF16

GuideLLM BF16

GuideLLM BF16

Тест модели в FP8

GuideLLM FP8

GuideLLM FP8

Видно, что производительность немного выше.

Запуск большой модели на нескольких GPU

Может возникнуть ситуация, когда требуется запустить большую модель, а она либо не помещается на одну GPU, либо помещается, но оставляет слишком мало места под KV-кэш.

В таком случае поможет режим Tensor Parallelism. В нём слои модели разбиваются на части, которые обрабатываются несколькими GPU одновременно. Это позволяет запускать модели, превышающие объём памяти одного GPU, и получать более высокую пропускную способность.

Для начала покажу на примере модели Qwen3.5-122B-A10B-FP8, что она формально влезает в одну GPU, но почти без запаса:

- name: qwen35-122b-fp8
  ...
  vllmConfig:
    extraArgs:
      - --served-model-name
      - Qwen3.5-122B-A10B-FP8
      - --kv-cache-dtype
      - fp8
    tensorParallelSize: 1

После старта vLLM видно, что под KV-кэш доступно всего около 7 GB памяти.

7.11 GB KV-кэша — это крайне мало

7.11 GB KV-кэша — это крайне мало

Результаты тестирования с помощью GuideLLM: 1 минута на каждый этап, промпты из датасета cnn_dailymail.

1 экземпляр - 1 GPU

1 экземпляр – 1 GPU

Также я провёл такое же тестирование на двух экземплярах по одной GPU каждый:

2 экземпляра - 2 GPU

2 экземпляра – 2 GPU

А теперь запущу один экземпляр, но в режиме TP=2 на двух GPU:

- name: qwen35-122b-fp8
  ...
  vllmConfig:
    extraArgs:
      - --served-model-name
      - Qwen3.5-122B-A10B-FP8
    tensorParallelSize: 2

По логам видно, что теперь запас KV-cache стал значительно больше.

Запас KV-кэша серьёзно вырос

Запас KV-кэша серьёзно вырос

И результаты тестирования в таком режиме показывают заметный рост производительности, даже по сравнению с двумя отдельными экземплярами:

Производительность выросла благодаря KV-кэшу

Производительность выросла благодаря KV-кэшу

Важное уточнение: для Tensor Parallel нужны две свободные GPU на одном узле.

Разделить модель на две GPU, находящиеся на разных узлах, тоже можно, но это уже тема для следующей части.

Запуск большой модели на одной GPU

Что делать, если второго GPU нет, а памяти немного не хватает? Может помочь опция --cpu-offload-gb, которая позволяет расширить VRAM за счёт RAM. Часть весов будет размещена в VRAM, часть — в RAM.

- name: qwen35-122b-fp8
  ...
  vllmConfig:
    extraArgs:
      ...
      - --cpu-offload-gb
      - "24"

Часть весов модели теперь в RAM, благодаря этому KV-кэш стал больше

KV-кэш стал больше, потому что часть весов выгружена в RAM

KV-кэш стал больше, потому что часть весов выгружена в RAM

Важно понимать, что такой режим работы будет медленнее, чем если бы вся модель помещалась на GPU целиком.

Запуск модели с KV-кэш offloading

Одна из важных возможностей экосистемы vLLM — выгрузка и переиспользование KV-кэша через внешние коннекторы. В production-сценариях это особенно полезно на повторяющихся или частично повторяющихся входах: можно не пересчитывать уже известный префикс заново, а доставать KV-кэш из внешнего слоя кэширования.

На практике это в первую очередь помогает снижать стоимость prefill-фазы и улучшать TTFT там, где есть повторное использование контекста.

В экосистеме vLLM и связанных сценариях встречаются разные механизмы передачи KV-кэша: LMCache, NIXL, SharedStorage, а также P2P/NCCL-сценарии для межпроцессного обмена. В этой статье я рассматриваю только самый простой вариант, который уже встроен в vLLM Production Stack, — LMCache.

Включается это довольно просто:

- name: qwen3-8b
  env:
    - name: LMCACHE_TRACK_USAGE
      value: "false"
  lmcacheConfig:
    cpuOffloadingBufferSize: "512"
    enabled: true
  ...
  vllmConfig:
    extraArgs:
      - --served-model-name
      - Qwen/Qwen3-8B

Переменная LMCACHE_TRACK_USAGE управляет отправкой статистики использования KV-кэша на внешний сервер.

Внутри логов vLLM можно увидеть, что LMCache успешно запустился.

LMCache запустился

LMCache запустился

Если погреть кэш, выполнив множество запросов, можно увидеть попадание в KV-кэш.

Вместо повторного вычисления KV-кэш копируется из памяти

Вместо повторного вычисления KV-кэш копируется из памяти

У LMCache есть и более тонкая настройка, но её я рассмотрю в следующей части статьи.

Важно помнить, что выгрузка KV-кэша стабильно работает, прежде всего, на transformer-моделях. Для гибридных моделей поддержка может быть ограниченной (на версию vLLM 0.17.0)

Speculative Decoding

Speculative Decoding — это техника ускорения генерации, при которой рядом с основной моделью работает более быстрый «черновик»: он заранее предлагает несколько следующих токенов, а большая модель затем проверяет их за один проход. Если догадки оказываются верными, часть токенов принимается сразу пачкой, за счёт чего ускоряется decode.

В vLLM это особенно полезно там, где узкое место — именно decode, а не prefill. Speculative decoding не всегда означает запуск второй модели рядом. Иногда это встроенный в архитектуру способ предсказывать несколько токенов вперёд (MTP).

Всегда внимательно проверяйте vLLM Recipes или README конкретной модели.

Для Qwen3.5 может быть как минимум два варианта настройки. Например, способ из README репозитория модели:

- name: qwen35-35b-a3b-fp8
  ...
  vllmConfig:
    extraArgs:
      ...
      - --speculative-config.method
      - qwen3_next_mtp
      - --speculative-config.num_speculative_tokens
      - "2"

Или способ из рекомендаций vLLM:

- name: qwen35-35b-a3b-fp8
  ...
  vllmConfig:
    extraArgs:
      ...
      - --speculative-config.method
      - mtp
      - --speculative-config.num_speculative_tokens
      - "1"

Оба способа рабочие. Всегда пробуйте доступные варианты, подбирайте их под свои сценарии и проверяйте нагрузочными тестами.

В логах модели можно увидеть метрики Speculative Decoding, в том числе сколько токенов в итоге было принято.

Метрики Speculative Decoding

Метрики Speculative Decoding


Особенности запуска больших моделей

При старте больших моделей увеличьте startupProbe. По умолчанию пробы ждут около 10 минут, после чего pod будет перезапущен.

Для огромных моделей этого точно не хватит. Например, для GLM-5 с 744B параметров старт может занимать заметно больше времени.

servingEngineSpec:
  startupProbe:
    failureThreshold: 180
    initialDelaySeconds: 30
    periodSeconds: 10
  strategy:
    type: Recreate

Также имеет смысл указать strategy.type=Recreate, чтобы при перезапусках модели старая копия сначала корректно завершала работу, а уже потом поднималась новая. Если свободных GPU много, это ограничение можно не использовать.

Кэш компиляции при старте

При старте vLLM выполняет компиляцию и прогрев ряда внутренних компонентов, из-за чего запуск больших моделей может занимать десятки минут.

На примере GLM-5:

23 минуты от старта до готовности к запросам

23 минуты от старта до готовности к запросам

В случае GLM-5 самые долгие этапы были такими:

  1. Чтение весов модели с диска в память GPU — около 1.5 минут.

  2. JIT-компиляция CUDA-ядер и оптимизация вычислительного графа под конкретную архитектуру GPU — около 5 минут.

  3. DeepGEMM Warmup — около 9.5 минут: прогрев GEMM-операций с автотюнингом и кэшированием скомпилированных ядер.

Чтобы ускорить запуск, нужно закэшировать результаты компиляции. Для этого достаточно создать PVC и смонтировать его в каталог /root/.cache/vllm:

modelSpec:
  - annotations:
      model: glm-5
    extraVolumeMounts:
      - mountPath: /root/.cache/vllm
        name: vllm-cache
    extraVolumes:
      - name: vllm-cache
        persistentVolumeClaim:
          claimName: pvc-glm-5-cache
    ...

После первого запуска, записи кэшей и повторного перезапуска экземпляра можно увидеть серьёзный прирост по скорости: теперь от старта до готовности к работе проходит около 5 минут.

5 минут от старта до готовности к запросам

5 минут от старта до готовности к запросам

Полезные ссылки (справочник)

Ниже собрал ссылки, которые удобно держать под рукой при работе с vLLM и Production Stack.

Примеры запуска

Примеры можно посмотреть в репозитории Production Stack:

https://github.com/vllm-project/production-stack/tree/main/tutorials/assets

Часть примеров придётся адаптировать под конкретную версию стека и вашу инфраструктуру.

OpenAI-compatible server и Supported APIs

Если нужен список поддерживаемых OpenAI-совместимых эндпоинтов и примеры запросов:

https://docs.vllm.ai/en/latest/serving/openai_compatible_server/

Поддерживаемые модели

Проверить, поддерживается ли нужная модель, можно здесь:

https://docs.vllm.ai/en/latest/models/supported_models/

vLLM Recipes

Практические рекомендации по запуску конкретных моделей и сценариев:

https://docs.vllm.ai/projects/recipes/

Аргументы vLLM

Про все аргументы, которые можно указать при старте vLLM, можно почитать в официальной документации:

https://docs.vllm.ai/en/stable/configuration/engine_args/

Optimization and tuning

Отдельный раздел документации по оптимизациям и настройке производительности:

https://docs.vllm.ai/en/stable/configuration/optimization/

Tool Calling

Документация по поддержке вызова инструментов:

https://docs.vllm.ai/en/latest/features/tool_calling/

Reasoning outputs

Документация по reasoning-моделям, thinking/non-thinking-режимам и chat_template_kwargs:

https://docs.vllm.ai/en/stable/features/reasoning_outputs/

Speculative Decoding

Документация по speculative decoding:

https://docs.vllm.ai/en/latest/features/speculative_decoding/

Quantized KV Cache и FP8

Документация по квантованному KV-кэшу:

https://docs.vllm.ai/en/latest/features/quantization/quantized_kvcache/

Запуск vLLM на CPU

Документация по CPU-режиму:

https://docs.vllm.ai/en/stable/getting_started/installation/cpu/

Production Stack: KV cache offloading с LMCache

Отдельный tutorial по offloading KV-кэша в Production Stack:

https://github.com/vllm-project/production-stack/blob/main/tutorials/05-offload-kv-cache.md

Примеры запросов

В репозитории есть коллекция примеров запросов, оформленных как Python-скрипты:

https://github.com/vllm-project/vllm/tree/main/examples/online_serving

Где отслеживать новый функционал

Если вы ждёте какой-то новый функционал, отслеживать изменения в nightly-сборках можно через отдельный репозиторий vLLM Daily:

https://github.com/vllm-project/vllm-daily/tree/main

Итоги

В этой части я разобрал базовые возможности самого vLLM, с которых проще всего начать практическую работу: первый запуск модели, ключевые стартовые параметры, tool calling, thinking/non-thinking-режимы, мультимодальные и CPU-сценарии, а также несколько важных оптимизаций — FP8, Tensor Parallelism, KV-cache offloading и Speculative decoding.

Если кратко, то уже на уровне самого vLLM можно получить довольно гибкий и производительный inference-сервер, даже без погружения в более сложные компоненты production-стека.

Во второй части я перейду к возможностям уже самого vLLM Production Stack: разберу LMStack Router и его режимы работы, LMStack Cache Server, использование Ray и другие компоненты, которые становятся важны, когда нужно строить более полноценную production-платформу вокруг vLLM.

Автор: Bambarambambum

Источник

Rambler's Top100