Gestisci un LLM (Llama3.1 405B) utilizzando più nodi GPU


Panoramica

Questo tutorial mostra come eseguire il servizio Llama 3.1 405b utilizzando GPU su più nodi su Google Kubernetes Engine (GKE), utilizzando il framework di servizio vLLM e l'API LeaderWorkerSet (LWS).

Questo documento è un buon punto di partenza se hai bisogno del controllo granulare, della scalabilità, della resilienza, della portabilità e dell'economicità di Kubernetes gestito per il deployment e l'erogazione dei tuoi carichi di lavoro di AI/ML.

LeaderWorkerSet (LWS)

LWS è un'API di deployment Kubernetes che gestisce i pattern di deployment comuni dei carichi di lavoro di inferenza multi-nodo di AI/ML. LWS consente di trattare più pod come un gruppo.

Pubblicazione multi-host con vLLM

Quando esegui il deployment di modelli linguistici eccezionalmente grandi che non possono essere inseriti in un singolo nodo GPU, utilizza più nodi GPU per pubblicare il modello. vLLM supporta sia il parallelismo tensoriale sia il parallelismo di pipeline per eseguire i carichi di lavoro su più GPU.

Il parallelismo tensoriale suddivide le moltiplicazioni di matrici nel livello del trasformatore su più GPU. Tuttavia, questa strategia richiede una rete veloce a causa della comunicazione necessaria tra le GPU, rendendola meno adatta per l'esecuzione di carichi di lavoro su più nodi.

Il parallismo della pipeline suddivide il modello per livello o in verticale. Questa strategia non richiede una comunicazione costante tra le GPU, il che la rende un'opzione migliore per l'esecuzione di modelli su più nodi.

Puoi utilizzare entrambe le strategie nella pubblicazione su più nodi. Ad esempio, se utilizzi due nodi con otto GPU H100 ciascuno, puoi utilizzare il parallelismo della pipeline bidirezionale per suddividere il modello nei due nodi e il parallelismo tensoriale a otto vie per suddividere il modello nelle otto GPU di ciascun nodo.

Obiettivi

  1. Prepara un cluster GKE Standard.
  2. Esegui il deployment di vLLM su più nodi del cluster.
  3. Utilizza vLLM per pubblicare il modello Llama3 405b tramite curl.

Prima di iniziare

  • 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 colunn 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.

      Vai a IAM
    2. Seleziona il progetto.
    3. Fai clic su Concedi accesso.
    4. Nel campo Nuove entità, inserisci il tuo identificatore utente. In genere si tratta dell'indirizzo email di un Account Google.

    5. Nell'elenco Seleziona un ruolo, seleziona un ruolo.
    6. Per concedere altri ruoli, fai clic su Aggiungi un altro ruolo e aggiungi ogni ruolo aggiuntivo.
    7. Fai clic su Salva.

Ottieni l'accesso al modello

Genera un token di accesso

Se non ne hai già uno, genera un nuovo token Hugging Face:

  1. Fai clic su Il tuo profilo > Impostazioni > Token di accesso.
  2. Seleziona Nuovo token.
  3. Specifica un nome a tua scelta e un ruolo di almeno Read.
  4. Seleziona Genera un token.

Prepara l'ambiente

In questo tutorial utilizzerai Cloud Shell per gestire le risorse ospitate su Google Cloud. Cloud Shell include il software di cui avrai bisogno per questo tutorial, tra cui kubectl e gcloud CLI.

Per configurare l'ambiente con Cloud Shell:

  1. Nella console Google Cloud, avvia una sessione Cloud Shell facendo clic su Icona di attivazione di Cloud Shell Attiva Cloud Shell nella console Google Cloud. Viene avviata una sessione nel riquadro inferiore della console Google Cloud.

  2. Imposta le variabili di ambiente predefinite:

    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
    

    Sostituisci i seguenti valori:

    • PROJECT_ID: il tuo ID progetto Google Cloud.
    • CLUSTER_NAME: il nome del tuo cluster GKE.
    • ZONE: una zona che supporta le schede H100.
    • IMAGE_NAME: l'immagine vLLM che include lo script ray. Forniamo us-docker.pkg.dev/vertex-ai/vertex-vision-model-garden-dockers/pytorch-vllm-serve:20240821_1034_RC00 oppure puoi crearne uno tuo.

Crea un cluster GKE

Puoi pubblicare modelli utilizzando vLLM su più nodi GPU in un cluster GKE Autopilot o Standard. Ti consigliamo di utilizzare un cluster Autopilot per un'esperienza Kubernetes completamente gestita. Per scegliere la modalità operativa di GKE più adatta ai tuoi carichi di lavoro, consulta Scegliere una modalità operativa GKE.

Autopilot

In Cloud Shell, esegui questo comando:

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

Standard

  1. Crea un cluster GKE Standard con due nodi CPU:

    gcloud container clusters create CLUSTER_NAME \
        --project=PROJECT_ID \
        --num-nodes=2 \
        --location=ZONE \
        --machine-type=e2-standard-16
    
  2. Crea un pool di nodi A3 con due nodi, ciascuno con otto 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
    

Configura kubectl per comunicare con il cluster:

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

Crea un secret di Kubernetes per le credenziali di Hugging Face

Crea un secret di Kubernetes contenente il token di Hugging Face:

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

(Facoltativo) Crea la tua immagine vLLM a più nodi

Scegli questa opzione per avere un maggiore controllo sui contenuti dell'immagine Docker e includere dipendenze specifiche insieme allo script. Per eseguire vLLM su più nodi, puoi utilizzare Ray per la comunicazione tra nodi. Puoi visualizzare il Dockerfile, che contiene un script bash per configurare Ray con vLLM nel repository LeaderWorkerSet.

Crea il container

  1. Clona il repository LeaderWorkerSet:

    git clone https://github.com/kubernetes-sigs/lws.git
    
  2. Crea l'immagine.

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

Esegui il push dell'immagine in Artifact Registry

Per assicurarti che il deployment di Kubernetes possa accedere all'immagine, archiviala in Artifact Registry all'interno del tuo progetto 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

Installa LeaderWorkerSet

Per installare LWS, esegui il seguente comando:

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

Verifica che il controller LeaderWorkerSet sia in esecuzione nello spazio dei nomi lws-system:

kubectl get pod -n lws-system

L'output è simile al seguente:

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

Esegui il deployment del server di modelli vLLM

Per eseguire il deployment del server di modelli vLLM:

  1. Controlla il file manifest 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. Applica il manifest eseguendo il seguente comando:

    kubectl apply -f vllm-llama3-405b-A3.yaml
    
  3. Visualizza i log del server del modello in esecuzione

    kubectl logs vllm-0 -c vllm-leader
    

    L'output dovrebbe essere simile al seguente:

    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)
    

Pubblica il modello

Esegui il comando seguente per configurare il port forwarding al modello

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

Interagire con il modello utilizzando curl

In un nuovo terminale, invia una richiesta al server:

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

L'output dovrebbe essere simile al seguente:

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

(Facoltativo) Accelera i tempi di caricamento del modello con Hyperdisk ML

vLLM può richiedere fino a 90 minuti per scaricare, caricare e eseguire l'inizializzazione di Llama 3.1-405B su ogni nuova replica. Puoi ridurre questo tempo a 20 minuti scaricando il modello direttamente su un Hyperdisk ML e montandolo su ogni pod.

Puoi seguire il tutorial Accelerare il caricamento dei dati AI/ML con Hyperdisk ML utilizzando i seguenti file YAML:

  1. Salva il seguente manifest di esempio come producer-pvc.yaml:

    kind: PersistentVolumeClaim
    apiVersion: v1
    metadata:
      name: producer-pvc
    spec:
      storageClassName: hyperdisk-ml
      accessModes:
      - ReadWriteOnce
      resources:
        requests:
          storage: 800Gi
    
  2. Salva il seguente manifest di esempio come 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
    

Esegui il deployment del server di modelli vLLM

Dopo aver completato i passaggi, puoi eseguire il deployment del server GPU multi-nodo vLLM che utilizza il volume ML Hyperdisk.



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

Esegui la pulizia

Per evitare che al tuo account Google Cloud vengano addebitati costi relativi alle risorse utilizzate in questo tutorial, elimina il progetto che contiene le risorse oppure mantieni il progetto ed elimina le singole risorse.

Elimina le risorse di cui è stato eseguito il deployment

Per evitare che al tuo account Google Cloud vengano addebitati costi relativi alle risorse che hai creato in questa guida, esegui il seguente comando:

gcloud container clusters delete CLUSTER_NAME \
  --location=ZONE

Passaggi successivi