LLMs wie DeepSeek-R1 671B oder Llama 3.1 405B auf Bare Metal bereitstellen


Übersicht

In diesem Leitfaden wird beschrieben, wie Sie moderne Large Language Models (LLMs) wie DeepSeek-R1 671B oder Llama 3.1 405B in Google Distributed Cloud (nur Software) auf Bare Metal mit GPUs auf mehreren Knoten bereitstellen.

In diesem Leitfaden wird gezeigt, wie Sie portable Open-Source-Technologien wie Kubernetes, vLLM und die LeaderWorkerSet-API (LWS) verwenden, um KI-/ML-Arbeitslasten in Bare Metal-Clustern bereitzustellen und auszuführen. Google Distributed Cloud erweitert GKE für die Verwendung in einer lokalen Umgebung und bietet gleichzeitig die Vorteile der detaillierten Steuerung, Skalierbarkeit, Resilienz, Portabilität und Kosteneffizienz von GKE.

Hintergrund

In diesem Abschnitt werden die in diesem Leitfaden verwendeten Schlüsseltechnologien beschrieben, einschließlich der beiden LLMs, die in diesem Leitfaden als Beispiele verwendet werden: DeepSeek-R1 und Llama 3.1 405B.

DeepSeek-R1

DeepSeek-R1 ist ein Large Language Model mit 671 Milliarden Parametern von DeepSeek, das für logische Schlussfolgerungen, mathematische Argumentation und die Lösung von Problemen in Echtzeit bei verschiedenen textbasierten Aufgaben entwickelt wurde. Google Distributed Cloud bewältigt die Rechenanforderungen von DeepSeek-R1 und unterstützt seine Funktionen mit skalierbaren Ressourcen, verteiltem Computing und effizienter Vernetzung.

Weitere Informationen finden Sie in der DeepSeek-Dokumentation.

Llama 3.1 405B

Llama 3.1 405B ist ein Large Language Model von Meta, das für eine Vielzahl von Natural Language Processing-Aufgaben entwickelt wurde, darunter Textgenerierung, Übersetzung und Fragenbeantwortung. Google Distributed Cloud bietet die robuste Infrastruktur, die für die verteilte Trainings- und Bereitstellungsanforderungen von Modellen dieser Größenordnung erforderlich ist.

Weitere Informationen finden Sie in der Llama-Dokumentation.

Verwalteter Kubernetes-Dienst von Google Distributed Cloud

Google Distributed Cloud bietet eine Vielzahl von Diensten, darunter Google Distributed Cloud (nur Software) für Bare Metal, die sich gut für die Bereitstellung und Verwaltung von KI/ML-Arbeitslasten in Ihrem eigenen Rechenzentrum eignet. Google Distributed Cloud ist ein verwalteter Kubernetes-Dienst, der die Bereitstellung, Skalierung und Verwaltung von containerisierten Anwendungen vereinfacht. Google Distributed Cloud bietet die erforderliche Infrastruktur, einschließlich skalierbarer Ressourcen, verteiltem Computing und effizienter Vernetzung, um die Rechenanforderungen von LLMs zu bewältigen.

Weitere Informationen zu wichtigen Kubernetes-Konzepten finden Sie unter Kubernetes lernen. Weitere Informationen zu Google Distributed Cloud und dazu, wie Sie damit Kubernetes skalieren, automatisieren und verwalten können, finden Sie in der Übersicht über Google Distributed Cloud (nur Software) für Bare Metal.

GPUs

Mit Grafikprozessoren (GPUs) können Sie bestimmte Arbeitslasten wie maschinelles Lernen und Datenverarbeitung beschleunigen. Google Distributed Cloud unterstützt Knoten mit diesen leistungsstarken GPUs. So können Sie Ihren Cluster für optimale Leistung bei Aufgaben für maschinelles Lernen und Datenverarbeitung konfigurieren. Google Distributed Cloud bietet eine Reihe von Maschinentypoptionen für die Knotenkonfiguration, einschließlich Maschinentypen mit NVIDIA H100-, L4- und A100-GPUs.

Weitere Informationen finden Sie unter NVIDIA-GPUs einrichten und verwenden.

LeaderWorkerSet (LWS)

LeaderWorkerSet (LWS) ist eine Kubernetes-Bereitstellungs-API, die gängige Bereitstellungsmuster von KI/ML-Inferenzarbeitslasten mit mehreren Knoten abdeckt. Beim Multi-Node-Serving werden mehrere Pods verwendet, die jeweils potenziell auf einem anderen Knoten ausgeführt werden, um die verteilte Inferenzlast zu verarbeiten. Mit LWS können mehrere Pods als Gruppe behandelt werden, was die Verwaltung der verteilten Modellbereitstellung vereinfacht.

vLLM und Bereitstellung mit mehreren Hosts

Wenn Sie rechenintensive LLMs bereitstellen, empfehlen wir die Verwendung von vLLM und die Ausführung der Arbeitslasten auf mehreren GPUs.

vLLM ist ein hoch optimiertes Open-Source-LLM-Bereitstellungs-Framework, das den Bereitstellungsdurchsatz auf GPUs über Funktionen wie die Folgenden beschleunigen kann:

  • Optimierte Transformer-Implementierung mit PagedAttention

  • Kontinuierliche Batchverarbeitung zur Verbesserung des allgemeinen Bereitstellungsdurchsatzes

  • Verteilte Bereitstellung auf mehreren GPUs

Bei besonders rechenintensiven LLMs, die nicht auf einen einzelnen GPU-Knoten passen, können Sie das Modell auf mehreren GPU-Knoten bereitstellen. vLLM unterstützt die Ausführung von Arbeitslasten auf mehreren GPUs mit zwei Strategien:

  • Bei der Tensor-Parallelität werden die Matrixmultiplikationen in der Transformer-Schicht auf mehrere GPUs aufgeteilt. Diese Strategie erfordert jedoch ein schnelles Netzwerk aufgrund der Kommunikation zwischen den GPUs. Daher ist sie weniger geeignet für die Ausführung von Arbeitslasten über Knoten hinweg.

  • Bei der Pipeline-Parallelität wird das Modell nach Schicht oder vertikal aufgeteilt. Für diese Strategie ist keine ständige Kommunikation zwischen GPUs erforderlich. Sie ist daher eine bessere Option, wenn Modelle auf mehreren Knoten ausgeführt werden.

Sie können beide Strategien für die Bereitstellung mit mehreren Knoten verwenden. Wenn Sie beispielsweise zwei Knoten mit jeweils acht H100-GPUs verwenden, können Sie beide Strategien nutzen:

  • Bidirektionale Pipeline-Parallelität zum Sharden des Modells auf die beiden Knoten

  • Achtfache Tensor-Parallelität zum Sharden des Modells auf die acht GPUs auf jedem Knoten

Weitere Informationen finden Sie in der vLLM-Dokumentation.

Lernziele

  1. Bereiten Sie Ihre Umgebung mit einem Google Distributed Cloud-Cluster im Autopilot- oder Standardmodus vor.

  2. Stellen Sie vLLM auf mehreren Knoten in Ihrem Cluster bereit.

  3. Verwenden Sie vLLM, um das Modell über curl bereitzustellen.

Hinweise

  • Erstellen Sie ein Hugging Face-Konto, falls Sie noch keines haben.

Zugriff auf das Modell erhalten

Sie können die Modelle Llama 3.1 405B oder DeepSeek-R1 verwenden.

DeepSeek-R1

Zugriffstoken erstellen

Falls Sie noch keines haben, generieren Sie ein neues Hugging Face-Token:

  1. Klicken Sie auf Profil > Einstellungen > Zugriffstokens.

  2. Wählen Sie Neues Token aus.

  3. Geben Sie einen Namen Ihrer Wahl und eine Rolle von mindestens Read an.

  4. Wählen Sie Token generieren aus.

Llama 3.1 405B

Zugriffstoken erstellen

Falls Sie noch keines haben, generieren Sie ein neues Hugging Face-Token:

  1. Klicken Sie auf Profil > Einstellungen > Zugriffstokens.

  2. Wählen Sie Neues Token aus.

  3. Geben Sie einen Namen Ihrer Wahl und eine Rolle von mindestens Read an.

  4. Wählen Sie Token generieren aus.

Umgebung vorbereiten

So richten Sie Ihre Umgebung ein:

  1. Legen Sie auf der Administrator-Workstation die folgenden Parameter fest:

    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
    

    Ersetzen Sie die folgenden Werte:

    • PROJECT_ID: die Projekt-ID, die Ihrem Cluster zugeordnet ist.

    • HUGGING_FACE_TOKEN: das Hugging Face-Token, das im vorherigen Abschnitt Zugriff auf das Modell erhalten generiert wurde.

Kubernetes-Secret für Hugging Face-Anmeldedaten erstellen

Erstellen Sie mit dem folgenden Befehl ein Kubernetes-Secret, das das Hugging Face-Token enthält:

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

Ersetzen Sie KUBECONFIG durch den Pfad der kubeconfig-Datei für den Cluster, auf dem Sie das LLM hosten möchten.

Eigenes vLLM-Image mit mehreren Knoten erstellen

Um die knotenübergreifende Kommunikation für vLLM zu erleichtern, können Sie Ray verwenden. Das LeaderWorkerSet-Repository enthält ein Dockerfile mit einem Bash-Skript zum Konfigurieren von Ray mit vLLM.

Wenn Sie ein eigenes vLLM-Multi-Node-Image erstellen möchten, müssen Sie das LeaderWorkerSet-Repository klonen, ein Docker-Image mit dem bereitgestellten Dockerfile erstellen (das Ray für die knotenübergreifende Kommunikation konfiguriert) und dieses Image dann zur Bereitstellung in Google Distributed Cloud in Artifact Registry hochladen.

Container erstellen

So erstellen Sie den Container:

  1. Klonen Sie das LeaderWorkerSet-Repository:

    git clone https://github.com/kubernetes-sigs/lws.git
    
  2. Image erstellen

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

Übertragen Sie das Image per Push in Artifact Registry:

Damit Ihr Kubernetes-Deployment auf das Image zugreifen kann, speichern Sie es in Artifact Registry in Ihrem Google Cloud -Projekt:

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

LeaderWorkerSet installieren

Führen Sie den folgenden Befehl aus, um LWS zu installieren:

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

Prüfen Sie mit dem folgenden Befehl, ob der LeaderWorkerSet-Controller im Namespace lws-system ausgeführt wird:

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

Die Ausgabe sieht etwa so aus:

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

vLLM Model Server bereitstellen

So stellen Sie den vLLM-Modellserver bereit:

  1. Erstellen Sie das Manifest und wenden Sie es an. Das hängt davon ab, welches LLM Sie bereitstellen möchten.

    DeepSeek-R1

    1. Erstellen Sie ein YAML-Manifest (vllm-deepseek-r1-A3.yaml) für den vLLM-Modellserver:

      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. Wenden Sie das Manifest mit dem folgenden Befehl an:

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

    Llama 3.1 405B

    1. Erstellen Sie ein YAML-Manifest (vllm-llama3-405b-A3.yaml) für den vLLM-Modellserver:

      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. Wenden Sie das Manifest mit dem folgenden Befehl an:

      kubectl apply -f vllm-llama3-405b-A3.yaml \
          --kubeconfig KUBECONFIG
      
  2. Sehen Sie sich die Logs des ausgeführten Modellservers mit dem folgenden Befehl an:

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

    Die Ausgabe sollte in etwa so aussehen:

    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)
    

Modell bereitstellen

Richten Sie mit dem folgenden Befehl die Portweiterleitung zum Modell ein:

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

Mithilfe von curl mit dem Modell interagieren

So interagieren Sie mit dem Modell mithilfe von curl:

DeepSeek-R1

Senden Sie in einem neuen Terminal eine Anfrage an den 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
}'

Die Ausgabe sollte in etwa so aussehen:

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

Senden Sie in einem neuen Terminal eine Anfrage an den 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
}'

Die Ausgabe sollte in etwa so aussehen:

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

Nächste Schritte