Eroga LLM come DeepSeek-R1 671B o Llama 3.1 405B su bare metal


Panoramica

Questa guida mostra come pubblicare modelli linguistici di grandi dimensioni (LLM) all'avanguardia, come DeepSeek-R1 671B o Llama 3.1 405B su Google Distributed Cloud (solo software) su bare metal utilizzando unità di elaborazione grafica (GPU) su più nodi.

Questa guida mostra come utilizzare tecnologie open source portatili, ovvero Kubernetes, vLLM e l'API LeaderWorkerSet (LWS), per eseguire il deployment e gestire i carichi di lavoro AI/ML su cluster bare metal. Google Distributed Cloud estende GKE per l'utilizzo in un ambiente on-premise, fornendo al contempo i vantaggi del controllo granulare, della scalabilità, della resilienza, della portabilità e della convenienza economica di GKE.

Sfondo

Questa sezione descrive le tecnologie chiave utilizzate in questa guida, tra cui i due LLM utilizzati come esempi in questa guida: DeepSeek-R1 e Llama 3.1 405B.

DeepSeek-R1

DeepSeek-R1, un modello linguistico di grandi dimensioni con 671 miliardi di parametri di DeepSeek, è progettato per l'inferenza logica, il ragionamento matematico e la risoluzione di problemi in tempo reale in varie attività basate su testo. Google Distributed Cloud gestisce le esigenze di calcolo di DeepSeek-R1, supportandone le funzionalità con risorse scalabili, computing distribuito e networking efficiente.

Per saperne di più, consulta la documentazione di DeepSeek.

Llama 3.1 405B

Llama 3.1 405B è un modello linguistico di grandi dimensioni di Meta progettato per un'ampia gamma di attività di elaborazione del linguaggio naturale, tra cui generazione di testi, traduzione e risposta a domande. Google Distributed Cloud offre l'infrastruttura solida necessaria per supportare le esigenze di addestramento e serving distribuito di modelli di questa scala.

Per saperne di più, consulta la documentazione di Llama.

Servizio Kubernetes gestito Google Distributed Cloud

Google Distributed Cloud offre un'ampia gamma di servizi, tra cui Google Distributed Cloud (solo software) per bare metal, ideale per il deployment e la gestione dei carichi di lavoro di AI/ML nel tuo data center. Google Distributed Cloud è un servizio Kubernetes gestito che semplifica il deployment, lo scaling e la gestione delle applicazioni containerizzate. Google Distributed Cloud fornisce l'infrastruttura necessaria, tra cui risorse scalabili, computing distribuito e networking efficiente, per gestire le esigenze di calcolo degli LLM.

Per saperne di più sui concetti chiave di Kubernetes, consulta Inizia a scoprire Kubernetes. Per scoprire di più su Google Distributed Cloud e su come ti aiuta a scalare, automatizzare e gestire Kubernetes, consulta la panoramica di Google Distributed Cloud (solo software) per bare metal.

GPU

Le GPU (Graphics Processing Unit) ti consentono di accelerare carichi di lavoro specifici, come machine learning ed elaborazione di dati. Google Distributed Cloud supporta i nodi dotati di queste potenti GPU, consentendoti di configurare il cluster per prestazioni ottimali nelle attività di machine learning ed elaborazione dei dati. Google Distributed Cloud offre una gamma di opzioni di tipo di macchina per la configurazione dei nodi, inclusi tipi di macchine con GPU NVIDIA H100, L4 e A100.

Per saperne di più, consulta Configurare e utilizzare le GPU NVIDIA.

LeaderWorkerSet (LWS)

LeaderWorkerSet (LWS) è un'API di deployment Kubernetes che gestisce i pattern di deployment comuni dei carichi di lavoro di inferenza AI/ML multimodali. Il servizio multi-nodo utilizza più pod, ognuno dei quali potenzialmente in esecuzione su un nodo diverso, per gestire il carico di lavoro di inferenza distribuita. LWS consente di trattare più pod come un gruppo, semplificando la gestione della pubblicazione di modelli distribuiti.

vLLM e servizio multi-host

Quando vengono pubblicati LLM ad alta intensità di calcolo, consigliamo di utilizzare vLLM ed eseguire i carichi di lavoro sulle GPU.

vLLM è un framework di gestione degli LLM open source altamente ottimizzato che può aumentare la velocità effettiva di gestione sulle GPU, con funzionalità come le seguenti:

  • Implementazione ottimizzata di Transformer con PagedAttention

  • Batch continuo per migliorare la velocità effettiva complessiva della pubblicazione

  • Pubblicazione distribuita su più GPU

Con LLM particolarmente intensivi dal punto di vista computazionale che non possono essere contenuti in un singolo nodo GPU, puoi utilizzare più nodi GPU per gestire il modello. vLLM supporta l'esecuzione di carichi di lavoro su più GPU con due strategie:

  • Il parallelismo tensoriale suddivide le moltiplicazioni di matrici nel livello Transformer tra più GPU. Tuttavia, questa strategia richiede una rete veloce a causa della comunicazione necessaria tra le GPU, il che la rende meno adatta per l'esecuzione di workload tra i nodi.

  • Il parallelismo 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 quando si eseguono modelli su più nodi.

Puoi utilizzare entrambe le strategie nel servizio multimodello. Ad esempio, quando utilizzi due nodi con otto GPU H100 ciascuno, puoi utilizzare entrambe le strategie:

  • Parallelismo della pipeline bidirezionale per partizionare il modello tra i due nodi

  • Parallelismo dei tensori a otto vie per suddividere il modello tra le otto GPU di ogni nodo

Per saperne di più, consulta la documentazione di vLLM.

Obiettivi

  1. Prepara l'ambiente con un cluster Google Distributed Cloud in modalità Autopilot o Standard.

  2. Esegui il deployment di vLLM su più nodi del cluster.

  3. Utilizza vLLM per pubblicare il modello tramite curl.

Prima di iniziare

Ottenere l'accesso al modello

Puoi utilizzare i modelli Llama 3.1 405B o DeepSeek-R1.

DeepSeek-R1

Generare 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.

Llama 3.1 405B

devi firmare il contratto di consenso.

Generare 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

Per configurare l'ambiente:

  1. Imposta i seguenti parametri sulla workstation di amministrazione:

    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
    

    Sostituisci i seguenti valori:

    • PROJECT_ID: l'ID progetto associato al tuo cluster.

    • HUGGING_FACE_TOKEN: il token Hugging Face generato dalla sezione precedente Ottieni l'accesso al modello.

Crea un secret di Kubernetes per le credenziali di Hugging Face

Crea un secret Kubernetes che contenga il token Hugging Face utilizzando il seguente comando:

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

Sostituisci KUBECONFIG con il percorso del file kubeconfig per il cluster su cui intendi ospitare l'LLM.

Crea la tua immagine multinodo vLLM

Per facilitare la comunicazione tra nodi per vLLM, puoi utilizzare Ray. Il repository LeaderWorkerSet fornisce un Dockerfile, che include uno script bash per configurare Ray con vLLM.

Per creare la tua immagine multinodo vLLM, devi clonare il repository LeaderWorkerSet, creare un'immagine Docker utilizzando il Dockerfile fornito (che configura Ray per la comunicazione tra nodi) e poi eseguire il push dell'immagine in Artifact Registry per il deployment su Google Distributed Cloud.

Crea il container

Per creare il contenitore:

  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.GPU . -t vllm-multihost
    

Esegui il push dell'immagine in Artifact Registry

Per assicurarti che il deployment Kubernetes possa accedere all'immagine, archiviala in Artifact Registry all'interno del tuo progetto Google Cloud :

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

Installazione di LeaderWorkerSet

Per installare LWS, esegui questo comando:

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

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

kubectl get pod -n lws-system --kubeconfig KUBECONFIG

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 del modello vLLM:

  1. Crea e applica il manifest, a seconda dell'LLM che vuoi eseguire il deployment.

    DeepSeek-R1

    1. Crea un manifest YAML, vllm-deepseek-r1-A3.yaml, per il server del modello 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. Applica il manifest eseguendo questo comando:

      kubectl apply -f vllm-deepseek-r1-A3.yaml \
          --kubeconfig KUBECONFIG
      

    Llama 3.1 405B

    1. Crea un manifest YAML, vllm-llama3-405b-A3.yaml, per il server del modello 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. Applica il manifest eseguendo questo comando:

      kubectl apply -f vllm-llama3-405b-A3.yaml \
          --kubeconfig KUBECONFIG
      
  2. Visualizza i log del server del modello in esecuzione con questo comando:

    kubectl logs vllm-0 -c vllm-leader \
        --kubeconfig KUBECONFIG
    

    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

Configura il port forwarding al modello eseguendo questo comando:

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

Interagisci con il modello utilizzando curl

Per interagire con il modello utilizzando curl, segui queste istruzioni:

DeepSeek-R1

In un nuovo terminale, invia una richiesta al server:

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

L'output dovrebbe essere simile al seguente:

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

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

Passaggi successivi