Согласно материалу AWS Machine Learning, в этой статье описывается, как развернуть одну большую языковую модель (LLM) на двух графических процессорах (GPU) в Amazon Elastic Kubernetes Service (Amazon EKS) с использованием многонодовых контейнеров глубокого обучения Ray LLM. Используются отдельные узлы предзаполнения и декодирования за одним совместимым с OpenAI конечным пунктом. Такой многонодовой подход часто применяется для тензорного, конвейерного и параллелизма данных, а также для разграниченных нагрузок вывода предзаполнения и декодирования. В предыдущей статье мы представили Ray Serve DLC и использовали его для настройки небольшой модели за HTTP-конечным пунктом на одном узле GPU. У вас был рабочий конечный пункт вывода, и контейнер поддерживает совместимость стека GPU, включая библиотеки CUDA, так что вам не нужно было писать шаблонный код. Ray является распределённой вычислительной платформой, поэтому развертывания Ray Serve могут охватывать несколько узлов. Клиенты используют многонодовой Ray Serve по нескольким причинам. Некоторые хотят запускать базовые или открытые модели, которые слишком велики для одного GPU. Другие нуждаются в большей пропускной способности, чем может обеспечить одна реплика. Третьи хотят разделить две фазы вывода, чтобы улучшить производительность. В каждом случае клиентам необходимо самостоятельно построить цепочку зависимостей: выбрать совместимые библиотеки для стека GPU и отлаживать проблемы, когда компонент выходит из строя. Эта работа не является дифференцированной и отнимает время и усилия от доставки моделей. AWS Deep Learning Containers (DLC) давно решают подобные проблемы для тренировочных и выводных нагрузок. DLC — это предварительно собранные, оптимизированные по производительности образы Docker, которые объединяют фреймворк, его зависимости и стек GPU в протестированную и исправленную комбинацию, которую вы можете сразу загрузить и использовать. С запуском Ray LLM DLC для многонодовых развертываний этот же подход теперь распространяется и на многонодовой вывод. Вы получаете контейнер, специально созданный для обслуживания моделей за HTTP-конечным пунктом, который поддерживается и тестируется AWS, с уже собранным полным стеком вывода. В статье описывается реализация многонодного разграниченного вывода с использованием многонодового Ray LLM DLC: развертывание одной большой языковой модели на двух узлах с поддержкой GPU в Amazon EKS, работающих за одним конечным пунктом. В то время как один из узлов GPU обрабатывает фазу предзаполнения, другой узел GPU обрабатывает фазу декодирования. Разделение фаз предотвращает задержки в генерации токенов для запросов, уже находящихся в процессе, из-за ресурсоёмких длинных подсказок. Этот подход хорошо подходит для таких нагрузок, как расширенное извлечение информации (RAG), суммирование документов и агентские приложения. Мы используем многонодовой Ray LLM DLC, который включает Ray Serve LLM, библиотеку передачи вывода NVIDIA (NIXL) и стек Elastic Fabric Adapter (EFA), и предоставляет совместимый с OpenAI API без кода приложения. Полный код доступен в сопроводительном репозитории. Разделение вывода LLM на две отдельные фазы: предзаполнение обрабатывает весь входной запрос параллельно и строит кэш ключей и значений (KV). Декодирование затем генерирует ответ по одному токену за раз, считывая и расширяя этот кэш. Предзаполнение ограничено вычислительными ресурсами, а декодирование зависит от пропускной способности памяти. Когда обе фазы используют один GPU, они конкурируют за одни и те же ресурсы, и ресурсоёмкая работа по предзаполнению мешает стабильной, чувствительной к задержкам работе декодирования. Разделение фазы предзаполнения от фазы декодирования позволяет выбрать оборудование и свойства обслуживания для каждой фазы, улучшая общую производительность в масштабах. Разграничение PD помещает две фазы на отдельные узлы, так что каждая фаза работает без вмешательства в другую и может масштабироваться независимо. Однако есть компромисс: кэш KV, построенный во время предзаполнения, должен быть передан узлу декодирования, и эта передача должна быть достаточно быстрой, чтобы не нивелировать преимущества. Именно поэтому соединение между узлами имеет значение. Что предоставляет Ray LLM DLC? AWS DLC — это предварительно собранные, оптимизированные по производительности образы Docker, которые объединяют фреймворк, его зависимости и стек GPU в протестированную и исправленную комбинацию, которую вы можете сразу загрузить и использовать. Все компоненты проверяются вместе перед каждой версией, чтобы избежать расхождений версий между средой выполнения CUDA, фреймворком и слоем обслуживания. Устранение уязвимостей безопасности применяются на этапе сборки. Ray LLM DLC расширяет это на распределённое обслуживание LLM. Он включает vLLM в качестве движка вывода, HTTP Ray Serve в качестве слоя обслуживания, NIXL для передачи кэша KV между узлами и стек EFA (libfabric и aws-ofi-nccl) для высокоскоростной связи между узлами. Поскольку библиотека Ray Serve LLM включает встроенный генератор приложений, всё приложение обслуживания представляет собой блок YAML. Нет класса обработчика, нет архиватора модели и нет необходимости строить пользовательский образ. Один помощник, build_pd_openai_app, принимает конфигурацию предзаполнения и конфигурацию декодирования и соединяет передачу кэша KV между ними за одним совместимым с OpenAI конечным пунктом. Архитектура развертывания показана на диаграмме. Запрос пользователя достигает головного узла Ray на узле системы CPU, а два узла-работника GPU выполняют фазы предзаполнения и декодирования модели. Развертывание представляет собой кластер EKS внутри виртуальной частной сети (VPC). Узлы работают в частных подсетях с исходящим доступом через NAT Gateway. Узел системы CPU (m7i.xlarge) размещает системные нагрузки, оператор KubeRay и под головного узла Ray, который работает только на CPU. Два узла-работника GPU (g6.8xlarge) запускают рабочие узлы Ray: один обслуживает фазу предзаполнения, а другой — фазу декодирования. Оба узла загружают Ray LLM DLC из Amazon Elastic Container Registry (Amazon ECR). KubeRay отслеживает RayService, который вы применяете, и автоматически соединяет головной и рабочие узлы. Ray Serve размещает развертывания предзаполнения и декодирования на двух рабочих узлах GPU. Кэш KV передаётся от рабочего узла предзаполнения к рабочему узлу декодирования через NIXL, используя EFA через бэкенд libfabric NIXL. Группа узлов GPU закреплена за одной зоной доступности (AZ), так как EFA не может пересекать AZ. Чтобы следовать за этой статьей, вам потребуется: учётная запись AWS с включённой оплатой; достаточная квота услуг для запуска двух экземпляров g6.8xlarge (по крайней мере 64 vCPU) в вашем целевом регионе; интерфейс командной строки AWS (AWS CLI) с настроенными учётными данными; интерфейс командной строки kubectl для взаимодействия с управляющей плоскостью кластера Kubernetes; интерфейс командной строки eksctl для создания и управления кластерами на EKS; helm (менеджер пакетов для Kubernetes) для установки оператора KubeRay; утилита envsubst (часть GNU gettext) для рендеринга манифеста. Проверьте, что ваши учётные данные AWS активны с помощью следующей команды: aws sts get-caller-identity. Используйте предварительно собранное приложение для обслуживания вывода. Приложение — это блок serveConfigV2 в manifest/rayservice.yaml. Оно использует build_pd_openai_app, который принимает prefill_config и decode_config и соединяет передачу кэша KV между ними. Несколько настроек имеют наибольшее значение. max_model_len: 4096 задаёт контекст для соответствия измеренному кэшу KV на GPU. dtype: bfloat16 и gpu_memory_utilization: 0.90 устанавливают точность и то, сколько видеопамяти vLLM может использовать. enforce_eager: true пропускает захват графиков CUDA, чтобы уменьшить пик видеопамяти (в зависимости от модели и входных данных). Настройка kv_transfer_config — это то, что делает разграничение возможным: обе фазы используют NixlConnector с kv_role: kv_both и отдельным engine_id (предзаполнение на одном, декодирование на другом). Критическая настройка — kv_connector_extra_config: { backends: ["LIBFABRIC"] }. Без этой строки NIXL по умолчанию использует UCX, который работает на скорости TCP даже на EFA. Остальная часть манифеста в rayClusterConfig описывает поды. Головной под работает только на CPU (num-gpus: "0") и закреплён за группой системных узлов. Каждый рабочий под запрашивает один GPU и один интерфейс EFA (nvidia.com/gpu: "1", vpc.amazonaws.com/efa: "1") и нацеливается на узлы GPU через nodeSelector: { role: gpu-worker }. Он работает с привилегиями: true, так как NIXL регистрирует CUDA VRAM с EFA NIC. Возможности IPC_LOCK недостаточно. Рабочие узлы устанавливают FI_PROVIDER=efa, читают VLLM_HOST_IP из IP пода и монтируют общий объём памяти. Ограничения по памяти находятся значительно выше примерно 18 GiБ контрольной точки, чтобы ОС могла удерживать его в кэше страниц. Для загрузки модели Qwen3.5-9B на g6.8xlarge с предыдущей конфигурацией кэша время загрузки весов модели сокращается с примерно 145 секунд до 2 секунд при перезапуске. Развертывание на Amazon EKS. Все сценарии используют один файл конфигурации, scripts/env.sh. Скрипт содержит значения по умолчанию для CLUSTER_NAME, REGION, K8S_VERSION и NAMESPACE. Другие значения по умолчанию охватывают системные и GPU группы узлов, версию KubeRay и модель. Переопределите любую переменную, экспортируя её перед запуском скрипта. Из корневого проекта установите базовый каталог, затем загрузите переменные: CURRENT_DIR=$(pwd) cd $CURRENT_DIR/ray-serve-multi-node/scripts source ./env.sh. Шаг 1: Создайте кластер EKS ./deploy_cluster.sh. Это создаёт кластер EKS (VPC, OIDC провайдер, основные дополнения) и группу узлов системного процессора, которая запускает системные нагрузки, оператора KubeRay и головной узел Ray. Узлы работают в частных подсетях с выходом через NAT Gateway, а подсети кластера расположены в AZ, которые предлагают тип экземпляра GPU. Скрипт идемпотентен и занимает 15–20 минут при первом запуске. Шаг 2: Добавьте рабочие узлы GPU ./deploy_node_group.sh. Это создаёт группу узлов GPU: два экземпляра g6.8xlarge с включённым EFA, закреплённых за одной AZ и помеченных как role=gpu-worker, чтобы рабочие узлы Ray нацеливались на них. Включение EFA также устанавливает плагин устройства EFA, который рекламирует vpc.amazonaws.com/efa на каждом узле. Этот шаг занимает около 5-20 минут, обычно дольше при первом запуске. Шаг 3: Установите оператора KubeRay ./install_kuberay.sh. Это устанавливает оператора KubeRay с использованием Helm, закреплённого за группой системных узлов, чтобы оставить узлы GPU доступными для вывода. KubeRay отслеживает RayService, который вы применяете далее, и автоматически соединяет головной и рабочие узлы. Этот шаг занимает около 1–2 минут. Шаг 4: Разверните RayService ./deploy_ray_service.sh. Это рендерит manifest/rayservice.yaml с вашим изображением, моделью и переменными именования, используя envsubst, и применяет это. KubeRay создаёт головные и два рабочих пода, а Ray Serve LLM поднимает развертывания предзаполнения и декодирования. Скрипт ждёт, пока головной узел не станет готовым, оба рабочих узла не начнут работать, и, наконец, пока приложение Serve не сообщит о состоянии RUNNING. Поскольку каждый узел загружает модель, ожидайте, что этот шаг займет несколько минут. Вы можете проверить статус в любое время, включая информацию о том, на каком узле каждый под расположен и какая ёмкость GPU доступна: ./deploy_ray_service.sh status. Вызов модели. После того как приложение Serve будет здоровым, KubeRay создаёт стабильный сервис -serve-svc. Подключитесь к нему: kubectl port-forward -n inference svc/ray-llm-serve-svc 8000:8000. Поскольку DLC предоставляет совместимый с OpenAI API, вы можете перечислить модели и отправить стандартный запрос на завершение чата без пользовательской схемы: curl --silent http://127.0.0.1:8000/v1/models curl --fail --silent --show-error \ --request POST "http://127.0.0.1:8000/v1/chat/completions" \ --header "Content-Type: application/json" \ --data '{ "model": "qwen3.5-9b", "messages": [ {"role": "user", "content": "Объясните преимущества разграничения предзаполнения/декодирования."} ], "max_tokens": 200, "temperature": 0.7 }'. Проверьте, что EFA используется. NIXL возвращается к транспортировке на скорости TCP, если он не может использовать libfabric, и делает это без поднятия ошибки, поэтому стоит подтвердить, какой бэкенд он выбрал. Просмотрите логи рабочих узлов: for POD in $(kubectl get pods -n inference -l ray.io/node-type=worker \ -o jsonpath='{.items[*].metadata.name}'); do kubectl exec -n inference "$POD" -c ray-worker -- \ grep -rhoE "Backend (LIBFABRIC|UCX) was instantiated" /tmp/ray/session_latest/logs/ 2>/dev/null done | sort | uniq -c deploy_node_group.sh уже печатает количество vpc.amazonaws.com/efa на каждом узле, когда создаёт группу. Для получения данных о пропускной способности между узлами DLC включает предварительно собранную библиотеку NVIDIA Collective Communications Library (NCCL) benchmark в /usr/local/bin/all_reduce_perf. Очистка. Чтобы избежать продолжающихся затрат, удалите ресурсы в обратном порядке: ./delete_ray_service.sh # Удалить RayService ./install_kuberay.sh cleanup # Удалить оператора KubeRay ./delete_node_group.sh # Удалить группу узлов GPU ./delete_cluster.sh # Удалить кластер EKS. Заключение и следующие шаги. Вы обслуживали одну модель на двух узлах GPU с разграничением предзаполнения/декодирования, и всё приложение было блоком YAML, без кода обработчика, без архиватора модели и без ручной сборки стека GPU и EFA. Многонодовой Ray LLM DLC обеспечивал протестированный стек вывода, KubeRay координировал кластер Ray, а NIXL через EFA переносил кэш KV между узлами. Ваша задача заключалась в конфигурации, а не в сборке. Распространённые схемы для запуска многонодового вывода включают тензорный и конвейерный параллелизм (разделение модели, слишком большой для одного экземпляра), параллелизм данных (репликация модели для пропускной способности) и разграничение предзаполнения/декодирования (разделение двух фаз вывода на разные узлы). Отсюда вы можете масштабировать реплики декодирования для большей пропускной способности, перейти к большим моделям с тензорным или конвейерным параллелизмом на более крупных экземплярах GPU или добавить дополнительные библиотеки на ту же протестированную базу для моделей, которым они нужны. Для полного кода, текущего списка тегов изображений и основных механизмов смотрите сопроводительный репозиторий, страницу доступных изображений Ray LLM DLC, документацию по предзаполнению/декодированию Ray Serve LLM и документацию по EFA. Материал подготовлен нейросетью на основе оригинальной публикации. ИИ может ошибаться в деталях (цифры, имена, даты, цитаты) — для важной информации сверяйтесь с первоисточником.