Aidez à améliorer cette page
Les traductions sont fournies par des outils de traduction automatique. En cas de conflit entre le contenu d'une traduction et celui de la version originale en anglais, la version anglaise prévaudra.
Pour contribuer à ce guide de l'utilisateur, cliquez sur le GitHub lien Modifier cette page qui se trouve dans le volet droit de chaque page.
Les traductions sont fournies par des outils de traduction automatique. En cas de conflit entre le contenu d'une traduction et celui de la version originale en anglais, la version anglaise prévaudra.
Identifier les seuils des métriques de mise à l'échelle pour l'inférence de l'IA
Astuce
Inscrivez-vous
Cette section explique comment tester la charge d'un serveur d'inférence vLLM sur Amazon EKS afin d'identifier le moment où il devient saturé. Utilisez les résultats des tests de charge pour configurer la profondeur de la file d'attente et les seuils de latence pour la mise à l'échelle automatique horizontale dans la Inférence basée sur l'IA à l'échelle automatique avec HPA et KEDA section suivante.
La procédure pas à pas utilise les outils suivants :
-
k6
(Grafana k6) est un outil de test de charge open source, exécuté en tant qu'image du grafana/k6conteneur, qui envoie des demandes d'inférence à la réplique à des taux de demandes contrôlés. -
Ministral-3-8B-Instruct-2512 modèle
sur une g6e.4xlargeinstance. Vous pouvez appliquer la même méthodologie à n'importe quel modèle open source et à n'importe quel type de GPU.
Conditions préalables
Cette section s'appuie sur deux sections précédentes de la documentation. Complétez les deux avant de commencer :
-
Cluster Setup fournit un cluster EKS avec un GPU NodePool, la pile de surveillance kube-prometheus-stack (Prometheus et Grafana) et un compartiment Amazon S3 pour les pondérations des modèles. Le modèle pèse le godet S3
-
Load & Serve Models déploie le serveur d'inférence vLLM et crée le. vllm-inference-app ServiceMonitor
Confirmez chaque élément ci-dessous avant de commencer, et complétez d'abord la section liée en cas d'échec de la vérification.
Si vous avez ouvert un nouveau terminal, définissez le nom du cluster et la région que vous avez utilisés précédemment :
export CLUSTER_NAME=ai-eks-docs export AWS_REGION=us-east-2
Capturez le nom du compartiment de modèle Amazon S3 qui a été créé lors des étapes de configuration du cluster. Le générateur de charge transmet cette valeur à VLLm.
MODEL_BUCKET=$(aws s3api list-buckets \ --query "Buckets[?starts_with(Name, '${CLUSTER_NAME}-models-')].Name | [0]" \ --output text) echo "Model bucket: ${MODEL_BUCKET}"
Vérifiez que la pile de surveillance est en cours d'exécution
La pile kube-prometheus de la configuration de surveillance s'exécute dans l'espace de noms. monitoring
kubectl get pods -n monitoring
Les capsules Prometheus et Grafana doivent être : Running
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
S'ils sont absents, terminez la configuration de la surveillance avant de continuer.
Vérifiez que le modèle vLLM est en cours d'exécution
Le vllm-inference-app déploiement et le vllm-inference-svc service de Load & Serve Models s'exécutent dans l'defaultespace de noms.
kubectl get deployment vllm-inference-app
Le déploiement devrait être 1/1 prêt pour le rapport :
NAME READY UP-TO-DATE AVAILABLE AGE vllm-inference-app 1/1 1 1 3h
Si le déploiement est manquant ou n'est pas prêt, complétez les modèles Load & Serve avant de continuer.
Accédez à Grafana
Vous regardez le test de charge dans le tableau de bord vLLM préchargé. Assurez-vous donc que l'équilibreur de charge Grafana que vous avez configuré dans la section Access Grafana est accessible. Imprimez son URL :
echo "http://$(kubectl get ingress kube-prometheus-stack-grafana -n monitoring -o jsonpath='{.status.loadBalancer.ingress[0].hostname}')"
Ouvrez l'URL dans votre navigateur et connectez-vous avec le nom d'utilisateur admin et le mot de passe à l'aide de la commande suivante :
kubectl --namespace monitoring get secrets kube-prometheus-stack-grafana -o jsonpath="{.data.admin-password}" | base64 -d ; echo
Confirmez l'existence du vLLM ServiceMonitor
Le vllm-inference-app ServiceMonitor, créé à l'étape Monitor vLLM, s'exécute dans l'defaultespace de noms et indique à Prometheus où extraire les métriques vLLM.
kubectl get servicemonitor vllm-inference-app
Sortie attendue :
NAME AGE vllm-inference-app 3h
S'il est absent, terminez l'étape Monitor vLLM.
Étape 1 : préchauffez le GPU
Même si le modèle est chargé dans la mémoire du GPU et que la réplique indique Ready, les premières requêtes prennent plus de temps que les suivantes, car vLLM et le GPU terminent leur initialisation dès les premières inférences. vLLM capture les graphes CUDA, compile et atotalise les noyaux GPU, et alloue ses pools de mémoire cache KV, tandis que le GPU augmente sa vitesse d'horloge de repos à sa plage de boost.
Il s'agit de coûts ponctuels. Par conséquent, la mesure des seuils avant l'échauffement enregistre le démarrage des enregistrements plutôt que de procéder à un service en régime permanent. L'échauffement suivant envoie 100 demandes séquentielles pour terminer cette initialisation, de sorte que le test de charge qui suit mesure la capacité en régime permanent.
Commencez par créer le script ConfigMap contenant le script d'échauffement (warmup.js). Le script lit le modèle cible à partir de la variable d'MODELenvironnement, que le Job définit à partir de la MODEL_BUCKET valeur que vous avez capturée précédemment :
Exempleéchauffement 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
Exécutez ensuite le job d'échauffement. Un seul client envoie 100 requêtes séquentielles :
Exemple Warm-up Poste
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
Attendez la fin de l'échauffement (cela prend quelques minutes) :
kubectl wait --for=condition=complete job/vllm-warmup --timeout=600s
Ouvrez Grafana (ouvrez le nom d'hôte de l'équilibreur de charge ; voir Access Grafana pour plus de détails) et accédez à Tableaux de bord > Surveillance du GPU > Tests de performance - Analyse de charge vLLM. Le panneau VLLM Request Rate affiche la rafale de trafic de préchauffage. Le panneau vLLM Average Latency indique que la première demande est plus lente, ce qui correspond au coût unique de préchauffage du GPU.
Tableau de bord vLLM montrant l'effet d'échauffement du GPU sur la latence
Supprimez ensuite la tâche de préchauffage :
kubectl delete job vllm-warmup
Étape 2 : envoyer du chargement à des taux de demande croissants
Au cours de cette étape, vous exécutez un test de charge qui envoie des requêtes au réplica vLLM unique à des taux croissants : 10, 20, 30, 40, 50, 60 et 70 requêtes par seconde. Cela simule une demande croissante des utilisateurs. Le test utilise k6 dans un modèle à taux d'arrivée constant, où la variable d'TARGET_RPSenvironnement définit le taux de demande cible. Chaque taux est exécuté pendant 60 secondes pour permettre aux indicateurs de se stabiliser, suivi d'une pause de 30 secondes avant la prochaine augmentation de taux.
L'objectif de ce test est d'identifier le point de saturation, c'est-à-dire le taux de demandes auquel une seule réplique ne peut plus suivre les demandes entrantes. Vous surveillez la profondeur de la file d'attente (vllm:num_requests_waiting) et la latence de bout en bout (vllm:e2e_request_latency_seconds). L'augmentation de la profondeur et de la latence de la file d'attente indique que la réplique approche de la saturation.
Tout d'abord, créez le script ConfigMap qui contient le script de test de chargement k6 (). script.js Le script lit le modèle cible à partir de la variable d'MODELenvironnement, que chaque pod définit à partir de la MODEL_BUCKET valeur que vous avez capturée précédemment :
Exemple essai de charge 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', 'blackberry', '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
Exécutez ensuite le test de charge à chaque taux de demande. La boucle suivante soumet un Job distinct pour chaque tarif :
Exemple Boucle de test de charge
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
Observez les statistiques dans Grafana pendant l'exécution du test (voir Étape 3). Chaque tarif crée sa propre tâche (vllm-loadtest-10à traversvllm-loadtest-70), ce qui vous permet d'inspecter n'importe quelle course aveckubectl get jobs. Lorsque vous avez terminé, supprimez toutes les tâches de test de charge :
kubectl delete jobs -l app=vllm-loadtest
Étape 3 : Mesurer les métriques sous charge
Pendant l'exécution du chargement, observez les deux indicateurs qui pilotent la mise à l'échelle automatique :
-
Profondeur de la file d'attente (
vllm:num_requests_waiting) : nombre de demandes en attente de traitement. -
Latence de bout en bout p95 (
vllm:e2e_request_latency_seconds) : temps de réponse du 95e centile.
Afficher les statistiques dans Grafana
Ouvrez Grafana (ouvrez le nom d'hôte de l'équilibreur de charge et connectez-vous en tant que tel admin ; voir Access Grafana pour plus de détails) et accédez à Tableaux de bord > Surveillance du GPU > Tests de performance - Analyse de charge vLLM. Ce tableau de bord est basé sur Amazon Managed Service pour Prometheus. Les panneaux peuvent donc être retardés par rapport à l'état réel jusqu'à une minute, car les métriques sont écrites à distance par lots.
Tests de performance - Tableau de bord d'analyse de charge vLLM pendant le test de charge
Sur la base des résultats des tests de charge, la profondeur de la file d'attente est restée proche de zéro malgré 50 demandes simultanées. Il a ensuite fortement augmenté avec 60 demandes simultanées, avec un pic entre 150 et 350 demandes en file d'attente environ. Dans le même temps, la latence de bout en bout de p95 est passée d'une valeur de référence stable d'environ 2,5 secondes à 6 à 10 secondes. Ce comportement indique le début d'une surcharge prolongée et suggère que la mise à l'échelle automatique devrait commencer avant que la profondeur de la file d'attente n'atteigne des niveaux entraînant une latence d'environ 10 secondes. Un point de départ pratique consiste à déclencher la mise à l'échelle lorsque la profondeur de la file d'attente dépasse 25 demandes pendant 30 à 60 secondes. En tant que signal secondaire, vous pouvez également déclencher la mise à l'échelle lorsque la latence de bout en bout de p95 dépasse 5 secondes sur le même intervalle. Cela permet de prendre en compte les modèles de charge de travail dans lesquels la latence augmente avant que la profondeur de la file d'attente n'augmente.
Étape 4 : Choisissez les indicateurs et les seuils sur lesquels vous souhaitez évoluer
À l'aide des mesures observées au point de saturation, vous déterminez les seuils utilisés pour configurer la mise à l'échelle automatique dans la section suivante.
-
Profondeur de la file d'attente (
vllm:num_requests_waiting) : nombre de demandes en attente de traitement une fois qu'une réplique est saturée. Définissez le seuil au-dessus du point où la file d'attente reste positive pour la première fois, afin que les pics transitoires ne déclenchent pas la mise à l'échelle. Pour le test de charge de cette section, la profondeur de la file d'attente est restée proche de zéro pendant 50 demandes simultanées et a fortement augmenté pour atteindre environ 150 à 350 demandes en file d'attente sur 60 demandes simultanées. Un point de départ pratique consiste à déclencher la mise à l'échelle lorsque la profondeur de la file d'attente dépasse 25 demandes pendant 30 à 60 secondes. -
Latence (
vllm:e2e_request_latency_seconds) : temps de réponse de bout en bout lorsqu'une réplique approche de sa limite de capacité. Définissez le seuil inférieur à votre SLO de latence, afin que l'autoscaler évolue avant que la latence visible par l'utilisateur ne devienne inacceptable. Pour le test de charge décrit dans cette section, la latence de bout en bout de p95 est restée stable à environ 2,5 secondes et a augmenté à 6 à 10 secondes à saturation. En tant que signal secondaire, un point de départ pratique consiste à déclencher la mise à l'échelle lorsque la latence de bout en bout de p95 dépasse 5 secondes pendant 30 à 60 secondes. Cela permet de prendre en compte les modèles de charge de travail dans lesquels la latence augmente avant que la profondeur de la file d'attente n'augmente ou lorsque la latence augmente sans mise en file d'attente. -
Scale-down— la vitesse à laquelle l'autoscaler supprime les répliques en cas de baisse de la demande. Réduisez la taille plus lentement que vous ne l'augmentez. Pour cet exemple, réduisez la taille uniquement lorsque la profondeur de la file d'attente est faible
0et que la latence de bout en bout de p95 est inférieure à 3 secondes pendant 5 minutes. Cela empêche l'autoscaler de supprimer les répliques pendant de courtes pauses dans le trafic et permet d'éviter les oscillations.
Ces valeurs constituent un point de départ pour configurer la mise à l'échelle automatique dans la section suivante. Répétez ce processus pour votre propre modèle, votre type de GPU et vos modèles de demande afin de déterminer les seuils appropriés pour votre déploiement.