Руководство по сервингу LoRAX: LoRA-адаптеры в Kubernetes в масштабе
Автоматический перевод Эта статья была автоматически переведена с оригинальной английской версии.
Одна базовая модель и множество LoRA-адаптеров создают нетипичную задачу сервинга. Базовые веса общие, но каждому запросу может требоваться свой набор весов адаптера. Обычный дизайн «один деплой на каждый вариант» расходует GPU-память, когда большинство вариантов простаивает.
LoRAX решает эту проблему long tail. Он загружает адаптеры по требованию, объединяет запросы для разных адаптеров в батчи и перемещает веса адаптеров между памятью GPU и CPU. Привлекательный тезис звучит так: «тысячи файн-тюненных моделей на одной GPU». Но инженерный вопрос уже: с учётом вашего набора активных адаптеров, паттерна входящего трафика и целевой латентности действительно ли планирование обмена даёт достаточный выигрыш, чтобы оправдать ещё один рантайм для сервинга?
Это руководство предназначено для инженеров инференса и платформ, которым нужно обслуживать множество LoRA-вариантов на базе одной модели. Здесь показано, как тестировать документированные API, оценивать размер рабочего набора и превратить стартовый Helm-чарт в явный production-план.
Кратко. Выбирайте LoRAX, когда множество совместимых LoRA-адаптеров используют одну базовую модель, а трафик разреженный или имеет long-tail-распределение. Ёмкость зависит от активного рабочего набора, а не от размера каталога. Зафиксируйте версию рантайма, добавьте аутентификацию и allowlist ID адаптеров, настройте долговременное кэширование артефактов и пробы, выполняйте роутинг с учётом локальности кэша и отдельно измеряйте cold- и warm-пути.
Задача сервинга — это рабочий набор
LoRA фиксирует базовую модель и обучает low-rank-обновления для выбранных матриц весов. Получившийся адаптер обычно намного меньше полного чекпоинта, но его размер всё равно зависит от rank, целевых модулей, числа слоёв и dtype. Фиксированные утверждения вроде «каждый адаптер занимает 100 MB» плохо подходят для расчёта ёмкости.
В сервинге нужно различать три величины:
- Размер каталога: все адаптеры, которые платформа может разрешить из хранилища
- Активный рабочий набор: адаптеры, получавшие запросы в течение окна удержания кэша
- Набор одновременной активности: адаптеры, представленные в батчах в один и тот же момент
В каталоге могут находиться тысячи адаптеров, даже если в VRAM нельзя одновременно разместить тысячи. Важно, как часто меняется активный набор, насколько велики эти адаптеры и могут ли запросы к разным адаптерам объединяться в эффективные батчи.
LoRAX сочетает четыре механизма:
- Базовая модель остаётся размещённой в памяти для всех совместимых адаптеров.
- Запрос указывает адаптер, который можно разрешить через Hugging Face, Predibase или файловую систему.
- Планирование обмена адаптерами заранее загружает и выгружает веса между памятью GPU и CPU.
- Heterogeneous continuous batching объединяет запросы, направленные к разным адаптерам.
В проекте LoRAX заявлено, что heterogeneous batching сохраняет throughput и латентность почти постоянными при росте числа одновременно используемых адаптеров в их бенчмарках. Считайте этот результат вендорской гипотезой для вашего workload. Длина промпта, длина ответа, rank, целевые модули, заполненность батчей, churn кэша и поколение GPU могут изменить итог.
Восстановленный график ниже взят из отчёта Predibase о запуске LoRAX за ноябрь 2023 года. Predibase тестировала Llama 2 7B на одном NVIDIA A10G, распределяя запросы между 1 и 128 адаптерами. На графике показано сравнение стоимости LoRAX и других вариантов для 1–32 адаптеров при обработке одного миллиона токенов, равномерно распределённых между адаптерами.

Архивное сравнение проекта, воспроизведённое по отчёту Predibase о LoRAX за 2023 год. Стоимость LoRAX и выделенных деплоев рассчитана по GPU-часам из того эксперимента. На уровне детализации исходника линия GPT-3.5 Turbo использует стоимость файн-тюненной модели за токен.
Плоские оранжевые столбцы следует интерпретировать как результат дизайна того бенчмарка, а не как актуальное обещание по цене. Тест отправлял фиксированный общий объём в один миллион токенов и позволял разным адаптерам совместно использовать батч. В выделенном baseline предполагались отдельные хостируемые ресурсы для каждой модели, поэтому стоимость росла вместе с числом моделей.
Predibase не раскрыла следующие параметры бенчмарка:
- соотношение токенов промпта и ответа
- длины запросов
- размер батча
- rank адаптеров
- стоимость GPU-часа
- точную модель утилизации выделенного деплоя
- точные исторические цены GPT-3.5 Turbo на входные и выходные токены
Поэтому отчёт содержит недостаточно данных для независимого воспроизведения показанных значений в долларах.
График также не учитывает разреженный трафик, холодные скачивания, текущие цены облаков, более новые GPU, другие базовые модели или адаптеры с иными rank. Он подтверждает необходимость воспроизвести workload. Но заменить такое воспроизведение не может.
Когда LoRAX выглядит подходящим вариантом
LoRAX стоит включить в бенчмарк, если выполняются все следующие условия:
- адаптеры обучались для одной и той же поддерживаемой базовой модели и совместимого с ней токенизатора
- трафик распределён между множеством адаптеров и имеет заметный long tail
- загрузка холодного адаптера по требованию предпочтительнее резервирования отдельного деплоя
- роутинг по tenant или задаче уже существует на границе приложения
- команда готова эксплуатировать специализированный рантайм инференса и управлять его поведением кэша
Типичные случаи — ассистенты для отдельных tenant, множество доменных вариантов и онлайн-эксперименты, использующие общий базовый чекпоинт.
LoRAX хуже подходит, когда основная часть трафика приходится на несколько адаптеров или модели не используют общую базу. Это также слабый вариант, если жёсткие требования по латентности не допускают холодных загрузок либо платформа не может безопасно контролировать, какие артефакты сервер загружает. В таких случаях стандартный деплой vLLM или TGI с фиксированным набором адаптеров может оказаться проще.
Не используйте Kubernetes только потому, что каталог большой. Сначала докажите совместимость рантайма и адаптеров на одной GPU.
Локально попробуйте одну базовую модель и один адаптер
В README LoRAX рекомендуется готовый контейнер. В реальном окружении используйте неизменяемый digest образа. main приведён здесь только потому, что это документированный тег для быстрого старта в репозитории. Эти примеры иллюстративны и не проверялись локально в этом checkout. Сверьте их с версиями образа и зависимостей, которые собираетесь деплоить.
mkdir -p data
docker run --rm --gpus all --shm-size 1g \
-p 8080:80 \
-v "$PWD/data:/data" \
ghcr.io/predibase/lorax:main \
--model-id mistralai/Mistral-7B-Instruct-v0.1
Минимальные документированные требования — Linux, Docker, NVIDIA GPU поколения Ampere или новее и драйверы, совместимые с CUDA 11.8. Для лицензий моделей и gated-репозиториев также может понадобиться токен Hugging Face.
Начните с базовой модели:
curl http://127.0.0.1:8080/generate \
-H 'Content-Type: application/json' \
-d '{
"inputs": "[INST] Give one reason to measure cold-adapter latency. [/INST]",
"parameters": {"max_new_tokens": 64}
}'
Затем отправьте совместимый адаптер:
curl http://127.0.0.1:8080/generate \
-H 'Content-Type: application/json' \
-d '{
"inputs": "[INST] Solve: Natalia sold 48 clips in April and half as many in May. What is the total? [/INST]",
"parameters": {
"max_new_tokens": 64,
"adapter_id": "vineetsharma/qlora-adapter-Mistral-7B-Instruct-v0.1-gsm8k"
}
}'
Первый запрос может скачать и загрузить адаптер. Последующие запросы смогут использовать закэшированные артефакты и резидентные веса. Зафиксируйте оба пути. Один warm-запрос мало что говорит о поведении long-tail.
Используйте OpenAI-совместимый клиент
LoRAX предоставляет OpenAI-совместимый chat endpoint. Поле model идентифицирует адаптер:
from openai import OpenAI
client = OpenAI(
api_key="EMPTY",
base_url="http://127.0.0.1:8080/v1",
)
response = client.chat.completions.create(
model="alignment-handbook/zephyr-7b-dpo-lora",
messages=[
{"role": "user", "content": "Explain cache locality in two sentences."},
],
max_tokens=100,
)
print(response.choices[0].message.content)
По умолчанию сервер не требует API key. Для localhost это удобно, но в конфигурации, доступной из Интернета, небезопасно. Разместите перед ним аутентификацию, авторизацию tenant, квоты и allowlist адаптеров.
Определите gate совместимости
До добавления адаптера в каталог проверьте как минимум:
- заявленную базовую модель и revision
- совместимость токенизатора и chat-template
- поддерживаемые рантаймом rank LoRA и целевые модули
- формат артефакта и формы тензоров
- лицензию, происхождение и digest целостности
- небольшой набор поведенческих и регрессионных тестов
Отклоняйте несовместимые артефакты на этапе регистрации, а не при первом запросе пользователя.
Перед деплоем разберитесь с residency
Базовая модель занимает наибольшую фиксированную долю GPU-памяти. Веса адаптеров, KV cache, workspace для батчей и рантайм-ядра конкурируют за оставшийся объём. RAM CPU может хранить выгруженные адаптеры, а /data используется для хранения скачанных артефактов.
Это не взаимозаменяемые уровни. Артефакт на диске или в Hub нужно прочитать и материализовать, прежде чем он станет адаптером, размещённым в CPU- или GPU-памяти. Измеряйте переходы отдельно:
| Путь | Что включает | Метрика для записи |
|---|---|---|
| GPU hit | Адаптер уже находится в памяти | время в очереди и time to first token |
| CPU hit | Перенос или повторная материализация в GPU | задержка загрузки адаптера и end-to-end latency |
| Artifact hit | Чтение из локального кэша /data | задержка чтения/загрузки и объём кэша |
| Remote miss | Скачивание, валидация и загрузка | время скачивания, ошибки и полная cold latency |
Планирование ёмкости должно воспроизводить реальное распределение популярности адаптеров. Равномерно случайные ID адаптеров создают другую задачу для кэша, чем tenant-workload с Zipf-подобным распределением.
Осторожно деплойте chart из репозитория
В репозитории есть charts/lorax, поэтому воспроизводимая отправная точка — зафиксированная revision репозитория и локальный chart:
git clone https://github.com/predibase/lorax.git
cd lorax
git checkout <reviewed-commit-or-release>
helm dependency update charts/lorax
helm template lorax charts/lorax -f values.production.yaml
helm upgrade --install lorax charts/lorax \
--namespace inference \
--create-namespace \
-f values.production.yaml
По состоянию на 15 июля 2026 года values чарта и шаблон Deployment содержат дефолты, требующие внимания:
- тег образа —
latest /data— этоemptyDir, поэтому при замене pod скачанные артефакты теряются- liveness- и readiness-пробы пусты
- токен Hugging Face задан как literal environment value
- по умолчанию запрашивается одна GPU
Chart — полезный каркас. Но production-политикой он не является.
Начинайте с фактической структуры values чарта
Chart помещает конфигурацию рантайма в deployment, а аргументы launcher представлены списком пар name/value. Минимальный overlay выглядит так:
deployment:
replicas: 1
image:
repository: ghcr.io/predibase/lorax
tag: "<tested-release-tag>"
args:
- name: "--model-id"
value: "mistralai/Mistral-7B-Instruct-v0.1"
- name: "--max-input-length"
value: "2048"
- name: "--max-total-tokens"
value: "3072"
- name: "--max-batch-total-tokens"
value: "8192"
- name: "--max-batch-prefill-tokens"
value: "4096"
resources:
requests:
nvidia.com/gpu: "1"
limits:
nvidia.com/gpu: "1"
env:
- name: HUGGING_FACE_HUB_TOKEN
valueFrom:
secretKeyRef:
name: lorax-hub
key: token
readinessProbe:
httpGet:
path: /health
port: http
periodSeconds: 5
failureThreshold: 600
service:
serviceType: ClusterIP
port: 80
Указанные выше лимиты токенов — примерные стартовые значения, а не рекомендации по sizing. Выводите их из репрезентативных промптов, ожидаемой конкурентности, объёма GPU-памяти и load tests.
Явно закройте пробелы
Текущий шаблон чарта передаёт deployment.env через toYaml, поэтому обычный values overlay может использовать valueFrom.secretKeyRef для credentials Hub, как показано выше. Шаблон жёстко задаёт тома emptyDir, не содержит hook startupProbe и всегда формирует образ как repository:tag. Поэтому одним values-файлом нельзя настроить долговременный /data, startup probe или pinning digest образа; для этого используйте проверенный fork чарта, post-render patch или слой манифестов более высокого уровня. В production-слое добавьте:
- PersistentVolumeClaim или node-local кэш артефактов, смонтированный в
/data - startup probe перед агрессивной liveness-пробой
- policy disruption pod и topology spread для нескольких реплик
- NetworkPolicy, ограничения service account и аутентифицированный gateway
- pinning digest образа и проверки целостности артефактов
Не помещайте токен Hub напрямую в закоммиченный values-файл. Две реплики дают полезную high availability только в том случае, если обе могут загрузить базовую модель. Роутер также должен избегать отправки каждого холодного адаптера на оба pod.
Выполняйте роутинг с учётом локальности кэша
Балансировка round-robin может превратить каждую реплику в холодный кэш. Полезный роутер сопоставляет разрешённый ID адаптера с одной репликой по хэшу или другому стабильному правилу. При этом нужен failover-путь на случай недоступности этой реплики.
Routing key должен формироваться из аутентифицированного состояния приложения, а не из произвольного публичного URL-параметра. Иначе вызывающая сторона сможет провоцировать удалённые скачивания, churn кэшей или проверять наличие приватных имён адаптеров.
LoRAX или vLLM?
В актуальной документации vLLM описаны LoRA-модули, объявляемые при старте, и динамическая загрузка через endpoint или resolver plugin. В ней также предупреждается, что обновление адаптеров во время работы несёт риски безопасности и не должно использоваться в production вне изолированного доверенного окружения.
Полезно сравнивать эти решения не по числам, а с операционной точки зрения:
| Вопрос | LoRAX | vLLM |
|---|---|---|
| Как обнаруживаются long-tail-адаптеры? | ID адаптера может разрешать артефакты из Hugging Face, Predibase или файловой системы по запросу | Статические модули, endpoint динамического управления или resolver plugin |
| Как управляется residency? | Явное планирование обмена адаптерами между GPU и CPU | Настроенные лимиты активных и CPU LoRA плюс поведение resolver |
| Каков интерфейс запросов? | TGI-style /generate, Python-клиент и OpenAI-совместимый chat | OpenAI-совместимый сервинг и нативные Python API |
| Что должно определять выбор? | Cold/warm latency, churn кэша, throughput heterogeneous batching и операционная пригодность | То же воспроизведение workload и те же операционные критерии |
Избегайте правил вроде «LoRAX для 1 000 адаптеров, vLLM для десяти». Размер каталога сам по себе не определяет производительность. Сравнивайте оба решения на одной базе, с теми же адаптерами, промптами, rank, arrival trace и аппаратурой.
Production acceptance test
До расширения каталога выполните replay, включающий:
- Фиксированный hot set для определения warm throughput и латентности.
- Long-tail-распределение для измерения попаданий в CPU и artifact-кэш.
- Всплеск ранее неизвестных, но разрешённых адаптеров.
- Замену pod для измерения восстановления базовой модели и адаптеров.
- Один недоступный или повреждённый адаптер для проверки изоляции и fallback.
- Конкурирующие tenant для проверки аутентификации, квот и меток метрик.
Отслеживайте следующие метрики:
- rate запросов
- время в очереди
- time to first token
- inter-token latency
- общая латентность
- время загрузки адаптера
- класс cache hit
- память GPU
- память CPU
- объём скачанных данных
- ошибки с разбивкой по причинам
Не помещайте ID адаптеров в метки метрик без ограниченного кардиналитета. Сопоставляйте их с контролируемыми измерениями или используйте sampled traces.
До теста определите acceptance thresholds. Задайте максимальный error rate для cold-пути и целевой warm P99. Также задайте целевой cache-hit для наблюдаемого распределения популярности и recovery-time objective после потери pod.
Заключение
LoRAX превращает множество совместимых файн-тюнов из флота копий базовой модели в задачу размещения адаптеров. Это может быть сильным дизайном для long-tail-workload, но само по себе не делает стоимость или латентность постоянными. Результат по-прежнему определяют активный рабочий набор, путь обмена, состав батчей и слой хранения.
Сначала докажите работу этих механизмов на одной GPU. Затем отнеситесь к Kubernetes серьёзно: фиксируйте артефакты, сохраняйте кэш, защищайте credentials, авторизуйте адаптеры, выполняйте роутинг с учётом локальности и измеряйте каждый путь residency. Если LoRAX опережает текущую конфигурацию vLLM при одинаковом replay, решение о деплое будет основано на данных.
Ссылки
- Репозиторий и README LoRAX — поддерживаемые возможности, требования, API и Helm chart
- Отчёт о запуске LoRAX и бенчмарк за 2023 год — источник и условия для восстановленного графика стоимости
- Values чарта LoRAX и шаблон Deployment — текущие дефолты и поведение томов
- LoRA-адаптеры в vLLM — статический и динамический сервинг адаптеров
- Статья о LoRA — метод low-rank adaptation
- Документация Hugging Face PEFT — форматы адаптеров и интеграция с обучением