- BrainTools - https://www.braintools.ru -
Продолжаем разбираться с тем, как можно эффективно работать с большими языковыми моделями, используя доступное оборудование.
В этой части мы перейдём к организации распределённого инференса с помощью vLLM и обеспечим доступ к нему через Ray Serve. А ещё выясним, как запустить модель Gemma 3 в Ray-кластере и как проверить работу нашего OpenAI-совместимого эндпойнта с JWT-аутентификацией.

Если вы ещё не читали первую часть, стоит начать с неё [1]. Там описано «железо» моей домашней лаборатории и процесс подготовки всего необходимого для развёртывания распределённого инференса с Ray Serve и vLLM.
Начнём с ключевых концепций шардирования, параллелизации вычислений и принципов работы API.
vLLM [2] — это фреймворк, который поддерживает параллельный инференс больших языковых моделей. Он предоставляет:
Нативную поддержку шардирования (tensor parallel): разбивает весовые тензоры модели между GPU в рамках одной ноды, позволяя каждому ускорителю обрабатывать свою часть вычислений одновременно.
Пайплайн-параллелизм (pipeline parallel): разбивает модель на последовательные блоки-слои и распределяет их по разным нодам: первые слои выполняются на GPU ноды А, следующие — на GPU ноды B и так далее. При этом пока нода А обрабатывает следующий запрос, нода B уже догоняет и завершает предыдущий — конвейер остаётся загруженным.
Интеграцию с NVIDIA Collective Communications Library (NCCL), которая обеспечивает сверхбыстрый обмен промежуточными активациями и градиентами между GPU и между нодами (в том числе по RDMA/InfiniBand), автоматически группируя все устройства в единый вычислительный пул.
Стриминговые ответы: отдаёт результат по мере генерации, как в OpenAI ChatCompletion API, чтобы клиенты видели ответ постепенно.
Вместе это даёт нам:
Объединённую видеопамять: все VRAM-карты работают как одно целое, суммируя свои ресурсы.
Постоянную загрузку: тензорные операции на каждой карте идут параллельно, а конвейерные этапы никогда не простаивают.
Горизонтальное масштабирование «из коробки»: независимо от того, 1 или 8 GPU на ноде и каково общее количество нод, vLLM + NCCL автоматически развернут любую большую модель сразу на десятках карт.
В конфиге vLLM задаём:
Tensor parallel size — сколько «кусочков» весов распределять по GPU внутри узла.
Pipeline parallel size — на сколько стадий дробим модель по нодам.
gpu_memory_utilization и cpu_offload_gb — параметры, которые помогают эффективно расходовать видеопамять и часть объёма оперативной памяти [3] хоста.
Так мы балансируем память и производительность под свой кластер.
Ray Serve [4] — это компонент фреймворка Ray, который предоставляет микросервисную архитектуру для инференса:
Позволяет развернуть FastAPI-приложение и автоматически управлять его репликами.
Делает возможной горизонтальную масштабируемость — каждая реплика может быть на отдельном узле, если нужно.
Может эффективно работать с GPU, когда под каждый сервис зарезервирована часть видеокарт.
Мы используем Ray Serve, чтобы предоставить внешний HTTP API, совместимый с OpenAI-протоколами (ChatCompletion). Через этот API клиенты смогут делать запросы к модели, развёрнутой в vLLM.
Внутри узла. Tensor Parallelism дробит модель на куски, загружает их на все локальные GPU и с помощью NCCL синхронизирует параметры.
Между узлами. Pipeline Parallelism превращает несколько серверов с GPU в конвейер: каждый узел обрабатывает свою «стадию» модели, передавая промежуточные данные дальше — так все карты работают без простоев.
Внешний интерфейс. Ray Serve оборачивает всю эту мощь в единый HTTP-сервис по OpenAI-протоколу. Он разбивает входящий запрос на нужные шард-группы, отправляет их на соответствующие GPU и ноды, а затем собирает полученные фрагменты в единый осмысленный ответ.
Дисклеймер: я не Python-разработчик, поэтому скрипты далеко не production-ready и написаны больше для ознакомительных целей.
Скрипт serve.py
Первый скрипт — serve.py [5]. В нём:
инициируется vLLM с заданными параметрами (tensor parallel, pipeline parallel и так далее);
создаётся FastAPI-приложение с эндпойнтами для ChatCompletion — стримингового и обычного;
добавляется JWT-аутентификация для контроля доступа.
Ray Serve оборачивает это приложение и распределяет HTTP-запросы по рабочим процессам.
Скрипт auth.py
Второй скрипт — auth.py [6], который:
работает с JWT — создание и проверка токенов, роль пользователя;
берёт загрузку пользователя (логин, роль, хэш пароля) из переменных окружения (например, USER_LIST);
предоставляет вспомогательные функции для проверки роли (admin/user) и времени жизни токена.
Этот модуль подключается внутри
serve.py, чтобы проверять токены при обращении к эндпойнтам. Его реализация максимально упрощена (то же касается хранения логинов, паролей и ролей) и служит только демонстрационным примером.
Теперь у нас есть два скрипта для дальнейшего использования:
serve.py — основной сервер, где Ray Serve запускает FastAPI-приложение с vLLM под капотом;
auth.py — вспомогательная логика [7] JWT-аутентификации, используемая в серверном скрипте.
Благодаря этим двум модулям мы получаем распределённый инференс, шардирование больших языковых моделей и удобный API для взаимодействия с ними. Теперь нужно собрать скрипты в Docker-образ и развернуть всё в KubeRay.
В этом разделе мы разберёмся, как подготовить Docker-образ, поместить его в Registry и развернуть Ray Cluster с нужными параметрами. Это поможет организовать распределённый инференс, использовать CephFS или другое хранилище, настраивать CPU- и GPU-ресурсы и так далее. В примерах я беру за основу официальный Docker-образ Ray, но вы можете легко заменить его на любой другой совместимый базовый образ.
1. Базовый образ Ray:
Берём официальный образ Ray соответствующей версии, например rayproject/ray:2.44.0-py310-cu124. Он уже содержит нужные компоненты Ray, CUDA-библиотеки и Python 3.10.
2. Добавляем скрипты и пакеты:
Помещаем serve.py, auth.py (упаковывая всё в zip-архив).
Устанавливаем vLLM, httpx, PyJWT и другие зависимости.
3. Сборка:
Пишем Dockerfile (dockerfile.ray [8]), в котором описываем добавление zip-файла и установку pip-зависимостей.
Собираем образ с помощью Make (Makefile [9]) командой:
make package-container
4. Загрузка образа:
Пушим результат в нужный Registry — DockerHub, GitLab Registry и так далее:
docker push gitlab.example.com:5050/it-operations/k8s-config/vllm-0.8.4-ray-2.44.0-py310-cu124-serve:1.1.7
Убеждаемся, что Kubernetes-узлы или кластер имеют доступ к этому Registry. Кстати, в Deckhouse уже есть возможность [10] добавить внутренний registry, а также авторизацию для него.
Для удобства описываем Ray Cluster с помощью Helm-чарта и values-файла. Основная идея такая:
head (головной узел) — содержит Ray Head Pod с установленным Ray Dashboard, Autoscaler, если он нужен, и обеспечивает взаимодействие с worker’ами. Для обеспечения режима HA есть отдельная опция [11] gcsFaultToleranceOptions — подробнее о её работе расскажу чуть позже.
worker — 1+ подов, каждый из которых может работать на CPU или GPU.
additionalWorkerGroups — дополнительные группы worker’ов с другими конфигурациями (например, меньше памяти, более слабый GPU).
Ниже приведён пример ap-values.yaml [12] с основными настройками.
1. image
image:
repository: gitlab.example.com:5050/it-operations/k8s-config/vllm-0.8.4-ray-2.44.0-py310-cu124-serve
tag: 1.1.7
pullPolicy: IfNotPresent
Здесь:
repository и tag — это Docker-образ, который мы собрали на предыдущем шаге;
pullPolicy: IfNotPresent означает, что Kubernetes не будет заново скачивать образ, если он уже есть локально.
2. imagePullSecrets
imagePullSecrets:
- name: regcred
Если Registry приватный, нужно прописать секрет (credential).
3. common
common:
containerEnv:
- name: HF_HOME
value: "/data/model-cache"
- name: DTYPE
value: "bfloat16"
- name: GPU_MEMORY_UTIL
value: "0.98"
- name: MAX_MODEL_LEN
value: "131072"
containerEnv задаёт переменные окружения, которые попадут и в head, и в worker:
DTYPE (например, float16) — тип данных при инференсе;
GPU_MEMORY_UTIL — доля видеопамяти, которую можно занимать при работе vLLM;
MAX_MODEL_LEN — максимально допустимая длина токенов для модели (по необходимости).
4. head
head:
rayVersion: "2.44.0"
enableGcsFT: true
gcsFT:
# externalStorageNamespace: ray-gcs-backup
redisAddress: redis:6379
redisSecret:
name: redis-password-secret
key: password
labels:
component: ray-head
serviceAccountName: "sa-deepseek-cluster"
rayStartParams:
dashboard-host: "0.0.0.0"
num-cpus: "0"
metrics-export-port: "8080"
containerEnv:
- name: RAY_PROMETHEUS_HOST
value: https://prometheus.d8-monitoring:9090
envFrom:
- secretRef:
name: auth-config
resources:
limits:
cpu: "6"
memory: "8G"
requests:
cpu: "3"
memory: "4G"
volumes:
- name: model-cache
persistentVolumeClaim:
claimName: model-cache-pvc
volumeMounts:
- mountPath: /data/model-cache
name: model-cache
Здесь:
rayVersion — указывает, какая версия Ray используется Autoscaler’ом, если включён in-tree Autoscaling;
serviceAccountName — ServiceAccount, который нужно дать поду. Он может понадобиться для доступа к PV, Secret и так далее;
envFrom — берём переменные из Secret auth-config. Это логины, пароли, JWT-ключи;
resources — запросы и лимиты CPU/RAM для head-пода. В примере: лимит 6 CPU, 8GiB RAM;
volumes / volumeMounts — примонтированный CephFS (через PVC model-cache-pvc) на путь /data/model-cache, чтобы модель была доступна для head.
HA-режим GCS (GCS Fault Tolerance)
Чтобы защитить метаданные Ray-кластера от единственной точки отказа (GCS Head), мы включили опциональный режим репликации через Redis:
enableGcsFT: true — кастомный флаг, добавленный вручную, который заставляет Helm-шаблон сгенерировать секцию gcsFaultToleranceOptions. На момент написания статьи этого флага ещё нет в официальном чарте RayCluster [13].
gcsFT.redisAddress: redis:6379 — адрес нашего Redis-сервиса, где будут храниться дубли GCS-метаданных.
gcsFT.redisSecret.name/key — Kubernetes Secret с учётными данными доступа к Redis, чтобы пароль не «плавал» в YAML.
Принцип работы
При старте Ray-кластера GCS head теперь дублирует свои служебные данные — информацию о запущенных задачах и состояние акторов — не только во внутреннее хранилище, но и в Redis. Если оригинальный head-под выходит из строя, резервный head подхватывает сохранённые в Redis метаданные и продолжает обслуживание без потери состояния.
5. worker
worker:
groupName: rtx-3090
replicas: 2
minReplicas: 2
maxReplicas: 2
resources:
limits:
cpu: "16"
memory: "24G"
nvidia.com/gpu: "1"
requests:
cpu: "8"
memory: "12G"
nvidia.com/gpu: "1"
nodeSelector:
node.deckhouse.io/group: "w-gpu"
tolerations:
- key: "dedicated.apiac.ru"
operator: "Equal"
value: "w-gpu"
effect: "NoExecute"
volumes:
- name: model-cache
persistentVolumeClaim:
claimName: model-cache-pvc
volumeMounts:
- mountPath: /data/model-cache
name: model-cache
Здесь:
groupName — название группы worker’ов (rtx-3090);
replicas и minReplicas/maxReplicas — сколько подов worker будет по умолчанию и какой допускается диапазон;
resources — лимиты CPU/Memory и GPU. Например, 1 карта на под;
nodeSelector и tolerations — размещаем worker’ы на узлах с лейблом node.deckhouse.io/group=w-gpu;
volumes / volumeMounts — аналогично head монтируем PVC для общей модели.
6. additionalWorkerGroups
additionalWorkerGroups:
rtx-3060:
disabled: true
replicas: 1
minReplicas: 1
maxReplicas: 1
nodeSelector:
node.deckhouse.io/group: "w-gpu-3060"
...
Можно добавить сколько угодно дополнительных групп, каждая — со своими ресурсами, лейблами, affinities и так далее. Если пока не хотим их запускать, ставим disabled: true, как в примере.
Gemma 3 — одна из передовых LLM от Google, доступных на Hugging Face [14]. Она умеет работать с очень длинными текстами (до 128K токенов) и спроектирована так, чтобы эффективно использовать память и быстро отвечать. Этот пример подходит для демонстрации работы, но вы можете использовать аналогичные шаги для развёртывания других доступных в открытом доступе LLM.
В этом разделе разберёмся, как выбрать и запустить модель в Ray-кластере, а также как проверить работу нашего OpenAI-совместимого эндпойнта с JWT-аутентификацией.
Для запуска модели в Ray-кластере мы используем Ray Application. Данный механизм указывает Ray Serve, какие файлы брать, какую модель загружать и под какими настройками параллелизации (tensor/pipeline) работать.
Отправляем POST-запрос на https://ray-dashboard.k8s.example.com/api/serve/applications/ с телом:
{
"applications": [
{
"import_path": "serve:model",
"name": "Gemma-3-12b",
"route_prefix": "/",
"autoscaling_config": {
"min_replicas": 1,
"initial_replicas": 1,
"max_replicas": 1
},
"deployments": [
{
"name": "VLLMDeployment",
"num_replicas": 1,
"ray_actor_options": {},
"deployment_ready_timeout_s": 1200
}
],
"runtime_env": {
"working_dir": "file:///home/ray/serve.zip",
"env_vars": {
"MODEL_ID": "google/gemma-3-12b-it",
"TENSOR_PARALLELISM": "1",
"PIPELINE_PARALLELISM": "2",
"MODEL_NAME": "gemma-3-12b",
"MAX_MODEL_LEN": "131072",
"MAX_NUM_SEQS": "256",
"ENABLE_ENFORCE_EAGER": "true",
"CPU_OFFLOAD_GB": "0",
"DTYPE": "auto",
"VLLM_USE_V1": "1",
"GPU_MEMORY_UTIL": "0.98"
}
}
}
]
}
1. import_path: "serve:model"
Указывает Ray, что в архиве (working_dir) находится Python-модуль serve.py, в котором определён объект model.
Именно он развёртывается как Ray Serve Deployment.
2. name: "Gemma-3-12b"
Имя приложения в Ray Dashboard, чтобы легко отличать его от других.
3. route_prefix: "/"
Базовый путь, по которому будет доступен сервис при наличии Ingress или сервисов.
4. autoscaling_config
min_replicas, initial_replicas, max_replicas задают политику масштабирования. В примере — 1 реплика без автоскейла.
5. deployments
Здесь описан VLLMDeployment с num_replicas = 1. Это класс/обёртка vLLM в serve.py.
deployment_ready_timeout_s = 1200 даёт время (20 минут) на инициализацию модели. Это полезно при больших загрузках.
6. runtime_env
working_dir: "file:///home/ray/serve.zip" говорит Ray, где лежит код — скрипты serve.py, auth.py.
env_vars: задаёт переменные окружения для vLLM:
"MODEL_ID" — название модели на Hugging Face, здесь "google/gemma-3-12b-it";
"TENSOR_PARALLELISM" и "PIPELINE_PARALLELISM" — регламентируют шардирование и конвейерную параллельность ( "TENSOR_PARALLELISM": "1" — на каждом узле 1 GPU, "PIPELINE_PARALLELISM": "2" — 2 GPU всего в кластере);
"MODEL_NAME" — отображается в ответах API как название модели;
"MAX_MODEL_LEN" — максимальная длина обрабатываемой последовательности в токенах, можно задать в чарте Ray Cluster;
"MAX_NUM_SEQS" — максимальное число одновременных сессий/запросов к модели;
"ENABLE_ENFORCE_EAGER" — принудительное включение eager-режима в vLLM для улучшения детерминированности и дебага;
"CPU_OFFLOAD_GB" — объём оперативной памяти (GB), выделяемой под офлоад части вычислений с GPU на CPU;
"DTYPE" — выбор формата данных (bfloat16/float32 и т.д.) для инференса, можно задать в чарте Ray Cluster;
"VLLM_USE_V1" — переключает движок на новую архитектуру vLLM V1 с единым диспетчером задач, менеджером KV-кэша и другими оптимизациями, включёнными по умолчанию;
"GPU_MEMORY_UTIL" — доля доступной видеопамяти (98 %) для использования моделью, можно задать в чарте Ray Cluster.
После успешного запроса Ray:
Извлекает файлы из serve.zip и создаёт Ray Serve Application под именем Gemma-3-12b.
В разделе Deployments появляется VLLMDeployment. vLLM инициализируется, подгружая Gemma-3-12b из Hugging Face.
При желании можно просмотреть логи, где будет видно, как vLLM скачивает вес модели и запускает инференс.
С этого момента модель Gemma 3 готова к приёму запросов на OpenAI-совместимые эндпойнты (например, /v1/chat/completions), используя конфигурацию параллелизации (tensor/pipeline), указанную в env_vars.

1. Создание пользователей
В Kubernetes создаём Secret (auth.yaml [15]) с JWT-ключом, параметрами истечения токена, а также списком пользователей (логины, роли, хэши паролей).
Пример auth.yaml:
apiVersion: v1
kind: Secret
metadata:
name: auth-config
namespace: kuberay-projects
type: Opaque
data:
JWT_KEY: ZGVmYXVsdF9qd3RfS2V5
ACCESS_TOKEN_EXPIRE_MINUTES: NjA=
SKIP_EXP_CHECK: ZmFsc2U=
HUGGING_FACE_HUB_TOKEN: ""
USER_LIST: QUxJQ0UsIEJPQg==
ALICE_USERNAME: YWxpY2U=
ALICE_HASHED_PASSWORD: ZmFrZWhhc2hlZGhhc2g=
ALICE_ROLE: YWRtaW4=
BOB_USERNAME: Ym9i
BOB_HASHED_PASSWORD: ZmFrZWhhc2hlZGhhc2g=
BOB_ROLE: Z3Vlc3Q=
В переменных окружения ALICE_HASHED_PASSWORD и так далее хранится уже «соль + хэш» пароля.
2. Генерация пароля и хэша
Используем Python-скрипт gen_pwd.py [16], где с помощью secrets.token_hex и hashlib.sha256 вычисляем соли и хэши паролей. Затем записываем хэшированный пароль в значение строки PASSWORD для конкретного пользователя, например ALICE_HASHED_PASSWORD. Соль записываем в строку JWT_KEY. Это нужно, чтобы auth.py мог сравнивать введённые данные при аутентификации.
В качестве демонстрации у нас для всех паролей и ключа JWT одинаковая соль, но в реальных условиях, конечно же, так делать не нужно.
3. Авторизация и получение JWT
Выполняем запрос на /token, передавая username / password в формате form-data:
curl --location 'https://openai-api.example.com/token'
--form 'username="admin"'
--form 'password="password"'
В ответ:
{
"access_token": "<токен>",
"token_type": "bearer"
}
4. Вызов инференса
Теперь пробуем вызвать через API сам инференс, задав один из самых сложных вопросов во вселенной:
curl --location 'https://openai-api.example.com/v1/chat/completions'
--header 'Content-Type: application/json'
--header 'Authorization: Bearer <токен>'
--data '{
"model": "Gemma-3-12b",
"messages": [
{
"role": "user",
"content": "Сколько четвергов в марте 2025 года?"
}
],
"stream": false,
"max_tokens": 2000,
"temperature": 0.85,
"top_p": 0.95,
"top_k": 50,
"repetition_penalty": 1.0
}'
Получаем ответ, по которому видим, что ИИ успешно справился с задачей — хотя другие модели с трудом отвечали на этот вопрос (это не шутка):
{
"id": "chatcmpl-1745243015.727946",
"object": "chat.completion",
"created": 1745243015,
"model": "Gemma-3-12b",
"choices": [
{
"index": 0,
"message": {
"role": "assistant",
"content": "В марте 2025 года будет **четыре** четверга.nnВот даты:nn* 6 мартаn* 13 мартаn* 20 мартаn* 27 марта"
},
"finish_reason": "stop"
}
],
"usage": {
"prompt_tokens": 23,
"completion_tokens": 46,
"total_tokens": 69
}
}
В Ray Dashboard видим загруженность GPU: скорость ~40 token/s в зависимости от конкретной модели и конфигурации.
А логи отображают процесс инференса, можно анализировать ошибки [17] или задержки.

Чтобы понять, как быстро и надёжно работает модель, я провёл два вида тестов с помощью LLMPerf [18] — один на скорость и нагрузку, другой на корректность ответов под нагрузкой.
Тест скорости и нагрузки:
Скрипт token_benchmark_ray.py.
Менял число параллельных запросов (1, 4, 8, 16, 20), длину контекста (от 512 до 32 768 токенов) и способ сэмплинга (greedy, Top-K, Top-P, их сочетание).
Для каждого случая я запускал от 30 до 100 запросов и фиксировал:
время на токен (inter_token_latency_s);
время до первого токена (ttft_s);
общую задержку (end_to_end_latency_s);
скорость (throughput_token_per_s);
p50/p95/p99 в каждой метрике.
Тест стабильности и точности:
Скрипт llm_correctness.py.
Отправлял до 300 запросов при 20 параллельных сессиях и смотрел, стали ли ответы «уезжать» от ожидаемого или уходить в ошибку. Фиксировал error_rate и mismatch_rate.
CUDA Graph для Gemma-3-12B пришлось отключить (ENABLE_ENFORCE_EAGER=true), иначе модель падала. На Dolphin3.0 с графом производительность была ≈ 40 t/s.
Если у вас есть идеи, как вернуть CUDA Graph для Gemma-3 или другие способы ускорить эту модель, напишите, пожалуйста, в комментариях!
Тесты шли через внешний OpenAI API, поэтому в задержку добавлялся сетевой оверхед.
Все тесты на одной и той же конфигурации с постоянными GPU и RAM, чтобы сравнения были честными.
Базовая производительность: модель выдаёт новый токен каждые ≈ 0,116 с, скорость генерации ≈ 10 tok/s, время до первого токена ≈ 3,2 с.
Параллелизм: оптимально использовать до 8 одновременных сессий — при дальнейшем росте latency начинает расти непропорционально.
Контекст: безопасный предел — до ≈ 2 000–4 000 токенов; при больших окнах существенно падает скорость (до 6 tok/s на 8 192 токенах и ниже) и возрастает риск ошибок.
Сэмплинг: выбор Top-K или Top-P даёт незначительное замедление (< 0,005 с/токен), но может улучшить качество генерации.
Надёжность: при 20 concurrent запросах error_rate = 0 %, mismatch_rate < 1 % — модель отвечает стабильно, без падений.
Все подробные результаты и исходные скрипты доступны в репозитории [19].
Успешно подключили Gemma 3 из Hugging Face, развернув её в Ray-кластере с vLLM «под капотом».
API /v1/chat/completions проверен — он выдаёт JSON-ответ, совместимый с OpenAI-протоколом.
JWT-аутентификация ограничивает доступ к этому эндпойнту, пользователи хранятся в Secret.
Провели ряд тестов производительности с помощью LLMPerf. Средняя межтокеновая задержка составила ≈0,116 с, а throughput — ≈10 т/с, что наглядно подтверждает стабильность и корректность работы инференса. Не так быстро, как хотелось бы, но я продолжаю исследовать возможности улучшить результат.
Итак, мы протестировали API. Дальше хочется подключить удобный интерфейс. В следующей, заключительной, статье [20] мы рассмотрим, как развернуть OpenWebUI — бесплатный веб-интерфейс для взаимодействия с LLM. Подробно разберём его основные возможности и настройки, а также посмотрим, как связать OpenWebUI с нашим Ray-кластером, чтобы общаться с моделью через OpenAI-совместимый API.
Автор: Myskat_90
Источник [21]
Сайт-источник BrainTools: https://www.braintools.ru
Путь до страницы источника: https://www.braintools.ru/article/15244
URLs in this post:
[1] начать с неё: https://habr.com/ru/companies/flant/articles/906700/
[2] vLLM: https://github.com/vllm-project/vllm
[3] памяти: http://www.braintools.ru/article/4140
[4] Ray Serve: https://docs.ray.io/en/latest/serve/index.html
[5] serve.py: https://github.com/Myskat90/vllm-habr/blob/main/ray-serve-vllm/serve.py
[6] auth.py: https://github.com/Myskat90/vllm-habr/blob/main/ray-serve-vllm/auth.py
[7] логика: http://www.braintools.ru/article/7640
[8] dockerfile.ray: https://github.com/Myskat90/vllm-habr/blob/main/ray-serve-vllm/dockerfile.ray
[9] Makefile: https://github.com/Myskat90/vllm-habr/blob/main/ray-serve-vllm/Makefile
[10] уже есть возможность: https://deckhouse.ru/products/kubernetes-platform/documentation/v1/modules/node-manager/faq.html#%D0%BA%D0%B0%D0%BA-%D0%B4%D0%BE%D0%B1%D0%B0%D0%B2%D0%B8%D1%82%D1%8C-%D0%B0%D0%B2%D1%82%D0%BE%D1%80%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D1%8E-%D0%B2-%D0%B4%D0%BE%D0%BF%D0%BE%D0%BB%D0%BD%D0%B8%D1%82%D0%B5%D0%BB%D1%8C%D0%BD%D1%8B%D0%B9-registry
[11] отдельная опция: https://docs.ray.io/en/latest/cluster/kubernetes/user-guides/kuberay-gcs-ft.html#gcs-fault-tolerance-in-kuberay
[12] ap-values.yaml: https://github.com/Myskat90/vllm-habr/blob/main/argo-projects/kube-ray/charts/ray-cluster/ap-values.yaml
[13] RayCluster: https://github.com/ray-project/kuberay/tree/master/helm-chart/ray-cluster
[14] на Hugging Face: https://huggingface.co/google/gemma-3-12b-it
[15] auth.yaml: https://github.com/Myskat90/vllm-habr/blob/main/ray-serve-vllm/auth.yaml
[16] gen_pwd.py: https://github.com/Myskat90/vllm-habr/blob/main/ray-serve-vllm/gen_pwd.py
[17] ошибки: http://www.braintools.ru/article/4192
[18] LLMPerf: https://github.com/ray-project/llmperf
[19] в репозитории: https://github.com/Myskat90/vllm-habr/tree/main/llmperf_test_results
[20] В следующей, заключительной, статье: https://habr.com/ru/companies/flant/articles/906704/
[21] Источник: https://habr.com/ru/companies/flant/articles/906702/?utm_source=habrahabr&utm_medium=rss&utm_campaign=906702
Нажмите здесь для печати.