Publica LLMs como DeepSeek-R1 671B o Llama 3.1 405B en equipos físicos


Descripción general

En esta guía, se muestra cómo entregar modelos de lenguaje grandes (LLM) de vanguardia, como DeepSeek-R1 671B o Llama 3.1 405B, en Google Distributed Cloud (solo software) en Bare Metal con unidades de procesamiento gráfico (GPU) en varios nodos.

En esta guía, se muestra cómo usar tecnologías portátiles de código abierto (Kubernetes, vLLM y la API de LeaderWorkerSet [LWS]) para implementar y entregar cargas de trabajo de IA/ML en clústeres de equipos físicos. Google Distributed Cloud extiende GKE para su uso en un entorno local y, al mismo tiempo, proporciona las ventajas del control granular, la escalabilidad, la resiliencia, la portabilidad y la rentabilidad de GKE.

Fondo

En esta sección, se describen las tecnologías clave que se usan en esta guía, incluidos los dos LLM que se usan como ejemplos: DeepSeek-R1 y Llama 3.1 405B.

DeepSeek-R1

DeepSeek-R1, un modelo grande de lenguaje de 671 mil millones de parámetros de DeepSeek, está diseñado para la inferencia lógica, el razonamiento matemático y la resolución de problemas en tiempo real en diversas tareas basadas en texto. Google Distributed Cloud se encarga de las demandas computacionales de DeepSeek-R1 y admite sus capacidades con recursos escalables, computación distribuida y redes eficientes.

Para obtener más información, consulta la documentación de DeepSeek.

Llama 3.1 405b

Llama 3.1 405B es un modelo de lenguaje grande de Meta diseñado para una amplia variedad de tareas de procesamiento de lenguaje natural, como la generación de texto, la traducción y la respuesta de preguntas. Google Distributed Cloud ofrece la infraestructura sólida necesaria para admitir las necesidades de entrenamiento y entrega distribuidos de modelos de esta escala.

Para obtener más información, consulta la documentación de Llama.

Servicio de Kubernetes administrado de Google Distributed Cloud

Google Distributed Cloud ofrece una amplia variedad de servicios, incluido Google Distributed Cloud (solo software) para Bare Metal, que es ideal para implementar y administrar cargas de trabajo de AA/ML en tu propio centro de datos. Google Distributed Cloud es un servicio administrado de Kubernetes que simplifica la implementación, el escalamiento y la administración de aplicaciones alojadas en contenedores. Google Distributed Cloud proporciona la infraestructura necesaria, incluidos recursos escalables, procesamiento distribuido y redes eficientes, para satisfacer las demandas computacionales de los LLM.

Para obtener más información sobre los conceptos clave de Kubernetes, consulta Comienza a aprender sobre Kubernetes. Para obtener más información sobre Google Distributed Cloud y cómo te ayuda a escalar, automatizar y administrar Kubernetes, consulta la descripción general de Google Distributed Cloud (solo software) para Bare Metal.

GPU

Las unidades de procesamiento gráfico (GPU) te permiten acelerar cargas de trabajo específicas, como el aprendizaje automático y el procesamiento de datos. Google Distributed Cloud admite nodos equipados con estas potentes GPUs, lo que te permite configurar tu clúster para obtener un rendimiento óptimo en las tareas de aprendizaje automático y procesamiento de datos. Google Distributed Cloud proporciona una variedad de opciones de tipo de máquina para la configuración de nodos, incluidos los tipos de máquinas con GPUs NVIDIA H100, L4 y A100.

Para obtener más información, consulta Cómo configurar y usar GPUs de NVIDIA.

LeaderWorkerSet (LWS)

LeaderWorkerSet (LWS) es una API de implementación de Kubernetes que aborda patrones de implementación comunes de cargas de trabajo de inferencia de IA/AA de varios nodos. La entrega en varios nodos aprovecha varios Pods, cada uno de los cuales puede ejecutarse en un nodo diferente, para controlar la carga de trabajo de inferencia distribuida. LWS permite tratar varios Pods como un grupo, lo que simplifica la administración de la entrega de modelos distribuidos.

vLLM y entrega con varios hosts

Cuando se entregan LLMs que requieren una gran cantidad de procesamiento, recomendamos usar vLLM y ejecutar las cargas de trabajo en varias GPUs.

vLLM es un framework de entrega de LLM de código abierto altamente optimizado que puede aumentar la capacidad de procesamiento de entrega en GPUs, con funciones como las siguientes:

  • Implementación optimizada de transformadores con PagedAttention

  • Agrupación en lotes continua para mejorar la capacidad de procesamiento general de la entrega

  • Entrega distribuida en varias GPUs

Con los LLMs que requieren una gran cantidad de procesamiento y no caben en un solo nodo de GPU, puedes usar varios nodos de GPU para entregar el modelo. vLLM admite la ejecución de cargas de trabajo en varias GPUs con dos estrategias:

  • El paralelismo de tensor divide las multiplicaciones de matrices en la capa del transformador en varias GPUs. Sin embargo, esta estrategia requiere una red rápida debido a la comunicación necesaria entre las GPUs, lo que la hace menos adecuada para ejecutar cargas de trabajo en varios nodos.

  • El paralelismo de canalización divide el modelo por capas o de forma vertical. Esta estrategia no requiere una comunicación constante entre las GPUs, lo que la convierte en una mejor opción cuando se ejecutan modelos en varios nodos.

Puedes usar ambas estrategias en el servicio de varios nodos. Por ejemplo, cuando usas dos nodos con ocho GPUs H100 cada uno, puedes usar ambas estrategias:

  • Paralelismo de canalización bidireccional para fragmentar el modelo en los dos nodos

  • Paralelismo de tensor de ocho vías para fragmentar el modelo en las ocho GPUs de cada nodo

Para obtener más información, consulta la documentación de vLLM.

Objetivos

  1. Prepara tu entorno con un clúster de Google Distributed Cloud en modo Autopilot o Standard.

  2. Implementa vLLM en varios nodos de tu clúster.

  3. Usa vLLM para entregar el modelo a través de curl.

Antes de comenzar

  • Crea una cuenta de Hugging Face, si todavía no la tienes.

Obtén acceso al modelo

Puedes usar los modelos Llama 3.1 405b o DeepSeek-R1.

DeepSeek-R1

Genera un token de acceso

Si aún no tienes uno, genera un nuevo token de Hugging Face:

  1. Haz clic en Tu perfil > Configuración > Tokens de acceso.

  2. Selecciona Token nuevo.

  3. Especifica el nombre que desees y un rol de al menos Read.

  4. Selecciona Generate un token.

Llama 3.1 405b

Genera un token de acceso

Si aún no tienes uno, genera un nuevo token de Hugging Face:

  1. Haz clic en Tu perfil > Configuración > Tokens de acceso.

  2. Selecciona Token nuevo.

  3. Especifica el nombre que desees y un rol de al menos Read.

  4. Selecciona Generate un token.

Prepare el entorno

Para configurar tu entorno, sigue estos pasos:

  1. Configura los siguientes parámetros en la estación de trabajo de administrador:

    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
    

    Reemplaza los siguientes valores:

    • PROJECT_ID: Es el ID del proyecto asociado a tu clúster.

    • HUGGING_FACE_TOKEN: Es el token de Hugging Face que se generó en la sección anterior Obtén acceso al modelo.

Crea un secreto de Kubernetes para las credenciales de Hugging Face

Crea un Secret de Kubernetes que contenga el token de Hugging Face con el siguiente comando:

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

Reemplaza KUBECONFIG por la ruta de acceso del archivo kubeconfig del clúster en el que deseas alojar el LLM.

Crea tu propia imagen de vLLM de varios nodos

Para facilitar la comunicación entre nodos para vLLM, puedes usar Ray. El repositorio de LeaderWorkerSet proporciona un Dockerfile, que incluye una secuencia de comandos de Bash para configurar Ray con vLLM.

Para crear tu propia imagen de vLLM de varios nodos, debes clonar el repositorio de LeaderWorkerSet, compilar una imagen de Docker con el Dockerfile proporcionado (que configura Ray para la comunicación entre nodos) y, luego, enviar esa imagen a Artifact Registry para su implementación en Google Distributed Cloud.

Compile el contenedor

Para compilar el contenedor, sigue estos pasos:

  1. Clona el repositorio de LeaderWorkerSet:

    git clone https://github.com/kubernetes-sigs/lws.git
    
  2. Compila la imagen.

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

Envía la imagen al registro de Artifact Registry.

Para asegurarte de que tu implementación de Kubernetes pueda acceder a la imagen, almacénala en Artifact Registry dentro de tu proyecto Google Cloud :

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

Instala LeaderWorkerSet

Para instalar LWS, ejecuta el siguiente comando:

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

Valida que el controlador LeaderWorkerSet se ejecute en el espacio de nombres lws-system con el siguiente comando:

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

El resultado es similar a este:

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

Implementa el servidor de modelos de vLLM

Para implementar el servidor del modelo de vLLM, sigue estos pasos:

  1. Crea y aplica el manifiesto según el LLM que quieras implementar.

    DeepSeek-R1

    1. Crea un manifiesto YAML, vllm-deepseek-r1-A3.yaml, para el servidor del modelo 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. Aplica el manifiesto ejecutando el siguiente comando:

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

    Llama 3.1 405b

    1. Crea un manifiesto YAML, vllm-llama3-405b-A3.yaml, para el servidor del modelo 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. Aplica el manifiesto ejecutando el siguiente comando:

      kubectl apply -f vllm-llama3-405b-A3.yaml \
          --kubeconfig KUBECONFIG
      
  2. Para ver los registros del servidor del modelo en ejecución, usa el siguiente comando:

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

    El resultado debería ser similar al siguiente:

    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)
    

Entrega el modelo

Ejecuta el siguiente comando para configurar la redirección de puertos al modelo:

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

Interactúa con el modelo con curl

Para interactuar con el modelo usando curl, sigue estas instrucciones:

DeepSeek-R1

En una terminal nueva, envía una solicitud al servidor:

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

El resultado debería ser similar al siguiente ejemplo:

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

En una terminal nueva, envía una solicitud al servidor:

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

El resultado debería ser similar al siguiente ejemplo:

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

¿Qué sigue?