Contribuisci a migliorare questa pagina
Le traduzioni sono generate tramite traduzione automatica. In caso di conflitto tra il contenuto di una traduzione e la versione originale in Inglese, quest'ultima prevarrà.
Per contribuire a questa guida per l'utente, scegli il GitHub link Modifica questa pagina su che si trova nel riquadro destro di ogni pagina.
Le traduzioni sono generate tramite traduzione automatica. In caso di conflitto tra il contenuto di una traduzione e la versione originale in Inglese, quest'ultima prevarrà.
Identifica le soglie metriche di scalabilità per l'inferenza AI
Suggerimento
Questa sezione mostra come eseguire il test di carico di un server di inferenza vLLM su Amazon EKS per identificare quando diventa saturo. Usa i risultati del test di carico per configurare la profondità della coda e le soglie di latenza per la scalabilità automatica orizzontale nella sezione successiva. Inferenza AI con scalabilità automatica con HPA e KEDA
La procedura dettagliata utilizza i seguenti strumenti:
-
k6
(Grafana k6) è uno strumento di test di carico open source, eseguito come immagine del grafana/k6contenitore, che invia richieste di inferenza alla replica a frequenze di richiesta controllate. -
Ministral-3-8B-Instruct-2512 modello
su un'istanza. g6e.4xlargeÈ possibile applicare la stessa metodologia a qualsiasi modello open source e tipo di GPU.
Prerequisiti
Questa sezione si basa su due sezioni precedenti della documentazione. Completali entrambi prima di iniziare:
-
Cluster Setup fornisce un cluster EKS con una GPU NodePool, lo stack di monitoraggio kube-prometheus-stack (Prometheus e Grafana) e un bucket Amazon S3 per i pesi dei modelli. Il modello pesa un secchio S3
-
Load & Serve Models implementa vllm-inference-app ServiceMonitor il server di inferenza vLLM e crea il.
Conferma ogni elemento di seguito prima di iniziare e completa prima la sezione collegata se un controllo fallisce.
Se hai aperto un nuovo terminale, imposta il nome del cluster e la regione che hai usato in precedenza:
export CLUSTER_NAME=ai-eks-docs export AWS_REGION=us-east-2
Acquisisci il nome del bucket del modello Amazon S3 creato nelle fasi di configurazione del cluster. Il generatore di carico passa questo valore a vLLM.
MODEL_BUCKET=$(aws s3api list-buckets \ --query "Buckets[?starts_with(Name, '${CLUSTER_NAME}-models-')].Name | [0]" \ --output text) echo "Model bucket: ${MODEL_BUCKET}"
Verificare che lo stack di monitoraggio sia in esecuzione
Lo stack kube-prometheus-stack della configurazione di monitoraggio viene eseguito nel namespace. monitoring
kubectl get pods -n monitoring
I pod Prometheus e Running Grafana dovrebbero essere:
NAME READY STATUS RESTARTS AGE kube-prometheus-stack-grafana-6f9c8b7d5c-2xk9p 3/3 Running 0 3h kube-prometheus-stack-operator-7b8c9d6f4-q4m7n 1/1 Running 0 3h prometheus-kube-prometheus-stack-prometheus-0 2/2 Running 0 3h
Se mancano, completa la configurazione del monitoraggio prima di continuare. Monitoraggio
Verificare che il modello vLLM sia in esecuzione
I modelli vllm-inference-app Deployment and vllm-inference-svc Service from Load & Serve vengono eseguiti nel namespace. default
kubectl get deployment vllm-inference-app
Il rapporto di distribuzione dovrebbe essere pronto: 1/1
NAME READY UP-TO-DATE AVAILABLE AGE vllm-inference-app 1/1 1 1 3h
Se la distribuzione è mancante o non è pronta, completa i modelli Load & Serve prima di continuare.
Accedi a Grafana
Guarda il test di carico nella dashboard vLLM precaricata, quindi assicurati che il load balancer Grafana che hai impostato nella sezione Access Grafana sia raggiungibile. Accedi a Grafana Stampa il suo URL:
echo "http://$(kubectl get ingress kube-prometheus-stack-grafana -n monitoring -o jsonpath='{.status.loadBalancer.ingress[0].hostname}')"
Apri l'URL nel tuo browser e accedi con nome utente admin e password con il seguente comando:
kubectl --namespace monitoring get secrets kube-prometheus-stack-grafana -o jsonpath="{.data.admin-password}" | base64 -d ; echo
Conferma che il vLLM ServiceMonitor esista
Il vllm-inference-app ServiceMonitor, creato nella fase Monitor vLLM, viene eseguito nel default namespace e indica a Prometheus dove raccogliere le metriche vLLM.
kubectl get servicemonitor vllm-inference-app
Output previsto:
NAME AGE vllm-inference-app 3h
Se manca, completa il passaggio Monitor vLLM. Fase 4: Monitorare vLLM
Passaggio 1: riscaldare la GPU
Anche se il modello viene caricato nella memoria della GPU e la replica riporta Pronto, le prime richieste richiedono più tempo rispetto a quelle successive perché vLLM e la GPU terminano l'inizializzazione in base alle prime inferenze. vLLM acquisisce grafici CUDA, compila e ottimizza automaticamente i kernel della GPU e alloca i pool di memoria cache KV, mentre la GPU aumenta la velocità di clock da inattiva a quella di boost.
Si tratta di costi una tantum, quindi misurare le soglie prima di riscaldare i record di avvio funziona invece di servire allo stato stazionario. Il riscaldamento successivo invia 100 richieste sequenziali per completare l'inizializzazione, pertanto il test di carico che segue misura la capacità allo stato stazionario.
Per prima cosa, create il file ConfigMap che contiene lo script di riscaldamento (). warmup.js Lo script legge il modello di destinazione dalla variabile di MODEL ambiente, che Job imposta in base al MODEL_BUCKET valore acquisito in precedenza:
Esempio riscaldamento k6 ConfigMap
cat << 'EOF' | kubectl apply -f - apiVersion: v1 kind: ConfigMap metadata: name: vllm-warmup-script namespace: default data: warmup.js: | import http from 'k6/http'; export default function () { const payload = JSON.stringify({ model: __ENV.MODEL, prompt: 'Write a brief technical explanation about cloud computing concepts.', max_tokens: 100, temperature: 0.7, }); const params = { headers: { 'Content-Type': 'application/json' }, timeout: '240s' }; http.post('http://vllm-inference-svc:8000/v1/completions', payload, params); } EOF
Quindi esegui il warm-up Job. Un singolo client invia 100 richieste sequenziali:
Esempio Warm-up Lavoro
cat << EOF | kubectl apply -f - apiVersion: batch/v1 kind: Job metadata: name: vllm-warmup namespace: default spec: backoffLimit: 0 template: spec: restartPolicy: Never containers: - name: k6 image: grafana/k6:latest env: - name: MODEL value: "s3://${MODEL_BUCKET}/Ministral-3-8B-Instruct-2512/" args: ["run", "--vus", "1", "--iterations", "100", "/scripts/warmup.js"] volumeMounts: - name: script mountPath: /scripts volumes: - name: script configMap: name: vllm-warmup-script EOF
Attendi che il riscaldamento finisca (ci vogliono alcuni minuti):
kubectl wait --for=condition=complete job/vllm-warmup --timeout=600s
Apri Grafana (apri il nome host del load balancer; vedi Access Grafana per i dettagli) e vai a Dashboards > GPU Monitoring > Performance Testing - VLLM Load Analysis. Il pannello VLM Request Rate mostra l'aumento del traffico di riscaldamento. Il pannello VLLM Average Latency mostra che la prima richiesta è più lenta, ossia il costo di riscaldamento una tantum della GPU.
Dashboard vLLM che mostra l'effetto di riscaldamento della GPU sulla latenza
Quindi elimina il job di riscaldamento:
kubectl delete job vllm-warmup
Passaggio 2: invia il carico a tassi di richiesta crescenti
In questo passaggio, si esegue un test di carico che invia richieste alla singola replica vLLM a velocità crescenti: 10, 20, 30, 40, 50, 60 e 70 richieste al secondo. Questo simula l'aumento della domanda da parte degli utenti. Il test utilizza k6 in un modello a tasso di arrivo costante, in cui la variabile di TARGET_RPS ambiente imposta la frequenza di richiesta target. Ogni frequenza viene eseguita per 60 secondi per consentire alle metriche di stabilizzarsi, seguita da una pausa di 30 secondi prima del successivo aumento della frequenza.
L'obiettivo di questo test è identificare il punto di saturazione, ovvero la frequenza di richiesta alla quale una singola replica non è più in grado di tenere il passo con le richieste in arrivo. È possibile monitorare la profondità della coda () e la latenza end-to-end (vllm:num_requests_waiting). vllm:e2e_request_latency_seconds L'aumento della profondità e della latenza della coda indica che la replica si sta avvicinando alla saturazione.
Per prima cosa, create il file ConfigMap che contiene lo script k6 load-test (). script.js Lo script legge il modello di destinazione dalla variabile di MODEL ambiente, che ogni pod imposta in base al MODEL_BUCKET valore acquisito in precedenza:
Esempio test di carico k6 ConfigMap
cat << 'EOF' | kubectl apply -f - apiVersion: v1 kind: ConfigMap metadata: name: vllm-loadtest-script namespace: default data: script.js: | import http from 'k6/http'; // Dictionary of 10-letter words for variation (prevents prompt caching) const words = [ 'revolution', 'technology', 'javascript', 'kubernetes', 'basketball', 'strawberry', 'watermelon', 'blueberries', 'throughout', 'background', 'playground', 'understand', 'everything', 'protection', 'complexity' ]; export const options = { noConnectionReuse: true, scenarios: { constant_request_rate: { executor: 'constant-arrival-rate', rate: __ENV.TARGET_RPS || 10, // Overridden by TARGET_RPS env var timeUnit: '1s', duration: __ENV.DURATION || '60s', // Overridden by DURATION env var preAllocatedVUs: 50, maxVUs: 500, }, }, }; export default function () { const randomWord = words[Math.floor(Math.random() * words.length)]; const payload = JSON.stringify({ model: __ENV.MODEL, prompt: `Starting with the word "${randomWord}", write a brief technical explanation about cloud computing concepts. Cover containerization, orchestration, and scalability in about 80 words.`, max_tokens: 100, temperature: 0.7, }); const params = { headers: { 'Content-Type': 'application/json' }, timeout: '240s' }; http.post('http://vllm-inference-svc:8000/v1/completions', payload, params); } EOF
Quindi esegui il test di carico a ciascuna frequenza di richiesta. Il ciclo seguente invia un Job separato per ogni tariffa:
Esempio Loop di test di carico
for RPS in 10 20 30 40 50 60 70; do echo "=== Testing at ${RPS} req/s ===" cat << EOF | kubectl apply -f - apiVersion: batch/v1 kind: Job metadata: name: vllm-loadtest-${RPS} namespace: default labels: app: vllm-loadtest spec: backoffLimit: 0 template: metadata: labels: app: vllm-loadtest spec: restartPolicy: Never containers: - name: k6 image: grafana/k6:latest env: - name: K6_PROMETHEUS_RW_SERVER_URL value: "http://kube-prometheus-stack-prometheus.monitoring.svc.cluster.local:9090/api/v1/write" - name: MODEL value: "s3://${MODEL_BUCKET}/Ministral-3-8B-Instruct-2512/" - name: TARGET_RPS value: "${RPS}" - name: DURATION value: "60s" args: ["run", "-o", "experimental-prometheus-rw", "/scripts/script.js"] volumeMounts: - name: script mountPath: /scripts volumes: - name: script configMap: name: vllm-loadtest-script EOF kubectl wait --for=condition=complete job/vllm-loadtest-${RPS} --timeout=180s sleep 30 done
Guarda le metriche in Grafana mentre il test viene eseguito (vedi Passaggio 3). Ogni tasso crea il proprio Job (vllm-loadtest-10throughvllm-loadtest-70), in modo da poter controllare ogni run con. kubectl get jobs Quando hai finito, elimina tutti i job di load-test:
kubectl delete jobs -l app=vllm-loadtest
Fase 3: Misura le metriche sotto carico
Mentre il carico viene eseguito, osserva le due metriche che determinano la scalabilità automatica:
-
Profondità della coda (
vllm:num_requests_waiting): quante richieste sono in attesa di essere elaborate. -
latenza end-to-end p95 (
vllm:e2e_request_latency_seconds): il tempo di risposta del 95° percentile.
Visualizza le metriche in Grafana
Apri Grafana (apri il nome host del load balancer e accedi con nomeadmin; vedi Access Grafana per i dettagli) e vai a Dashboards > GPU Monitoring > Performance Testing - VLLM Load Analysis. Questa dashboard è basata su Amazon Managed Service for Prometheus, quindi i pannelli possono ritardare lo stato attivo fino a un minuto perché le metriche vengono scritte in remoto in batch.
Test delle prestazioni: dashboard di analisi del carico di vLLM durante il test di carico
In base ai risultati del test di carico, la profondità della coda è rimasta prossima allo zero su 50 richieste simultanee. È poi aumentata notevolmente con 60 richieste simultanee, con un picco compreso tra circa 150 e 350 richieste in coda. Allo stesso tempo, la latenza end-to-end di p95 è passata da una linea di base costante di circa 2,5 secondi a 6-10 secondi. Questo comportamento indica l'insorgenza di un sovraccarico prolungato e suggerisce che la scalabilità automatica dovrebbe iniziare prima che la profondità della coda raggiunga livelli che fanno sì che la latenza si avvicini ai 10 secondi. Un punto di partenza pratico è attivare il ridimensionamento quando la profondità della coda supera le 25 richieste per 30-60 secondi. Come segnale secondario, è possibile attivare il ridimensionamento anche quando la latenza end-to-end di p95 supera i 5 secondi nello stesso intervallo. Questo aiuta a tenere conto dei modelli di carico di lavoro in cui la latenza aumenta prima che la profondità della coda aumenti.
Fase 4: Scegli le metriche e le soglie su cui scalare
Utilizzando le metriche osservate nel punto di saturazione, determini le soglie utilizzate per configurare la scalabilità automatica nella sezione successiva.
-
Profondità della coda (
vllm:num_requests_waiting): il numero di richieste in attesa di essere elaborate una volta che una replica è saturata. Imposta la soglia al di sopra del punto in cui la coda rimane positiva per la prima volta, in modo che i picchi transitori non inneschino il ridimensionamento. Per il test di carico in questa sezione, la profondità della coda è rimasta prossima allo zero fino a 50 richieste simultanee ed è aumentata notevolmente fino a circa 150-350 richieste in coda su 60 richieste simultanee. Un pratico punto di partenza è attivare il ridimensionamento quando la profondità della coda supera le 25 richieste per 30-60 secondi. -
Latenza (
vllm:e2e_request_latency_seconds): il tempo di risposta end-to-end quando una replica si avvicina al limite di capacità. Imposta la soglia al di sotto del tuo SLO di latenza, in modo che l'autoscaler si ridimensioni prima che la latenza visibile all'utente diventi inaccettabile. Per il test di carico in questa sezione, la latenza end-to-end di p95 è rimasta stabile a circa 2,5 secondi ed è aumentata a 6-10 secondi a saturazione. Come segnale secondario, un pratico punto di partenza è attivare il ridimensionamento quando la latenza end-to-end di p95 supera i 5 secondi per 30-60 secondi. Questo aiuta a tenere conto dei modelli di carico di lavoro in cui la latenza aumenta prima che la profondità della coda aumenti o quando la latenza aumenta senza code. -
Scale-down— la velocità con cui l'autoscaler rimuove le repliche dopo un calo della domanda. Riduci più lentamente di quanto aumenti. Per questo esempio, riduci la scala solo quando la profondità della coda è
0e la latenza end-to-end di p95 è inferiore a 3 secondi per 5 minuti. Ciò impedisce all'autoscaler di rimuovere le repliche durante brevi pause nel traffico e aiuta a evitare le oscillazioni.
Questi valori forniscono un punto di partenza per la configurazione della scalabilità automatica nella sezione successiva. Ripeti questa procedura per il tuo modello, il tipo di GPU e i modelli di richiesta per determinare le soglie appropriate per la tua distribuzione.