Diffuser des LLM tels que DeepSeek-R1 671B ou Llama 3.1 405B sur GKE


Présentation

Ce guide vous explique comment diffuser des grands modèles de langage (LLM) de pointe tels que DeepSeek-R1 671B ou Llama 3.1 405B sur Google Kubernetes Engine (GKE) à l'aide de processeurs graphiques (GPU) sur plusieurs nœuds.

Ce guide explique comment utiliser des technologies Open Source portables (Kubernetes, vLLM et l'API LeaderWorkerSet (LWS)) pour déployer et diffuser des charges de travail d'IA/ML sur GKE, en profitant du contrôle précis, de l'évolutivité, de la résilience, de la portabilité et de la rentabilité de GKE.

Avant de lire cette page, assurez-vous de connaître les éléments suivants:

Contexte

Cette section décrit les principales technologies utilisées dans ce guide, y compris les deux LLM utilisés comme exemples dans ce guide : DeepSeek-R1 et Llama 3.1 405B.

DeepSeek-R1

DeepSeek-R1, un grand modèle de langage de 671 milliards de paramètres conçu par DeepSeek, est conçu pour l'inférence logique, le raisonnement mathématique et la résolution de problèmes en temps réel dans diverses tâches basées sur du texte. GKE gère les demandes de calcul de DeepSeek-R1, en exploitant ses capacités avec des ressources évolutives, le calcul distribué et un réseau efficace.

Pour en savoir plus, consultez la documentation sur DeepSeek.

Llama 3.1 405B

Llama 3.1 405 milliards est un grand modèle de langage de Meta conçu pour un large éventail de tâches de traitement du langage naturel, y compris la génération de texte, la traduction et les systèmes de questions-réponses. GKE offre l'infrastructure robuste requise pour répondre aux besoins d'entraînement et de diffusion distribués des modèles de cette envergure.

Pour en savoir plus, consultez la documentation Llama.

Service Kubernetes géré GKE

Google Cloud propose une large gamme de services, y compris GKE, qui est particulièrement adapté au déploiement et à la gestion des charges de travail d'IA/ML. GKE est un service Kubernetes géré qui simplifie le déploiement, la mise à l'échelle et la gestion des applications conteneurisées. GKE fournit l'infrastructure nécessaire, y compris des ressources évolutives, un calcul distribué et un réseau efficace, pour gérer les demandes de calcul des LLM.

Pour en savoir plus sur les concepts clés de Kubernetes, consultez Commencer à découvrir Kubernetes. Pour en savoir plus sur GKE et sur la façon dont il vous aide à faire évoluer, automatiser et gérer Kubernetes, consultez la présentation de GKE.

GPU

Les processeurs graphiques (GPU) vous permettent d'accélérer des charges de travail spécifiques telles que le machine learning et le traitement de données. GKE propose des nœuds équipés de ces GPU puissants, ce qui vous permet de configurer votre cluster pour des performances optimales dans les tâches de machine learning et de traitement des données. GKE fournit toute une gamme d'options de types de machines pour la configuration des nœuds, y compris les types de machines avec des GPU NVIDIA H100, L4 et A100.

Pour en savoir plus, consultez la page À propos des GPU dans GKE.

LeaderWorkerSet (LWS)

LeaderWorkerSet (LWS) est une API de déploiement Kubernetes qui traite des modèles de déploiement courants des charges de travail d'inférence multi-nœuds d'IA/ML. La diffusion multi-nœud s'appuie sur plusieurs pods, chacun pouvant s'exécuter sur un nœud différent, pour gérer la charge de travail d'inférence distribuée. LWS permet de traiter plusieurs pods en tant que groupe, ce qui simplifie la gestion de l'inférence de modèles distribués.

vLLM et diffusion multi-hôte

Lorsque vous diffusez des LLM intensives en calcul, nous vous recommandons d'utiliser vLLM et d'exécuter les charges de travail sur plusieurs GPU.

vLLM est un framework de diffusion LLM Open Source hautement optimisé qui peut augmenter le débit de diffusion sur les GPU, avec des fonctionnalités telles que:

  • Implémentation optimisée du transformateur avec PagedAttention
  • Traitement par lots continu pour améliorer le débit global de diffusion
  • Diffusion distribuée sur plusieurs GPU

Avec des LLM particulièrement gourmands en calcul qui ne peuvent pas tenir dans un seul nœud GPU, vous pouvez utiliser plusieurs nœuds GPU pour diffuser le modèle. vLLM prend en charge l'exécution de charges de travail sur plusieurs GPU à l'aide de deux stratégies:

  • Le parallélisme Tensor répartit les multiplications de matrices dans la couche de transformation sur plusieurs GPU. Toutefois, cette stratégie nécessite un réseau rapide en raison de la communication nécessaire entre les GPU, ce qui la rend moins adaptée à l'exécution de charges de travail sur plusieurs nœuds.

  • Le parallélisme de pipeline divise le modèle par couche, ou verticalement. Cette stratégie ne nécessite pas de communication constante entre les GPU, ce qui en fait une meilleure option pour exécuter des modèles sur plusieurs nœuds.

Vous pouvez utiliser les deux stratégies dans la diffusion multinœud. Par exemple, si vous utilisez deux nœuds avec huit GPU H100 chacun, vous pouvez utiliser les deux stratégies:

  • Parallélisme de pipeline bidirectionnel pour fractionner le modèle sur les deux nœuds
  • Parallélisme tensoriel à huit voies pour fractionner le modèle sur les huit GPU de chaque nœud

Pour en savoir plus, consultez la documentation de vLLM.

Objectifs

  1. Préparez votre environnement avec un cluster GKE en mode Autopilot ou Standard.
  2. Déployez vLLM sur plusieurs nœuds de votre cluster.
  3. Utilisez vLLM pour diffuser le modèle via curl.

Avant de commencer

  • Sign in to your Google Cloud account. If you're new to Google Cloud, create an account to evaluate how our products perform in real-world scenarios. New customers also get $300 in free credits to run, test, and deploy workloads.
  • In the Google Cloud console, on the project selector page, select or create a Google Cloud project.

    Go to project selector

  • Make sure that billing is enabled for your Google Cloud project.

  • Enable the required API.

    Enable the API

  • Make sure that you have the following role or roles on the project: roles/container.admin, roles/iam.serviceAccountAdmin, roles/iam.securityAdmin

    1. In the Google Cloud console, go to the IAM page.

      Go to IAM
    2. Select the project.
    3. In the Principal column, find all rows that identify you or a group that you're included in. To learn which groups you're included in, contact your administrator.

    4. For all rows that specify or include you, check the Role column to see whether the list of roles includes the required roles.
    1. In the Google Cloud console, go to the IAM page.

      Accéder à IAM
    2. Sélectionnez le projet.
    3. Cliquez sur Accorder l'accès.
    4. Dans le champ Nouveaux comptes principaux, saisissez votre identifiant utilisateur. Il s'agit généralement de l'adresse e-mail d'un compte Google.

    5. Dans la liste Sélectionner un rôle, sélectionnez un rôle.
    6. Pour attribuer des rôles supplémentaires, cliquez sur Ajouter un autre rôle et ajoutez chaque rôle supplémentaire.
    7. Cliquez sur Enregistrer.

Accéder au modèle

Vous pouvez utiliser les modèles Llama 3.1 405B ou DeepSeek-R1.

.

Générer un jeton d'accès

Si vous n'en possédez pas déjà un, générez un nouveau jeton Hugging Face:

  1. Cliquez sur Your Profile > Settings > Access Tokens (Votre profil > Paramètres > Jetons d'accès).
  2. Sélectionnez New Token (Nouveau jeton).
  3. Spécifiez le nom de votre choix et un rôle d'au moins Read.
  4. Sélectionnez Générer un jeton.
.

Générer un jeton d'accès

Si vous n'en possédez pas déjà un, générez un nouveau jeton Hugging Face:

  1. Cliquez sur Your Profile > Settings > Access Tokens (Votre profil > Paramètres > Jetons d'accès).
  2. Sélectionnez New Token (Nouveau jeton).
  3. Spécifiez le nom de votre choix et un rôle d'au moins Read.
  4. Sélectionnez Générer un jeton.

Préparer l'environnement

Dans ce tutoriel, vous utilisez Cloud Shell pour gérer les ressources hébergées surGoogle Cloud. Cloud Shell est préinstallé avec les logiciels dont vous avez besoin pour ce tutoriel, y compris kubectl et gcloud CLI.

Pour configurer votre environnement avec Cloud Shell, procédez comme suit :

  1. Dans la console Google Cloud, lancez une session Cloud Shell en cliquant sur Icône d'activation Cloud Shell Activer Cloud Shell dans la console Google Cloud. Une session s'ouvre dans le volet inférieur de la console Google Cloud.

  2. Définissez les variables d'environnement par défaut :

    gcloud config set project PROJECT_ID
    export PROJECT_ID=$(gcloud config get project)
    export CLUSTER_NAME=CLUSTER_NAME
    export ZONE=ZONE
    export HF_TOKEN=HUGGING_FACE_TOKEN
    export IMAGE_NAME=REGION_NAME-docker.pkg.dev/PROJECT_ID/vllm-multihost/vllm-multihost:latest
    

    Remplacez les valeurs suivantes :

    • PROJECT_ID: ID de votre projet. Google Cloud
    • CLUSTER_NAME : nom de votre cluster GKE.
    • ZONE: zone compatible avec les GPU NVIDIA H100 Tensor Core.
    • IMAGE_NAME: image vLLM incluant le script Ray.

Créer un cluster GKE

Vous pouvez diffuser des modèles à l'aide de vLLM sur plusieurs nœuds GPU dans un cluster GKE Autopilot ou Standard. Nous vous recommandons d'utiliser un cluster Autopilot pour une expérience Kubernetes entièrement gérée. Pour choisir le mode de fonctionnement GKE le mieux adapté à vos charges de travail, consultez la section Choisir un mode de fonctionnement GKE.

Dans Cloud Shell, exécutez la commande suivante :

  gcloud container clusters create-auto ${CLUSTER_NAME} \
    --project=${PROJECT_ID} \
    --region=${REGION} \
    --cluster-version=${CLUSTER_VERSION}
  1. Créez un cluster GKE Standard avec deux nœuds de processeur:

    gcloud container clusters create CLUSTER_NAME \
        --project=PROJECT_ID \
        --num-nodes=2 \
        --location=ZONE \
        --machine-type=e2-standard-16
    
  2. Créez un pool de nœuds A3 avec deux nœuds, chacun avec huit GPU H100:

    gcloud container node-pools create gpu-nodepool \
        --location=ZONE \
        --num-nodes=2 \
        --machine-type=a3-highgpu-8g \
        --accelerator=type=nvidia-h100-80gb,count=8,gpu-driver-version=LATEST \
        --placement-type=COMPACT \
        --cluster=CLUSTER_NAME
    

Configurez kubectl pour communiquer avec votre cluster

Configurez kubectl pour communiquer avec votre cluster à l'aide de la commande suivante:

gcloud container clusters get-credentials CLUSTER_NAME --location=ZONE

Créer un secret Kubernetes pour les identifiants Hugging Face

Créez un secret Kubernetes contenant le jeton Hugging Face à l'aide de la commande suivante:

kubectl create secret generic hf-secret \
  --from-literal=hf_api_token=${HF_TOKEN} \
  --dry-run=client -o yaml | kubectl apply -f -

Créer votre propre image multi-nœud vLLM

Pour faciliter la communication entre les nœuds pour vLLM, vous pouvez utiliser Ray. Le dépôt LeaderWorkerSet fournit un Dockerfile, qui inclut un script bash permettant de configurer Ray avec vLLM.

Pour créer votre propre image multi-nœud vLLM, vous devez cloner le dépôt LeaderWorkerSet, créer une image Docker à l'aide du fichier Dockerfile fourni (qui configure Ray pour la communication entre nœuds), puis transférer cette image vers Artifact Registry pour la déployer sur GKE.

Créer le conteneur

Pour créer le conteneur, procédez comme suit:

  1. Clonez le dépôt LeaderWorkerSet:

    git clone https://github.com/kubernetes-sigs/lws.git
    
  2. Créer l'image.

    cd lws/docs/examples/vllm/build/ && docker build -f Dockerfile.GPU . -t vllm-multihost
    

Transfert de l'image vers Artifact Registry

Pour vous assurer que votre déploiement Kubernetes peut accéder à l'image, stockez-la dans Artifact Registry dans votre Google Cloud projet.

gcloud artifacts repositories create vllm-multihost --repository-format=docker --location=REGION_NAME && \
gcloud auth configure-docker REGION_NAME-docker.pkg.dev && \
docker image tag vllm-multihost REGION_NAME-docker.pkg.dev/PROJECT_ID/vllm-multihost/vllm-multihost:latest && \
docker push REGION_NAME-docker.pkg.dev/PROJECT_ID/vllm-multihost/vllm-multihost:latest

Installer LeaderWorkerSet

Pour installer LWS, exécutez la commande suivante:

kubectl apply --server-side -f https://github.com/kubernetes-sigs/lws/releases/latest/download/manifests.yaml

Vérifiez que le contrôleur LeaderWorkerSet s'exécute dans l'espace de noms lws-system à l'aide de la commande suivante:

kubectl get pod -n lws-system

Le résultat ressemble à ce qui suit :

NAME                                      READY   STATUS    RESTARTS   AGE
lws-controller-manager-5c4ff67cbd-9jsfc   2/2     Running   0          6d23h

Déployer le serveur de modèles vLLM

Pour déployer le serveur de modèle vLLM, procédez comme suit:

  1. Appliquez le fichier manifeste en fonction du LLM que vous souhaitez déployer.

    1. Inspectez le fichier manifeste vllm-deepseek-r1-A3.yaml.

      
      apiVersion: leaderworkerset.x-k8s.io/v1
      kind: LeaderWorkerSet
      metadata:
        name: vllm
      spec:
        replicas: 1
        leaderWorkerTemplate:
          size: 2
          restartPolicy: RecreateGroupOnPodRestart
          leaderTemplate:
            metadata:
              labels:
                role: leader
            spec:
              nodeSelector:
                cloud.google.com/gke-accelerator: nvidia-h100-80gb
              containers:
                - name: vllm-leader
                  image: IMAGE_NAME
                  env:
                    - name: HUGGING_FACE_HUB_TOKEN
                      valueFrom:
                        secretKeyRef:
                          name: hf-secret
                          key: hf_api_token
                  command:
                    - sh
                    - -c
                    - "/vllm-workspace/ray_init.sh leader --ray_cluster_size=$(LWS_GROUP_SIZE); 
                      python3 -m vllm.entrypoints.openai.api_server --port 8080 --model deepseek-ai/DeepSeek-R1 --tensor-parallel-size 8 --pipeline-parallel-size 2 --trust-remote-code --max-model-len 4096"
                  resources:
                    limits:
                      nvidia.com/gpu: "8"
                  ports:
                    - containerPort: 8080
                  readinessProbe:
                    tcpSocket:
                      port: 8080
                    initialDelaySeconds: 15
                    periodSeconds: 10
                  volumeMounts:
                    - mountPath: /dev/shm
                      name: dshm
              volumes:
              - name: dshm
                emptyDir:
                  medium: Memory
                  sizeLimit: 15Gi
          workerTemplate:
            spec:
              nodeSelector:
                cloud.google.com/gke-accelerator: nvidia-h100-80gb
              containers:
                - name: vllm-worker
                  image: IMAGE_NAME
                  command:
                    - sh
                    - -c
                    - "/vllm-workspace/ray_init.sh worker --ray_address=$(LWS_LEADER_ADDRESS)"
                  resources:
                    limits:
                      nvidia.com/gpu: "8"
                  env:
                    - name: HUGGING_FACE_HUB_TOKEN
                      valueFrom:
                        secretKeyRef:
                          name: hf-secret
                          key: hf_api_token
                  volumeMounts:
                    - mountPath: /dev/shm
                      name: dshm   
              volumes:
              - name: dshm
                emptyDir:
                  medium: Memory
                  sizeLimit: 15Gi
      ---
      apiVersion: v1
      kind: Service
      metadata:
        name: vllm-leader
      spec:
        ports:
          - name: http
            port: 8080
            protocol: TCP
            targetPort: 8080
        selector:
          leaderworkerset.sigs.k8s.io/name: vllm
          role: leader
        type: ClusterIP
      
    2. Appliquez le fichier manifeste en exécutant la commande suivante:

      kubectl apply -f vllm-deepseek-r1-A3.yaml
      
    1. Inspectez le fichier manifeste vllm-llama3-405b-A3.yaml.

      
      apiVersion: leaderworkerset.x-k8s.io/v1
      kind: LeaderWorkerSet
      metadata:
        name: vllm
      spec:
        replicas: 1
        leaderWorkerTemplate:
          size: 2
          restartPolicy: RecreateGroupOnPodRestart
          leaderTemplate:
            metadata:
              labels:
                role: leader
            spec:
              nodeSelector:
                cloud.google.com/gke-accelerator: nvidia-h100-80gb
              containers:
                - name: vllm-leader
                  image: IMAGE_NAME
                  env:
                    - name: HUGGING_FACE_HUB_TOKEN
                      valueFrom:
                        secretKeyRef:
                          name: hf-secret
                          key: hf_api_token
                  command:
                    - sh
                    - -c
                    - "/vllm-workspace/ray_init.sh leader --ray_cluster_size=$(LWS_GROUP_SIZE); 
                      python3 -m vllm.entrypoints.openai.api_server --port 8080 --model meta-llama/Meta-Llama-3.1-405B-Instruct --tensor-parallel-size 8 --pipeline-parallel-size 2"
                  resources:
                    limits:
                      nvidia.com/gpu: "8"
                  ports:
                    - containerPort: 8080
                  readinessProbe:
                    tcpSocket:
                      port: 8080
                    initialDelaySeconds: 15
                    periodSeconds: 10
                  volumeMounts:
                    - mountPath: /dev/shm
                      name: dshm
              volumes:
              - name: dshm
                emptyDir:
                  medium: Memory
                  sizeLimit: 15Gi
          workerTemplate:
            spec:
              nodeSelector:
                cloud.google.com/gke-accelerator: nvidia-h100-80gb
              containers:
                - name: vllm-worker
                  image: IMAGE_NAME
                  command:
                    - sh
                    - -c
                    - "/vllm-workspace/ray_init.sh worker --ray_address=$(LWS_LEADER_ADDRESS)"
                  resources:
                    limits:
                      nvidia.com/gpu: "8"
                  env:
                    - name: HUGGING_FACE_HUB_TOKEN
                      valueFrom:
                        secretKeyRef:
                          name: hf-secret
                          key: hf_api_token
                  volumeMounts:
                    - mountPath: /dev/shm
                      name: dshm   
              volumes:
              - name: dshm
                emptyDir:
                  medium: Memory
                  sizeLimit: 15Gi
      ---
      apiVersion: v1
      kind: Service
      metadata:
        name: vllm-leader
      spec:
        ports:
          - name: http
            port: 8080
            protocol: TCP
            targetPort: 8080
        selector:
          leaderworkerset.sigs.k8s.io/name: vllm
          role: leader
        type: ClusterIP
      
    2. Appliquez le fichier manifeste en exécutant la commande suivante:

      kubectl apply -f vllm-llama3-405b-A3.yaml
      
  2. Affichez les journaux du serveur de modèle en cours d'exécution à l'aide de la commande suivante:

    kubectl logs vllm-0 -c vllm-leader
    

    Le résultat doit ressembler à ce qui suit :

    INFO 08-09 21:01:34 api_server.py:297] Route: /detokenize, Methods: POST
    INFO 08-09 21:01:34 api_server.py:297] Route: /v1/models, Methods: GET
    INFO 08-09 21:01:34 api_server.py:297] Route: /version, Methods: GET
    INFO 08-09 21:01:34 api_server.py:297] Route: /v1/chat/completions, Methods: POST
    INFO 08-09 21:01:34 api_server.py:297] Route: /v1/completions, Methods: POST
    INFO 08-09 21:01:34 api_server.py:297] Route: /v1/embeddings, Methods: POST
    INFO:     Started server process [7428]
    INFO:     Waiting for application startup.
    INFO:     Application startup complete.
    INFO:     Uvicorn running on http://0.0.0.0:8080 (Press CTRL+C to quit)
    

Diffuser le modèle

Configurez le transfert de port sur le modèle en exécutant la commande suivante:

kubectl port-forward svc/vllm-leader 8080:8080

Interagir avec le modèle à l'aide de curl

Pour interagir avec le modèle à l'aide de curl, procédez comme suit:

Dans un nouveau terminal, envoyez une requête au serveur:

curl http://localhost:8080/v1/completions \
-H "Content-Type: application/json" \
-d '{
    "model": "deepseek-ai/DeepSeek-R1",
    "prompt": "I have four boxes. I put the red box on the bottom and put the blue box on top. Then I put the yellow box on top the blue. Then I take the blue box out and put it on top. And finally I put the green box on the top. Give me the final order of the boxes from bottom to top. Show your reasoning but be brief",
    "max_tokens": 1024,
    "temperature": 0
}'

La sortie devrait ressembler à ce qui suit :

{
"id": "cmpl-f2222b5589d947419f59f6e9fe24c5bd",
"object": "text_completion",
"created": 1738269669,
"model": "deepseek-ai/DeepSeek-R1",
"choices": [
  {
    "index": 0,
    "text": ".\n\nOkay, let's see. The user has four boxes and is moving them around. Let me try to visualize each step. \n\nFirst, the red box is placed on the bottom. So the stack starts with red. Then the blue box is put on top of red. Now the order is red (bottom), blue. Next, the yellow box is added on top of blue. So now it's red, blue, yellow. \n\nThen the user takes the blue box out. Wait, blue is in the middle. If they remove blue, the stack would be red and yellow. But where do they put the blue box? The instruction says to put it on top. So after removing blue, the stack is red, yellow. Then blue is placed on top, making it red, yellow, blue. \n\nFinally, the green box is added on the top. So the final order should be red (bottom), yellow, blue, green. Let me double-check each step to make sure I didn't mix up any steps. Starting with red, then blue, then yellow. Remove blue from the middle, so yellow is now on top of red. Then place blue on top of that, so red, yellow, blue. Then green on top. Yes, that seems right. The key step is removing the blue box from the middle, which leaves yellow on red, then blue goes back on top, followed by green. So the final order from bottom to top is red, yellow, blue, green.\n\n**Final Answer**\nThe final order from bottom to top is \\boxed{red}, \\boxed{yellow}, \\boxed{blue}, \\boxed{green}.\n</think>\n\n1. Start with the red box at the bottom.\n2. Place the blue box on top of the red box. Order: red (bottom), blue.\n3. Place the yellow box on top of the blue box. Order: red, blue, yellow.\n4. Remove the blue box (from the middle) and place it on top. Order: red, yellow, blue.\n5. Place the green box on top. Final order: red, yellow, blue, green.\n\n\\boxed{red}, \\boxed{yellow}, \\boxed{blue}, \\boxed{green}",
    "logprobs": null,
    "finish_reason": "stop",
    "stop_reason": null,
    "prompt_logprobs": null
  }
],
"usage": {
  "prompt_tokens": 76,
  "total_tokens": 544,
  "completion_tokens": 468,
  "prompt_tokens_details": null
}
}

Dans un nouveau terminal, envoyez une requête au serveur:

curl http://localhost:8080/v1/completions \
-H "Content-Type: application/json" \
-d '{
    "model": "meta-llama/Meta-Llama-3.1-405B-Instruct",
    "prompt": "San Francisco is a",
    "max_tokens": 7,
    "temperature": 0
}'

La sortie devrait ressembler à ce qui suit :

{"id":"cmpl-0a2310f30ac3454aa7f2c5bb6a292e6c",
"object":"text_completion","created":1723238375,"model":"meta-llama/Meta-Llama-3.1-405B-Instruct","choices":[{"index":0,"text":" top destination for foodies, with","logprobs":null,"finish_reason":"length","stop_reason":null}],"usage":{"prompt_tokens":5,"total_tokens":12,"completion_tokens":7}}

Configurer l'autoscaler personnalisé

Dans cette section, vous allez configurer l'autoscaling horizontal des pods pour utiliser des métriques Prometheus personnalisées. Vous utilisez les métriques Google Cloud Managed Service pour Prometheus à partir du serveur vLLM.

Pour en savoir plus, consultez la section Google Cloud Managed Service pour Prometheus. Cette option doit être activée par défaut sur le cluster GKE.

  1. Configurez l'adaptateur de métriques personnalisées Stackdriver sur votre cluster:

    kubectl apply -f https://raw.githubusercontent.com/GoogleCloudPlatform/k8s-stackdriver/master/custom-metrics-stackdriver-adapter/deploy/production/adapter_new_resource_model.yaml
    
  2. Ajoutez le rôle "Lecteur de surveillance" au compte de service utilisé par l'adaptateur de métriques personnalisées Stackdriver:

    gcloud projects add-iam-policy-binding projects/PROJECT_ID \
        --role roles/monitoring.viewer \
        --member=principal://iam.googleapis.com/projects/PROJECT_NUMBER/locations/global/workloadIdentityPools/PROJECT_ID.svc.id.goog/subject/ns/custom-metrics/sa/custom-metrics-stackdriver-adapter
    
  3. Enregistrez le manifeste suivant sous le nom vllm_pod_monitor.yaml :

    
    apiVersion: monitoring.googleapis.com/v1
    kind: PodMonitoring
    metadata:
     name: vllm-pod-monitoring
    spec:
     selector:
       matchLabels:
        leaderworkerset.sigs.k8s.io/name: vllm
        role: leader
     endpoints:
     - path: /metrics
       port: 8080
       interval: 15s
    
  4. Appliquez le fichier manifeste au cluster :

    kubectl apply -f vllm_pod_monitor.yaml
    

Créer une charge sur le point de terminaison vLLM

Créez une charge sur le serveur vLLM pour tester l'autoscaling de GKE avec une métrique vLLM personnalisée.

  1. Configurez le transfert de port vers le modèle:

    kubectl port-forward svc/vllm-leader 8080:8080
    
  2. Exécutez un script bash (load.sh) pour envoyer un nombre N de requêtes parallèles au point de terminaison vLLM:

    #!/bin/bash
    N=PARALLEL_PROCESSES
    export vllm_service=$(kubectl get service vllm-service -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
    for i in $(seq 1 $N); do
      while true; do
        curl http://$vllm_service:8000/v1/completions -H "Content-Type: application/json" -d '{"model": "meta-llama/Meta-Llama-3.1-70B", "prompt": "Write a story about san francisco", "max_tokens": 100, "temperature": 0}'
      done &  # Run in the background
    done
    wait
    

    Remplacez PARALLEL_PROCESSES par le nombre de processus parallèles que vous souhaitez exécuter.

  3. Exécutez le script bash:

    nohup ./load.sh &
    

Vérifier que Google Cloud Managed Service pour Prometheus ingère les métriques

Une fois que Google Cloud Managed Service pour Prometheus a extrait les métriques et que vous ajoutez de la charge au point de terminaison vLLM, vous pouvez afficher les métriques dans Cloud Monitoring.

  1. Dans la console Google Cloud, accédez à la page Explorateur de métriques.

    Accéder à l'explorateur de métriques

  2. Cliquez sur < > PromQL.

  3. Saisissez la requête suivante pour observer les métriques de trafic:

    vllm:gpu_cache_usage_perc{cluster='CLUSTER_NAME'}
    

L'image suivante est un exemple de graphique après l'exécution du script de chargement. Ce graphique montre que Google Cloud Managed Service pour Prometheus ingère les métriques de trafic en réponse à la charge ajoutée au point de terminaison vLLM:

Métriques de trafic capturées pour le serveur vLLM

Déployer la configuration de l'autoscaler horizontal des pods

Lorsque vous choisissez la métrique sur laquelle effectuer l'autoscaling, nous vous recommandons les métriques suivantes pour vLLM:

  • num_requests_waiting: cette métrique concerne le nombre de requêtes en attente dans la file d'attente du serveur de modèle. Ce nombre commence à augmenter de manière notable lorsque le cache kv est plein.

  • gpu_cache_usage_perc: cette métrique concerne l'utilisation du cache KV, qui est directement corrélée au nombre de requêtes traitées pour un cycle d'inférence donné sur le serveur de modèle.

Nous vous recommandons d'utiliser num_requests_waiting lorsque vous optimisez pour le débit et les coûts, et lorsque vos objectifs de latence sont réalisables avec le débit maximal de votre serveur de modèles.

Nous vous recommandons d'utiliser gpu_cache_usage_perc lorsque vous avez des charges de travail sensibles à la latence où le scaling basé sur la file d'attente n'est pas assez rapide pour répondre à vos besoins.

Pour en savoir plus, consultez les bonnes pratiques pour l'autoscaling des charges de travail d'inférence de grands modèles de langage (LLM) avec des GPU.

Lorsque vous sélectionnez une cible averageValue pour votre configuration HPA, vous devez déterminer expérimentalement sur quelle métrique effectuer l'autoscaling. Pour obtenir d'autres idées sur l'optimisation de vos tests, consultez l'article de blog Économiser sur les GPU: autoscaling plus intelligent pour vos charges de travail d'inférence GKE. Le profile-generator utilisé dans cet article de blog fonctionne également pour vLLM.

Pour déployer la configuration de l'autoscaler horizontal de pods à l'aide de num_requests_waiting, procédez comme suit:

  1. Enregistrez le manifeste suivant sous le nom vllm-hpa.yaml :

    
    apiVersion: autoscaling/v2
    kind: HorizontalPodAutoscaler
    metadata:
      name: lws-hpa
    spec:
      minReplicas: 1
      maxReplicas: 2
      metrics:
      - type: Pods
        pods:
          metric:
            name: prometheus.googleapis.com|vllm:num_requests_waiting|gauge
          target:
            type: AverageValue
            averageValue: 5
      scaleTargetRef:
        apiVersion: leaderworkerset.x-k8s.io/v1
        kind: LeaderWorkerSet
        name: vllm
    

    Les métriques vLLM de Google Cloud Managed Service pour Prometheus suivent le format vllm:metric_name.

    Bonne pratique:

    Utilisez num_requests_waiting pour faire évoluer le débit. Utilisez gpu_cache_usage_perc pour les cas d'utilisation des GPU sensibles à la latence.

  2. Déployez la configuration de l'autoscaler horizontal des pods:

    kubectl apply -f vllm-hpa.yaml
    

    GKE planifie le déploiement d'un autre pod, ce qui déclenche l'autoscaler du pool de nœuds pour ajouter un deuxième nœud avant de déployer le deuxième réplica vLLM.

  3. Suivez la progression de l'autoscaling du pod:

    kubectl get hpa --watch
    

    Le résultat ressemble à ce qui suit :

    NAME      REFERENCE              TARGETS   MINPODS   MAXPODS   REPLICAS   AGE
    lws-hpa   LeaderWorkerSet/vllm   0/1       1         2         1          6d1h
    lws-hpa   LeaderWorkerSet/vllm   1/1       1         2         1          6d1h
    lws-hpa   LeaderWorkerSet/vllm   0/1       1         2         1          6d1h
    lws-hpa   LeaderWorkerSet/vllm   4/1       1         2         1          6d1h
    lws-hpa   LeaderWorkerSet/vllm   0/1       1         2         2          6d1h
    

Accélérer les temps de chargement des modèles avec Hyperdisk ML

Avec ces types de LLM, le téléchargement, le chargement et le préchauffage de la vLLM sur chaque nouveau réplica peuvent prendre un temps considérable. Par exemple, ce processus peut prendre environ 90 minutes avec Llama 3.1 405B. Vous pouvez réduire ce délai (à 20 minutes avec Llama 3.1 405B) en téléchargeant le modèle directement sur un volume Hyperdisk ML et en le montant sur chaque pod. Pour effectuer cette opération, ce tutoriel utilise un volume Hyperdisk ML et un job Kubernetes. Dans Kubernetes, un contrôleur de tâche crée un ou plusieurs pods et s'assure qu'ils exécutent correctement une tâche spécifique.

Pour accélérer le chargement des modèles, procédez comme suit:

  1. Enregistrez l'exemple de fichier manifeste suivant sous le nom producer-pvc.yaml :

    kind: PersistentVolumeClaim
    apiVersion: v1
    metadata:
      name: producer-pvc
    spec:
      storageClassName: hyperdisk-ml
      accessModes:
      - ReadWriteOnce
      resources:
        requests:
          storage: 800Gi
    
  2. Enregistrez l'exemple de fichier manifeste suivant sous le nom producer-job.yaml :

    
    apiVersion: batch/v1
    kind: Job
    metadata:
      name: producer-job
    spec:
      template:  # Template for the Pods the Job will create
        spec:
          affinity:
            nodeAffinity:
              requiredDuringSchedulingIgnoredDuringExecution:
                nodeSelectorTerms:
                - matchExpressions:
                  - key: cloud.google.com/machine-family
                    operator: In
                    values:
                    - "c3"
                - matchExpressions:
                  - key: topology.kubernetes.io/zone
                    operator: In
                    values:
                    - "ZONE"
          containers:
          - name: copy
            resources:
              requests:
                cpu: "32"
              limits:
                cpu: "32"
            image: python:3.11-alpine
            command:
            - sh
            - -c
            - "pip install 'huggingface_hub==0.24.6' && \
              huggingface-cli download deepseek-ai/DeepSeek-R1 --local-dir-use-symlinks=False --local-dir=/data/DeepSeek-R1 --include *.safetensors *.json *.py"
            env:
            - name: HUGGING_FACE_HUB_TOKEN
              valueFrom:
                secretKeyRef:
                  name: hf-secret
                  key: hf_api_token
            volumeMounts:
              - mountPath: "/data"
                name: volume
          restartPolicy: Never
          volumes:
            - name: volume
              persistentVolumeClaim:
                claimName: producer-pvc
      parallelism: 1         # Run 1 Pods concurrently
      completions: 1         # Once 1 Pods complete successfully, the Job is done
      backoffLimit: 4        # Max retries on failure
    
    
    
    apiVersion: batch/v1
    kind: Job
    metadata:
      name: producer-job
    spec:
      template:  # Template for the Pods the Job will create
        spec:
          affinity:
            nodeAffinity:
              requiredDuringSchedulingIgnoredDuringExecution:
                nodeSelectorTerms:
                - matchExpressions:
                  - key: cloud.google.com/machine-family
                    operator: In
                    values:
                    - "c3"
                - matchExpressions:
                  - key: topology.kubernetes.io/zone
                    operator: In
                    values:
                    - "ZONE"
          containers:
          - name: copy
            resources:
              requests:
                cpu: "32"
              limits:
                cpu: "32"
            image: python:3.11-alpine
            command:
            - sh
            - -c
            - "pip install 'huggingface_hub==0.24.6' && \
              huggingface-cli download meta-llama/Meta-Llama-3.1-405B-Instruct --local-dir-use-symlinks=False --local-dir=/data/Meta-Llama-3.1-405B-Instruct --include *.safetensors *.json"
            env:
            - name: HUGGING_FACE_HUB_TOKEN
              valueFrom:
                secretKeyRef:
                  name: hf-secret
                  key: hf_api_token
            volumeMounts:
              - mountPath: "/data"
                name: volume
          restartPolicy: Never
          volumes:
            - name: volume
              persistentVolumeClaim:
                claimName: producer-pvc
      parallelism: 1         # Run 1 Pods concurrently
      completions: 1         # Once 1 Pods complete successfully, the Job is done
      backoffLimit: 4        # Max retries on failure
    
    
  3. Suivez les instructions de la section Accélérez le chargement des données d'IA/ML avec Hyperdisk ML, en utilisant les deux fichiers que vous avez créés lors des étapes précédentes.

    Une fois cette étape terminée, vous avez créé et rempli le volume Hyperdisk ML avec les données du modèle.

  4. Déployez le déploiement du serveur GPU multi-nœud vLLM, qui utilisera le volume Hyperdisk ML nouvellement créé pour les données du modèle.

    
    
    apiVersion: leaderworkerset.x-k8s.io/v1
    kind: LeaderWorkerSet
    metadata:
      name: vllm
    spec:
      replicas: 1
      leaderWorkerTemplate:
        size: 2
        restartPolicy: RecreateGroupOnPodRestart
        leaderTemplate:
          metadata:
            labels:
              role: leader
          spec:
            containers:
              - name: vllm-leader
                image: IMAGE_NAME
                env:
                  - name: HUGGING_FACE_HUB_TOKEN
                    valueFrom:
                      secretKeyRef:
                        name: hf-secret
                        key: hf_api_token
                command:
                  - sh
                  - -c
                  - "/vllm-workspace/ray_init.sh leader --ray_cluster_size=$(LWS_GROUP_SIZE); 
                    python3 -m vllm.entrypoints.openai.api_server --port 8080 --model /models/DeepSeek-R1 --tensor-parallel-size 8 --pipeline-parallel-size 2 --trust-remote-code --max-model-len 4096"
                resources:
                  limits:
                    nvidia.com/gpu: "8"
                ports:
                  - containerPort: 8080
                readinessProbe:
                  tcpSocket:
                    port: 8080
                  initialDelaySeconds: 15
                  periodSeconds: 10
                volumeMounts:
                  - mountPath: /dev/shm
                    name: dshm
                  - mountPath: /models
                    name: deepseek-r1
            volumes:
            - name: dshm
              emptyDir:
                medium: Memory
            - name: deepseek-r1
              persistentVolumeClaim:
                claimName: hdml-static-pvc
        workerTemplate:
          spec:
            containers:
              - name: vllm-worker
                image: IMAGE_NAME
                command:
                  - sh
                  - -c
                  - "/vllm-workspace/ray_init.sh worker --ray_address=$(LWS_LEADER_ADDRESS)"
                resources:
                  limits:
                    nvidia.com/gpu: "8"
                env:
                  - name: HUGGING_FACE_HUB_TOKEN
                    valueFrom:
                      secretKeyRef:
                        name: hf-secret
                        key: hf_api_token
                volumeMounts:
                  - mountPath: /dev/shm
                    name: dshm
                  - mountPath: /models
                    name: deepseek-r1
            volumes:
            - name: dshm
              emptyDir:
                medium: Memory
            - name: deepseek-r1
              persistentVolumeClaim:
                claimName: hdml-static-pvc
    ---
    apiVersion: v1
    kind: Service
    metadata:
      name: vllm-leader
    spec:
      ports:
        - name: http
          port: 8080
          protocol: TCP
          targetPort: 8080
      selector:
        leaderworkerset.sigs.k8s.io/name: vllm
        role: leader
      type: ClusterIP
    
    
    
    apiVersion: leaderworkerset.x-k8s.io/v1
    kind: LeaderWorkerSet
    metadata:
      name: vllm
    spec:
      replicas: 1
      leaderWorkerTemplate:
        size: 2
        restartPolicy: RecreateGroupOnPodRestart
        leaderTemplate:
          metadata:
            labels:
              role: leader
          spec:
            containers:
              - name: vllm-leader
                image: IMAGE_NAME
                env:
                  - name: HUGGING_FACE_HUB_TOKEN
                    valueFrom:
                      secretKeyRef:
                        name: hf-secret
                        key: hf_api_token
                command:
                  - sh
                  - -c
                  - "/vllm-workspace/ray_init.sh leader --ray_cluster_size=$(LWS_GROUP_SIZE); 
                    python3 -m vllm.entrypoints.openai.api_server --port 8080 --model /models/Meta-Llama-3.1-405B-Instruct --tensor-parallel-size 8 --pipeline-parallel-size 2"
                resources:
                  limits:
                    nvidia.com/gpu: "8"
                ports:
                  - containerPort: 8080
                readinessProbe:
                  tcpSocket:
                    port: 8080
                  initialDelaySeconds: 15
                  periodSeconds: 10
                volumeMounts:
                  - mountPath: /dev/shm
                    name: dshm
                  - mountPath: /models
                    name: llama3-405b
            volumes:
            - name: dshm
              emptyDir:
                medium: Memory
            - name: llama3-405b
              persistentVolumeClaim:
                claimName: hdml-static-pvc
        workerTemplate:
          spec:
            containers:
              - name: vllm-worker
                image: IMAGE_NAME
                command:
                  - sh
                  - -c
                  - "/vllm-workspace/ray_init.sh worker --ray_address=$(LWS_LEADER_ADDRESS)"
                resources:
                  limits:
                    nvidia.com/gpu: "8"
                env:
                  - name: HUGGING_FACE_HUB_TOKEN
                    valueFrom:
                      secretKeyRef:
                        name: hf-secret
                        key: hf_api_token
                volumeMounts:
                  - mountPath: /dev/shm
                    name: dshm
                  - mountPath: /models
                    name: llama3-405b
            volumes:
            - name: dshm
              emptyDir:
                medium: Memory
            - name: llama3-405b
              persistentVolumeClaim:
                claimName: hdml-static-pvc
    ---
    apiVersion: v1
    kind: Service
    metadata:
      name: vllm-leader
    spec:
      ports:
        - name: http
          port: 8080
          protocol: TCP
          targetPort: 8080
      selector:
        leaderworkerset.sigs.k8s.io/name: vllm
        role: leader
      type: ClusterIP
    

Effectuer un nettoyage

Pour éviter que les ressources utilisées lors de ce tutoriel soient facturées sur votre compte Google Cloud, supprimez le projet contenant les ressources, ou conservez le projet et supprimez les ressources individuelles.

Supprimer les ressources déployées

Pour éviter que les ressources que vous avez créées dans ce guide ne soient facturées sur votre compte Google Cloud , exécutez la commande suivante:

ps -ef | grep load.sh | awk '{print $2}' | xargs -n1 kill -9
gcloud container clusters delete CLUSTER_NAME \
  --location=ZONE

Étape suivante