Compartir GPUs con varias cargas de trabajo mediante MPS de NVIDIA


En esta página se explica cómo usar el servicio multiproceso (MPS) de CUDA para permitir que varias cargas de trabajo compartan un único acelerador de hardware de GPU NVIDIA en los nodos de Google Kubernetes Engine (GKE).

Información general

MPS de NVIDIA es una solución para compartir GPUs que permite que varios contenedores compartan un único hardware de GPU física de NVIDIA conectado a un nodo.

MPS de NVIDIA se basa en el servicio multiproceso de NVIDIA en CUDA. MPS de NVIDIA es una implementación alternativa y compatible con binarios de la API de CUDA diseñada para permitir de forma transparente que las aplicaciones de CUDA multiproceso cooperativas se ejecuten simultáneamente en un único dispositivo de GPU.

Con MPS de NVIDIA, puedes especificar el número máximo de contenedores compartidos de una GPU física. Este valor determina la cantidad de potencia de la GPU física que recibe cada contenedor en función de las siguientes características:

Para obtener más información sobre cómo se programan las GPUs con NVIDIA MPS y cuándo debes usar CUDA MPS, consulta el artículo Acerca de las soluciones para compartir GPUs en GKE.

A quién va dirigida esta guía

Las instrucciones de esta sección se aplican si eres uno de los siguientes:

  • Administrador de la plataforma: crea y gestiona un clúster de GKE, planifica los requisitos de infraestructura y recursos, y monitoriza el rendimiento del clúster.
  • Desarrollador de aplicaciones: diseña y despliega cargas de trabajo en clústeres de GKE. Si quieres obtener instrucciones para solicitar NVIDIA MPS con GPUs, consulta Desplegar cargas de trabajo que usen NVIDIA MPS con GPUs.

Requisitos

  • Versión de GKE: puedes habilitar el uso compartido de GPUs con NVIDIA MPS en clústeres Standard de GKE que ejecuten la versión 1.27.7-gke.1088000 de GKE o una posterior.
  • Tipo de GPU: puedes habilitar MPS de NVIDIA para todos los tipos de GPU de NVIDIA.

Antes de empezar

Antes de empezar, asegúrate de haber realizado las siguientes tareas:

  • Habilita la API de Google Kubernetes Engine.
  • Habilitar la API de Google Kubernetes Engine
  • Si quieres usar Google Cloud CLI para esta tarea, instálala y, a continuación, inicialízala. Si ya has instalado la gcloud CLI, obtén la versión más reciente ejecutando gcloud components update.
  • Asegúrate de tener suficiente cuota de GPU NVIDIA. Si necesitas más cuota, consulta el artículo Solicitar el aumento de una cuota.
  • Planifica la capacidad de tus GPUs en función de las necesidades de recursos de las cargas de trabajo y de la capacidad de la GPU subyacente.
  • Consulta las limitaciones de NVIDIA MPS con GPUs.

Habilitar MPS de NVIDIA con GPUs en clústeres de GKE

Como administrador de la plataforma, debes habilitar NVIDIA MPS con GPUs en un clúster de GKE Standard. Después, los desarrolladores de aplicaciones pueden desplegar cargas de trabajo para usar MPS de NVIDIA con GPUs. Para habilitar MPS de NVIDIA con GPUs en GKE, haz lo siguiente:

  1. Habilita MPS de NVIDIA con GPUs en un nuevo clúster de GKE.
  2. Instala los controladores de dispositivos de GPU NVIDIA (si es necesario).
  3. Verifica los recursos de GPU disponibles en tus nodos.

Habilitar MPS de NVIDIA con GPUs en un clúster de GKE

Puedes habilitar MPS de NVIDIA con GPUs al crear clústeres estándar de GKE. El grupo de nodos predeterminado del clúster tiene la función habilitada. Sin embargo, debes habilitar MPS de NVIDIA con GPUs cuando crees manualmente grupos de nodos en ese clúster.

Crea un clúster con NVIDIA MPS habilitado mediante la CLI de Google Cloud:

gcloud container clusters create CLUSTER_NAME \
    --location=CONTROL_PLANE_LOCATION \
    --cluster-version=CLUSTER_VERSION \
    --machine-type=MACHINE_TYPE \
    --accelerator=type=GPU_TYPE,count=GPU_QUANTITY,gpu-sharing-strategy=mps,max-shared-clients-per-gpu=CLIENTS_PER_GPU,gpu-driver-version=DRIVER_VERSION

Haz los cambios siguientes:

  • CLUSTER_NAME: el nombre del nuevo clúster.
  • CONTROL_PLANE_LOCATION: la ubicación de Compute Engine del plano de control de tu clúster. Proporciona una región para los clústeres regionales o una zona para los clústeres zonales. El tipo de GPU que utilices debe estar disponible en la región seleccionada.
  • CLUSTER_VERSION: la versión de GKE del plano de control y los nodos del clúster. Usa la versión 1.27.7-gke.1088000 de GKE o una posterior. También puedes especificar un canal de lanzamiento con esa versión de GKE mediante la marca --release-channel=RELEASE_CHANNEL.
  • MACHINE_TYPE: el tipo de máquina de Compute Engine de tus nodos.
  • GPU_TYPE: el tipo de GPU, que debe ser una plataforma de GPU NVIDIA, como nvidia-tesla-v100.
  • GPU_QUANTITY: número de GPUs físicas que se van a asociar a cada nodo del grupo de nodos predeterminado.
  • CLIENTS_PER_GPU: número máximo de contenedores que pueden compartir cada GPU física.
  • DRIVER_VERSION: la versión del controlador de NVIDIA que se va a instalar. Puede ser uno de los siguientes:
    • default: instala la versión predeterminada del controlador para tu versión de GKE.
    • latest: Instala la versión más reciente del controlador disponible para tu versión de GKE. Solo está disponible para los nodos que usan Container-Optimized OS.
    • disabled: omite la instalación automática de controladores. Debes instalar manualmente un controlador después de crear el grupo de nodos. Si omite gpu-driver-version, esta es la opción predeterminada.

Habilitar MPS de NVIDIA con GPUs en un nuevo grupo de nodos

Puedes habilitar MPS de NVIDIA con GPUs cuando crees manualmente grupos de nodos en un clúster de GKE. Crea un grupo de nodos con NVIDIA MPS habilitado mediante la CLI de Google Cloud:

gcloud container node-pools create NODEPOOL_NAME \
    --cluster=CLUSTER_NAME \
    --machine-type=MACHINE_TYPE \
    --location=CONTROL_PLANE_LOCATION \
    --accelerator=type=GPU_TYPE,count=GPU_QUANTITY,gpu-sharing-strategy=mps,max-shared-clients-per-gpu=CONTAINER_PER_GPU,gpu-driver-version=DRIVER_VERSION

Haz los cambios siguientes:

  • NODEPOOL_NAME: el nombre del nuevo grupo de nodos.
  • CLUSTER_NAME: el nombre de tu clúster, que debe ejecutar la versión 1.27.7-gke.1088000 de GKE o una posterior.
  • CONTROL_PLANE_LOCATION: la ubicación de Compute Engine del plano de control de tu clúster. Proporciona una región para los clústeres regionales o una zona para los clústeres zonales.
  • MACHINE_TYPE: el tipo de máquina de Compute Engine de tus nodos. Para las GPUs A100, usa un tipo de máquina A2. Para el resto de las GPUs, usa un tipo de máquina N1.
  • GPU_TYPE: el tipo de GPU, que debe ser una plataforma de GPU NVIDIA, como nvidia-tesla-v100.
  • GPU_QUANTITY: número de GPUs físicas que se van a asociar a cada nodo del grupo de nodos.
  • CONTAINER_PER_GPU: número máximo de contenedores que pueden compartir cada GPU física.
  • DRIVER_VERSION: la versión del controlador de NVIDIA que se va a instalar. Se puede definir lo siguiente:

    • default: instala la versión predeterminada del controlador para tu versión de GKE.
    • latest: Instala la versión más reciente del controlador disponible para tu versión de GKE. Solo está disponible para los nodos que usan Container-Optimized OS.
    • disabled: omite la instalación automática de controladores. Debes instalar manualmente un controlador después de crear el grupo de nodos. Si omite gpu-driver-version, esta es la opción predeterminada.

Instalar los controladores de dispositivos de GPU NVIDIA

Si has inhabilitado la instalación automática de controladores al crear el clúster o si usas una versión de GKE anterior a la 1.27.2-gke.1200, debes instalar manualmente un controlador de NVIDIA compatible para gestionar la división MPS de NVIDIA de las GPUs físicas. Para instalar los controladores, debes implementar un DaemonSet de instalación de GKE que los configure.

Para obtener instrucciones, consulta el artículo Instalar controladores de dispositivos de GPU NVIDIA.

Verificar los recursos de GPU disponibles

Puedes verificar que el número de GPUs de tus nodos coincide con el número que especificaste al habilitar NVIDIA MPS. También puedes verificar que el daemon de control de NVIDIA MPS se esté ejecutando.

Verificar los recursos de GPU disponibles en los nodos

Para verificar los recursos de GPU disponibles en tus nodos, ejecuta el siguiente comando:

kubectl describe nodes NODE_NAME

Sustituye NODE_NAME por el nombre de tu nodo.

El resultado debería ser similar al siguiente:

...
Capacity:
  ...
  nvidia.com/gpu:             3
Allocatable:
  ...
  nvidia.com/gpu:             3

En este resultado, el número de recursos de GPU del nodo es 3 debido a los siguientes valores:

  • El valor de max-shared-clients-per-gpu es 3.
  • El count de GPUs físicas que se van a conectar al nodo es 1. Si el count de GPUs físicas fuera 2, el resultado mostraría 6 recursos de GPU asignables, tres en cada GPU física.

Verificar que el daemon de control de MPS se está ejecutando

El complemento de dispositivo de GPU realiza una comprobación del estado en el daemon de control de MPS. Cuando el daemon de control de MPS esté en buen estado, podrás desplegar un contenedor.

Para comprobar el estado de MPS, ejecuta el siguiente comando:

kubectl logs -l k8s-app=nvidia-gpu-device-plugin -n kube-system --tail=100 | grep MPS

El resultado debería ser similar al siguiente:

I1118 08:08:41.732875       1 nvidia_gpu.go:75] device-plugin started
...
I1110 18:57:54.224832       1 manager.go:285] MPS is healthy, active thread percentage = 100.0
...

En el resultado, puede que vea que han ocurrido los siguientes eventos:

  • El error failed to start GPU device manager precede al error MPS is healthy. Este error es transitorio. Si ves el mensaje MPS is healthy, significa que el daemon de control está en ejecución.
  • El mensaje active thread percentage = 100.0 significa que todo el recurso de GPU físico tiene un hilo completamente activo.

Desplegar cargas de trabajo que usen MPS

Como operador de aplicaciones que despliega cargas de trabajo de GPU, puedes indicar a GKE que comparta unidades de MPS en la misma GPU física. En el siguiente archivo de manifiesto, solicitas una GPU física y defines max-shared-clients-per-gpu=3. La GPU física obtiene tres unidades de uso compartido de MPS e inicia un nvidia/samples:nbody trabajo con tres pods (contenedores) que se ejecutan en paralelo.

  1. Guarda el archivo de manifiesto como gpu-mps.yaml:

      apiVersion: batch/v1
      kind: Job
      metadata:
        name: nbody-sample
      spec:
        # Specifies the desired number of successfully finished Pods.
        completions: 3
        # Specifies the maximum desired number of Pods that should run at any given time.
        parallelism: 3
        template:
          spec:
            # Allows the Pod to share the host's IPC namespace.
            # The following field is required for containers to communicate with the MPS control daemon.
            hostIPC: true
            # Selects a node with the 'mps' GPU sharing strategy.
            nodeSelector:
              cloud.google.com/gke-gpu-sharing-strategy: mps
            containers:
              - name: nbody-sample
                # A sample CUDA application from NVIDIA.
                image: nvidia/samples:nbody
                # The command to run in the container.
                command: ["/tmp/nbody"]
                # Arguments for the command. Runs the nbody simulation in benchmark mode.
                args: ["-benchmark", "-i=5000"]
                resources:
                  limits:
                    # Requests one MPS sharing unit from a physical GPU.
                    nvidia.com/gpu: 1
            restartPolicy: "Never"
        backoffLimit: 1
    

    En este manifiesto:

    • hostIPC: true permite que los pods se comuniquen con el daemon de control de MPS. Es obligatorio. Sin embargo, ten en cuenta que la configuración hostIPC: true permite que el contenedor acceda al recurso del host, lo que conlleva riesgos de seguridad.
    • Se realizan 5000 iteraciones en el modo de comparativas.
  2. Aplica el archivo de manifiesto:

    kubectl apply -f gpu-mps.yaml
    
  3. Comprueba que todos los pods se estén ejecutando:

    kubectl get pods
    

    El resultado debería ser similar al siguiente:

    NAME                           READY   STATUS    RESTARTS   AGE
    nbody-sample-6948ff4484-54p6q   1/1     Running   0          2m6s
    nbody-sample-6948ff4484-5qs6n   1/1     Running   0          2m6s
    nbody-sample-6948ff4484-5zpdc   1/1     Running   0          2m5s
    
  4. Consulta los registros de los pods para verificar que el trabajo se ha completado:

    kubectl logs -l job-name=nbody-sample -f
    

    El resultado debería ser similar al siguiente:

    ...
    > Compute 8.9 CUDA device: [NVIDIA L4]
    18432 bodies, total time for 5000 iterations: 9907.976 ms
    = 171.447 billion interactions per second
    = 3428.941 single-precision GFLOP/s at 20 flops per interaction
    ...
    

    Como GKE ejecuta 50.000 iteraciones, el registro puede tardar varios minutos.

Limpieza

Para eliminar los Jobs y todos sus pods, ejecuta el siguiente comando:

kubectl delete job --all

Limitar la memoria de dispositivos fijados y los hilos activos con MPS de NVIDIA

De forma predeterminada, cuando se usa una GPU con NVIDIA MPS en GKE, se insertan las siguientes variables de entorno de CUDA en la carga de trabajo de la GPU:

  • CUDA_MPS_ACTIVE_THREAD_PERCENTAGE: esta variable indica el porcentaje de hilos disponibles que puede usar cada unidad compartida de MPS. De forma predeterminada, cada unidad de compartición de MPS de la GPU se establece en 100 / MaxSharedClientsPerGPU para obtener una parte igual del cálculo de la GPU en términos de multiprocesador de flujo.
  • CUDA_MPS_PINNED_DEVICE_MEM_LIMIT: esta variable limita la cantidad de memoria de GPU que puede asignar una unidad de uso compartido de MPS de la GPU. De forma predeterminada, cada unidad de compartición de MPS de la GPU se establece en total mem / MaxSharedClientsPerGPU para obtener una parte igual de la memoria de la GPU.

Para definir el límite de recursos de tus cargas de trabajo de GPU, configura estas variables de entorno de NVIDIA MPS:

  1. Revisa y compila la imagen del ejemplo de cuda-mps en GitHub.

  2. Guarda el siguiente archivo de manifiesto como cuda-mem-and-sm-count.yaml:

    apiVersion: v1
    kind: Pod
    metadata:
      name: cuda-mem-and-sm-count
    spec:
      # Allows the Pod to share the host's IPC namespace.
      # The following field is required for containers to communicate with the MPS control daemon.
      hostIPC: true
      # Selects a node with the 'mps' GPU sharing strategy.
      nodeSelector:
        cloud.google.com/gke-gpu-sharing-strategy: mps
      containers:
        - name: cuda-mem-and-sm-count
          # The custom image built from the cuda-mps example.
          image: CUDA_MPS_IMAGE
          # Grants the container extended privileges on the host machine.
          securityContext:
            privileged: true
          resources:
            limits:
              # Requests one MPS sharing unit from a physical GPU.
              nvidia.com/gpu: 1
    

    Sustituye CUDA_MPS_IMAGE por el nombre de la imagen que has creado para el ejemplo cuda-mps.

    NVIDIA MPS requiere que definas hostIPC:true en los pods. La configuración hostIPC:true permite que el contenedor acceda al recurso del host, lo que conlleva riesgos de seguridad.

  3. Aplica el archivo de manifiesto:

    kubectl apply -f cuda-mem-and-sm-count.yaml
    
  4. Consulta los registros de este pod:

    kubectl logs cuda-mem-and-sm-count
    

    En un ejemplo que usa NVIDIA Tesla L4 con gpu-sharing-strategy=mps y max-shared-clients-per-gpu=3, el resultado es similar al siguiente:

    For device 0:  Free memory: 7607 M, Total memory: 22491 M
    For device 0:  multiProcessorCount: 18
    

    En este ejemplo, la GPU NVIDIA Tesla L4 tiene 60 SM y 24 GB de memoria. Cada unidad de uso compartido de MPS obtiene aproximadamente un 33% de subprocesos activos y 8 GB de memoria.

  5. Actualiza el archivo de manifiesto para solicitar 2 nvidia.com/gpu:

      resources:
            limits:
              nvidia.com/gpu: 2
    

    El resultado debería ser similar al siguiente:

    For device 0:  Free memory: 15230 M, Total memory: 22491 M
    For device 0:  multiProcessorCount: 38
    
  6. Actualiza el archivo de manifiesto para anular las variables CUDA_MPS_ACTIVE_THREAD_PERCENTAGE y CUDA_MPS_PINNED_DEVICE_MEM_LIMIT:

      env:
        - name: CUDA_MPS_ACTIVE_THREAD_PERCENTAGE
          value: "20"
        - name: CUDA_MPS_PINNED_DEVICE_MEM_LIMIT
          value: "0=8000M"
    

    El resultado debería ser similar al siguiente:

    For device 0:  Free memory: 7952 M, Total memory: 22491 M
    For device 0:  multiProcessorCount: 10
    

Limitaciones

  • MPS en GPUs anteriores a Volta (P100) tiene capacidades limitadas en comparación con los tipos de GPU de Volta y posteriores.
  • Con NVIDIA MPS, GKE se asegura de que cada contenedor obtenga una cantidad limitada de memoria de dispositivo fijada y un hilo activo. Sin embargo, otros recursos, como el ancho de banda de la memoria, los codificadores o los decodificadores, no se incluyen en estos límites. Por lo tanto, los contenedores pueden afectar negativamente al rendimiento de otros contenedores si todos solicitan el mismo recurso ilimitado.
  • NVIDIA MPS tiene limitaciones en cuanto a la protección de memoria y la contención de errores. Te recomendamos que evalúes estas limitaciones para asegurarte de que son compatibles con tus cargas de trabajo.
  • NVIDIA MPS requiere que definas hostIPC:true en los pods. La configuración hostIPC:true permite que el contenedor acceda al recurso del host, lo que conlleva riesgos de seguridad.
  • GKE puede rechazar determinadas solicitudes de GPU al usar NVIDIA MPS para evitar comportamientos inesperados durante la asignación de capacidad.
  • El número máximo de contenedores que pueden compartir una sola GPU física con NVIDIA MPS es 48 (las GPUs anteriores a Volta solo admiten 16). Cuando planifiques tu configuración de MPS de NVIDIA, ten en cuenta las necesidades de recursos de tus cargas de trabajo y la capacidad de las GPUs físicas subyacentes para optimizar el rendimiento y la capacidad de respuesta.

Siguientes pasos