Diffuser un LLM (Llama3.1 405B) à l'aide de plusieurs nœuds GPU


Présentation

Ce tutoriel explique comment diffuser Llama 3.1 405b à l'aide de processeurs graphiques (GPU) sur plusieurs nœuds sur Google Kubernetes Engine (GKE), à l'aide du framework de diffusion vLLM et de l'API LeaderWorkerSet (LWS).

Ce document est un bon point de départ si vous avez besoin du contrôle précis, de l'évolutivité, de la résilience, de la portabilité et de la rentabilité des services Kubernetes gérés lors du déploiement et de la diffusion de vos charges de travail d'IA/de ML.

LeaderWorkerSet (LWS)

LWS est une API de déploiement Kubernetes qui gère les modèles de déploiement courants des charges de travail d'inférence multi-nœuds d'IA/ML. LWS permet de traiter plusieurs pods comme un groupe.

Diffusion multi-hôte avec vLLM

Lorsque vous déployez des modèles de langage exceptionnellement volumineux qui ne peuvent pas tenir dans un seul nœud GPU, utilisez plusieurs nœuds GPU pour diffuser le modèle. vLLM est compatible à la fois avec le parallélisme de tenseur et le parallélisme de pipeline pour exécuter des charges de travail sur plusieurs GPU.

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 le parallélisme de pipeline à deux voies pour diviser le modèle entre les deux nœuds, et le parallélisme de tenseur à huit voies pour diviser le modèle entre les huit GPU de chaque nœud.

Objectifs

  1. Préparez un cluster GKE Standard.
  2. Déployez vLLM sur plusieurs nœuds de votre cluster.
  3. Utilisez vLLM pour diffuser le modèle Llama3 405b 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

  • 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

    Check for the roles

    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.

    Grant the 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

.

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 consoleGoogle 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=IMAGE_NAME
    

    Remplacez les valeurs suivantes :

    • PROJECT_ID: ID de votre projet Google Cloud
    • CLUSTER_NAME : nom de votre cluster GKE.
    • ZONE : zone compatible avec les H100.
    • IMAGE_NAME: image vLLM incluant le script de rayon. Nous vous fournissons us-docker.pkg.dev/vertex-ai/vertex-vision-model-garden-dockers/pytorch-vllm-serve:20240821_1034_RC00, ou vous pouvez créer le vôtre.

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.

Autopilot

Dans Cloud Shell, exécutez la commande suivante :

  gcloud container clusters create-auto ${CLUSTER_NAME} \
    --project=${PROJECT_ID} \
    --region=${REGION} \
    --cluster-version=${CLUSTER_VERSION}

Standard

  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 de manière à communiquer avec votre cluster :

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:

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

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

Pour mieux contrôler le contenu de votre image Docker et inclure des dépendances spécifiques avec votre script, choisissez cette option. Pour exécuter vLLM sur plusieurs nœuds, vous pouvez utiliser Ray pour la communication entre les nœuds. Vous pouvez consulter le Dockerfile, qui contient un script bash permettant de configurer Ray avec vLLM dans le dépôt LeaderWorkerSet.

Créer le conteneur

  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 . -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 projet Google Cloud .

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:

VERSION=v0.4.2
kubectl apply --server-side -f https://github.com/kubernetes-sigs/lws/releases/download/$VERSION/manifests.yaml

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

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. 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
                  - "/workspace/vllm/examples/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
                  - "/workspace/vllm/examples/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
    
  3. Afficher les journaux du serveur de modèles en cours d'exécution

    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

Exécutez la commande suivante pour configurer le transfert de port sur le modèle.

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

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

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

(Facultatif) Accélérez les temps de chargement des modèles avec Hyperdisk ML

Le téléchargement, le chargement et le préchauffage de Llama 3.1-405B sur chaque nouveau réplica peuvent prendre jusqu'à 90 minutes. Vous pouvez réduire ce délai à 20 minutes en téléchargeant le modèle directement sur un Hyperdisk ML et en le montant sur chaque pod.

Vous pouvez suivre le tutoriel Accélérez le chargement des données d'IA/ML avec Hyperdisk ML à l'aide des fichiers YAML suivants:

  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-bookworm
            command:
            - bash
            - -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
    

Déployer le serveur de modèles vLLM

Une fois ces étapes terminées, vous pouvez déployer le serveur GPU multi-nœud vLLM qui consomme le volume Hyperdisk ML.



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
              - "/workspace/vllm/examples/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
              - "/workspace/vllm/examples/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:

gcloud container clusters delete CLUSTER_NAME \
  --location=ZONE

Étape suivante