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


Présentation

Ce guide vous explique comment diffuser des grands modèles de langage (LLM) de pointe tels que DeepSeek-R1 671B ou Llama3.1 405B sur Google Distributed Cloud (logiciel uniquement) sur Bare Metal à 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 des clusters bare metal. Google Distributed Cloud étend GKE pour une utilisation dans un environnement sur site, tout en offrant les avantages du contrôle précis, de l'évolutivité, de la résilience, de la portabilité et de la rentabilité de GKE.

Arrière-plan

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 développé 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. Google Distributed Cloud gère les exigences de calcul de DeepSeek-R1, en prenant en charge ses capacités avec des ressources évolutives, le calcul distribué et une mise en réseau efficace.

Pour en savoir plus, consultez la documentation DeepSeek.

Llama 3.1 405B

Llama 3.1 405B 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. Google Distributed Cloud 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é Google Distributed Cloud

Google Distributed Cloud propose un large éventail de services, y compris Google Distributed Cloud (logiciel uniquement) pour Bare Metal, qui est idéal pour déployer et gérer des charges de travail d'IA/ML dans votre propre centre de données. Google Distributed Cloud est un service Kubernetes géré qui simplifie le déploiement, le scaling et la gestion des applications conteneurisées. Google Distributed Cloud fournit l'infrastructure nécessaire, y compris des ressources évolutives, le calcul distribué et une mise en réseau efficace, pour répondre aux exigences 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 Google Distributed Cloud et sur la façon dont il vous aide à mettre à l'échelle, à automatiser et à gérer Kubernetes, consultez Présentation de Google Distributed Cloud (logiciel uniquement) pour solution Bare Metal.

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 des données. Google Distributed Cloud est compatible avec les 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. Google Distributed Cloud 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 Configurer et utiliser des GPU NVIDIA.

LeaderWorkerSet (LWS)

LeaderWorkerSet (LWS) est une API de déploiement Kubernetes qui répond aux modèles de déploiement courants des charges de travail d'inférence multinœuds d'IA/ML. Le service multinoeuds utilise 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 comme un groupe, ce qui simplifie la gestion de l'inférence de modèles distribuée.

vLLM et mise en service multi-hôtes

Lorsque vous diffusez des LLM intensifs 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 les suivantes :

  • 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 les LLM particulièrement gourmands en ressources de 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 permet d'exécuter des charges de travail sur plusieurs GPU avec deux stratégies :

  • Le parallélisme Tensor divise les multiplications matricielles dans la couche Transformer 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 le service multinoeud. Par exemple, lorsque vous utilisez deux nœuds avec huit GPU H100 chacun, vous pouvez utiliser les deux stratégies :

  • Parallélisme de pipeline bidirectionnel pour partitionner le modèle sur les deux nœuds

  • Parallélisme Tensor à huit voies pour partitionner 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 Google Distributed Cloud 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

Accéder au modèle

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

DeepSeek-R1

.

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

Si vous n'en avez 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.

Llama 3.1 405B

.

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

Si vous n'en avez 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

Pour configurer votre environnement, procédez comme suit :

  1. Définissez les paramètres suivants sur le poste de travail administrateur :

    gcloud config set project PROJECT_ID
    export PROJECT_ID=$(gcloud config get project)
    export HF_TOKEN=HUGGING_FACE_TOKEN
    export IMAGE_NAME= gcr.io/PROJECT_ID/vllm-multihost/vllm-multihost:latest
    

    Remplacez les valeurs suivantes :

    • PROJECT_ID : ID du projet associé à votre cluster.

    • HUGGING_FACE_TOKEN : jeton Hugging Face généré à partir de la section précédente Accéder au modèle.

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 \
    --kubeconfig KUBECONFIG \
    --from-literal=hf_api_token=${HF_TOKEN} \
    --dry-run=client -o yaml | kubectl apply -f -

Remplacez KUBECONFIG par le chemin d'accès au fichier kubeconfig du cluster sur lequel vous prévoyez d'héberger le LLM.

Créer votre propre image vLLM multinœud

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 vLLM multinœud, 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 les nœuds), puis transférer cette image vers Artifact Registry pour le déploiement sur Google Distributed Cloud.

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 au sein de votre projet Google Cloud  :

docker image tag vllm-multihost ${IMAGE_NAME}
docker push ${IMAGE_NAME}

Installer LeaderWorkerSet

Pour installer LWS, exécutez la commande suivante :

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

Validez 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 --kubeconfig KUBECONFIG

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. Créez et appliquez le fichier manifeste, en fonction du LLM que vous souhaitez déployer.

    DeepSeek-R1

    1. Créez un fichier manifeste YAML, vllm-deepseek-r1-A3.yaml, pour le serveur de modèle vLLM :

      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:
              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 \
          --kubeconfig KUBECONFIG
      

    Llama 3.1 405B

    1. Créez un fichier manifeste YAML, vllm-llama3-405b-A3.yaml, pour le serveur de modèle vLLM :

      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:
              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 \
          --kubeconfig KUBECONFIG
      
  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 \
        --kubeconfig KUBECONFIG
    

    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 vers le modèle en exécutant la commande suivante :

kubectl port-forward svc/vllm-leader 8080:8080 \
    --kubeconfig KUBECONFIG

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

Pour interagir avec le modèle à l'aide de curl, suivez ces instructions :

DeepSeek-R1

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
  }
}

Llama 3.1 405B

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}}

Étapes suivantes