Esecuzione di carichi LLM multi-nodo con i contenitori Ray LLM di AWS
Introduzione
Questo documento illustra come distribuire un Large Language Model (LLM) su due nodi GPU in Amazon Elastic Kubernetes Service (EKS) utilizzando i Ray LLM Deep Learning Containers. La soluzione prevede nodi separati per le fasi di prefill e decode, tutti accessibili tramite un unico endpoint compatibile con l’API di OpenAI. Il modello viene servito dietro un endpoint HTTP senza scrivere codice personalizzato, grazie all’integrazione tra Ray Serve, NIXL e l’interfaccia di rete ad alta velocità Elastic Fabric Adapter (EFA).
Perché scegliere una configurazione multi‑nodo
Ray è un framework di calcolo distribuito; le sue distribuzioni (Ray Serve) possono estendersi su più nodi. Diverse esigenze spingono i clienti verso un’architettura multi‑nodo:
- Modelli di grandi dimensioni che non entrano in una singola GPU.
- Necessità di throughput superiore rispetto a una sola replica.
- Separazione delle fasi di inferenza per ottimizzare le performance.
In tutti questi casi, gli utenti devono gestire manualmente la catena di dipendenze (librerie CUDA, framework, ecc.) e risolvere eventuali problemi di incompatibilità, attività che distolgono tempo dallo sviluppo del modello.
Deep Learning Containers di AWS
I Deep Learning Containers (DLC) di AWS forniscono immagini Docker pre‑costruite, ottimizzate per le GPU e contenenti framework, dipendenze e stack CUDA già testati e patchati. Con il lancio del Ray LLM DLC per distribuzioni multi‑nodo, questa filosofia si estende anche all’inferenza distribuita. Il container è pronto per servire modelli dietro un endpoint HTTP, è mantenuto da AWS e contiene già l’intera stack di inferenza.
Disaggregazione prefill/decode (PD)
L’inferenza LLM è composta da due fasi distinte:
- Prefill: elabora l’intero prompt in parallelo, costruendo la cache chiave‑valore (KV).
- Decode: genera la risposta token per token, leggendo ed estendendo la cache KV.
Prefill è limitata dalla potenza di calcolo, mentre Decode è limitata dalla larghezza di banda della memoria. Quando entrambi condividono la stessa GPU, il lavoro pesante di prefill può bloccare la latenza sensibile di Decode. Spostando le due fasi su hardware separato, è possibile scegliere GPU e configurazioni di servizio ottimali per ciascuna fase, migliorando le prestazioni complessive.
Vantaggi e compromessi della PD disaggregata
Separare prefill e decode su nodi distinti elimina le contese per le risorse, ma richiede il trasferimento rapido della cache KV dal nodo prefill a quello decode. Questo rende cruciale la scelta di un’interconnessione ad alta velocità, motivo per cui il progetto utilizza EFA con libfabric.
Componenti forniti dal Ray LLM DLC
Il Ray LLM DLC include:
- vLLM come motore di inferenza.
- Ray Serve per l’esposizione HTTP.
- NIXL per il trasferimento della cache KV tra i nodi.
- Stack EFA (libfabric e aws‑ofi‑nccl) per comunicazioni a larghezza di banda elevata.
La libreria Ray Serve LLM incorpora un costruttore di applicazioni basato su YAML; non serve scrivere classi handler, archivi di modello o immagini personalizzate. Un unico helper, buildpdopenai_app, collega le configurazioni di prefill e decode dietro un singolo endpoint OpenAI‑compatible.
Architettura di riferimento
La figura (non mostrata) descrive il deployment su Amazon EKS:
- Un nodo CPU (
m7i.xlarge) funge da nodo head Ray, ospita KubeRay e la logica di routing. - Due nodi GPU (
g6.8xlarge) gestiscono rispettivamente le fasi di prefill e decode. - Tutti i nodi risiedono in una VPC, nelle subnet private con accesso in uscita tramite NAT Gateway.
- I worker GPU scaricano il Ray LLM DLC da Amazon Elastic Container Registry (ECR).
- KubeRay osserva il
RayServiceapplicato e stabilisce automaticamente le connessioni head‑to‑worker.
Il gruppo di GPU è vincolato a una singola Availability Zone, poiché EFA non funziona attraverso zone diverse.
Prerequisiti per la sperimentazione
Per seguire la guida occorrono:
- Un account AWS con fatturazione attiva.
- Quota di servizio sufficiente per avviare due istanze
g6.8xlarge(almeno 64 vCPU) nella regione desiderata. - AWS CLI configurato con credenziali valide.
- kubectl per interagire con il cluster Kubernetes.
- eksctl per creare e gestire il cluster EKS.
- helm per installare l’operatore KubeRay.
- Utility
envsubst(parte di GNU gettext) per renderizzare i manifest.
Verifica le credenziali con:
aws sts get-caller-identity
Configurazione dell’applicazione
Il file manifest/rayservice.yaml contiene il blocco serveConfigV2. La sezione applications utilizza buildpdopenai_app per definire le configurazioni di prefill e decode. Di seguito la configurazione completa (tradotta in italiano):
applications:
- name: qwen
route_prefix: /
args:
prefill_config:
modelloadingconfig: { modelid: qwen3.5-9b, modelsource: Qwen/Qwen3.5-9B }
engine_kwargs:
maxmodellen: 4096
dtype: bfloat16
gpumemoryutilization: 0.90
enforce_eager: true
kvtransferconfig:
kv_connector: NixlConnector
kvrole: kvboth
engine_id: prefill
kvconnectorextra_config: { backends: ["LIBFABRIC"] }
deploymentconfig: { numreplicas: 1 }
decode_config:
modelloadingconfig: { modelid: qwen3.5-9b, modelsource: Qwen/Qwen3.5-9B }
engine_kwargs:
maxmodellen: 4096
dtype: bfloat16
gpumemoryutilization: 0.90
enforce_eager: true
kvtransferconfig:
kv_connector: NixlConnector
kvrole: kvboth
engine_id: decode
kvconnectorextra_config: { backends: ["LIBFABRIC"] }
deploymentconfig: { numreplicas: 1 }
Questa configurazione specifica:
- Il modello
qwen3.5-9bproveniente dal repositoryQwen/Qwen3.5-9B. - Lunghezza massima del modello di 4096 token.
- Precisione
bfloat16con utilizzo della memoria GPU fissato al 90%. - L’uso di
enforce_eagerper evitare la cattura del grafo CUDA, riducendo il picco di memoria. - Il connettore NIXL configurato per libfabric, garantendo trasferimenti velocissimi della cache KV.
Procedura di deployment
1. Crea il cluster EKS con eksctl create cluster specificando almeno un nodo CPU (m7i.xlarge) e due nodi GPU (g6.8xlarge).
2. Installa KubeRay tramite Helm: helm repo add kuberay https://ray-project.github.io/kuberay-helm/ e helm install kuberay kuberay/kuberay-operator.
3. Carica il Ray LLM DLC su ECR o utilizza l’immagine pubblica fornita da AWS.
4. Applica il manifest con kubectl apply -f manifest/rayservice.yaml. KubeRay rileva il RayService, avvia il pod head su CPU e i due worker su GPU.
5. Verifica l’endpoint invocando un comando curl verso l’indirizzo del servizio (es. curl -X POST http://).
Valutazione delle performance
Test preliminari mostrano che la separazione prefill/decode riduce la latenza di token generation del 30‑40 % rispetto a una configurazione monolitica su una singola GPU. Inoltre, la capacità di scalare indipendentemente le due fasi consente di aggiungere repliche decode per aumentare il throughput senza dover aumentare le risorse di prefill.
Casi d’uso consigliati
Questa architettura è particolarmente efficace per:
- Retrieval‑Augmented Generation (RAG), dove i prompt possono essere lunghi e variabili.
- Sommari di documenti estesi, che richiedono un prefill intensivo.
- Applicazioni agentiche che devono gestire molte richieste simultanee con latenza ridotta.
Riferimenti e risorse aggiuntive
Il codice completo è disponibile nel repository collegato di AWS (link fornito nell’articolo originale). Altri riferimenti utili includono:
- Documentazione ufficiale dei Deep Learning Containers di AWS.
- Guida all’installazione di KubeRay su EKS.
- Specifiche tecniche di Elastic Fabric Adapter e libfabric.
Conclusioni
Utilizzando i Ray LLM Deep Learning Containers, è possibile creare una pipeline di inferenza LLM distribuita, efficiente e pronta per la produzione con pochi comandi YAML. La disaggregazione prefill/decode elimina le contese di risorse, migliora la latenza e consente una scalabilità flessibile, mantenendo al contempo la semplicità operativa grazie al packaging completo di AWS. Questo approccio riduce significativamente il lavoro non differenziato di configurazione e consente ai team di concentrarsi sul valore aggiunto dei propri modelli.