Compartilhe GPUs com várias cargas de trabalho usando o NVIDIA MPS


Nesta página, mostramos como usar o Serviço multiprocesso (MPS, na sigla em inglês) CUDA para permitir que várias cargas de trabalho compartilhem um único acelerador de hardware GPU NVIDIA no Google Kubernetes Engine (GKE).

Visão geral

O NVIDIA MPS é uma solução de compartilhamento de GPU que permite que vários contêineres compartilhem um único hardware de GPU NVIDIA física anexado a um nó.

O NVIDIA MPS depende do serviço multiprocesso da NVIDIA em CUDA. O NVIDIA MPS é uma implementação alternativa e compatível com binários da API CUDA projetada para permitir que aplicativos CUDA cooperativos de vários processos sejam executados simultaneamente em um único dispositivo GPU.

Com o NVIDIA MPS, é possível especificar o máximo de contêineres compartilhados de uma GPU física. Esse valor determina quanto da potência física da GPU cada contêiner recebe, em termos das seguintes características:

Para saber mais sobre como as GPUs programadas com NVIDIA MPS e quando usar CUDA MPS devem ser usadas, consulte Sobre soluções de compartilhamento de GPU no GKE.

Quem precisa usar este guia

As instruções nesta seção se aplicam a você se você se encaixa em uma destas categorias:

  • Administrador da plataforma: cria e gerencia um cluster do GKE, planeja requisitos de infraestrutura e recursos e monitora o desempenho do cluster.
  • Desenvolvedor de aplicativos: projeta e implanta cargas de trabalho em clusters do GKE. Se você quiser instruções para solicitar o NVIDIA MPS com GPUs, consulte Implantar cargas de trabalho que usam MPS da NVIDIA com GPUs.

Requisitos

  • Versão do GKE: é possível ativar o compartilhamento de GPU com o NVIDIA MPS em clusters do GKE Standard que executam o GKE versão 1.27.7-gke.1088000 e posterior.
  • Tipo de GPU: você pode ativar NVIDIA MPS para todos os tipos de GPU NVIDIA Tesla®.

Antes de começar

Antes de começar, verifique se você realizou as tarefas a seguir:

  • Ativar a API Google Kubernetes Engine.
  • Ativar a API Google Kubernetes Engine
  • Se você quiser usar a Google Cloud CLI para essa tarefa, instale e, em seguida, inicialize a CLI gcloud. Se você instalou a CLI gcloud anteriormente, instale a versão mais recente executando gcloud components update.

Ativar o NVIDIA MPS com GPUs em clusters do GKE¹

Como administrador da plataforma, você precisa ativar NVIDIA MPS com GPUs em um cluster padrão do GKE. Em seguida, os desenvolvedores de aplicativos podem implantar cargas de trabalho para usar o NVIDIA MPS com GPUs. Para ativar o NVIDIA MPS com GPUs no GKE, faça o seguinte:

  1. Ative o NVIDIA MPS com GPUs em um cluster do GKE.
  2. Instale os drivers de dispositivo da GPU NVIDIA (se necessário).
  3. Verifique os recursos da GPU disponíveis nos nós.

Ativar o NVIDIA MPS com GPUs em um cluster do GKE

É possível ativar o NVIDIA MPS com GPUs ao criar clusters do GKE Standard. O pool de nós padrão no cluster tem o recurso ativado. Ainda será necessário ativar o NVIDIA MPS com GPUs ao criar manualmente novos pools de nós nesse cluster.

Crie um cluster com o NVIDIA MPS ativado usando a CLI do Google Cloud:

gcloud container clusters create CLUSTER_NAME \
    --region=COMPUTE_REGION \
    --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

Substitua:

  • CLUSTER_NAME: o nome do novo cluster;
  • COMPUTE_REGION: a região do Compute Engine para o novo cluster. Para clusters de zona, especifique --zone=COMPUTE_ZONE. O tipo de GPU que você usa precisa estar disponível na zona selecionada.
  • CLUSTER_VERSION: a versão do GKE do plano e dos nós de controle do cluster. Use o GKE versão 1.27.7-gke.1088000 ou posterior. Se preferir, especifique um canal de lançamento com essa versão do GKE usando a sinalização --release-channel=RELEASE_CHANNEL.
  • MACHINE_TYPE: o tipo de máquina do Compute Engine para os nós.
  • GPU_TYPE: o tipo de GPU, que precisa ser uma plataforma de GPU NVIDIA Tesla, como nvidia-tesla-v100.
  • GPU_QUANTITY: o número de GPUs físicas a serem anexadas a cada nó no pool de nós padrão.
  • CLIENTS_PER_GPU: o número máximo de contêineres que podem compartilhar cada GPU física.
  • DRIVER_VERSION: a versão do driver NVIDIA a ser instalado. Será um dos seguintes valores:
    • default: instale a versão padrão do driver para a versão do GKE.
    • latest: instale a versão mais recente disponível do driver para a versão do GKE. Disponível apenas para nós que usam o Container-Optimized OS.
    • disabled: pula a instalação automática do driver. Você precisa instalar manualmente um driver depois de criar o pool de nós. Se você omitir gpu-driver-version, essa será a opção padrão.

Ativar o NVIDIA MPS com GPUs em um novo pool de nós

É possível ativar o NVIDIA MPS com GPUs ao criar manualmente novos pools de nós em um cluster do GKE. Crie um pool de nós com o NVIDIA MPS ativado usando a Google Cloud CLI:

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

Substitua:

  • NODEPOOL_NAME: o nome do novo pool de nós.
  • CLUSTER_NAME: o nome do cluster, que precisa executar a versão 1.27.7-gke.1088000 ou posterior do GKE.
  • COMPUTE_REGION: a região do Compute Engine do cluster. Para clusters zonais, especifique --zone=COMPUTE_ZONE.
  • MACHINE_TYPE: o tipo de máquina do Compute Engine para os nós. Para GPUs A100, use um tipo de máquina A2. Para todas as outras GPUs, use um tipo de máquina N1.
  • GPU_TYPE: o tipo de GPU, que precisa ser uma plataforma de GPU NVIDIA Tesla, como nvidia-tesla-v100.
  • GPU_QUANTITY: o número de GPUs físicas a serem anexadas a cada nó no pool de nós.
  • CONTAINER_PER_GPU: o número máximo de contêineres que podem compartilhar cada GPU física.
  • DRIVER_VERSION: a versão do driver NVIDIA a ser instalado. Será um dos seguintes valores:

    • default: instale a versão padrão do driver para a versão do GKE.
    • latest: instale a versão mais recente disponível do driver para a versão do GKE. Disponível apenas para nós que usam o Container-Optimized OS.
    • disabled: pula a instalação automática do driver. Você precisa instalar manualmente um driver depois de criar o pool de nós. Se você omitir gpu-driver-version, essa será a opção padrão.

Instalar drivers de dispositivo da GPU NVIDIA

Se você optar por desativar a instalação automática de drivers ao criar o cluster ou usar uma versão do GKE anterior a 1.27.2-gke.1200, instale manualmente um driver NVIDIA compatível para gerenciar a divisão do NVIDIA MPS das GPUs físicas. Para instalar os drivers, implante um DaemonSet de instalação do GKE.

Para instruções, consulte Como instalar drivers de dispositivo da GPU NVIDIA.

Verifique os recursos da GPU disponíveis

É possível verificar se o número de GPUs nos nós corresponde ao número especificado quando você ativou o NVIDIA MPS. Também é possível verificar se o daemon de controle do NVIDIA MPS está em execução.

Verifique os recursos de GPU disponíveis nos nós

Para verificar os recursos da GPU disponíveis nos nós, execute o seguinte comando:

kubectl describe nodes NODE_NAME

Substitua NODE_NAME pelo nome de um dos seus pods.

O resultado será assim:

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

Nesta saída, o número de recursos de GPU no nó é 3 devido aos seguintes valores:

  • O valor em max-shared-clients-per-gpu é 3.
  • O count de GPUs físicas a serem anexadas ao nó é 1. Se o count das GPUs físicas fosse 2, a saída mostraria 6 recursos alocáveis da GPU, três em cada GPU física.

Verificar se o daemon de controle do MPS está em execução

O plug-in do dispositivo GPU executa uma verificação de integridade no daemon de controle MPS. Quando o daemon de controle do MPS está íntegro, é possível implantar um contêiner.

Para verificar se o MPS é o status, execute o seguinte comando:

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

O resultado será assim:

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

Na saída, talvez os seguintes eventos tenham acontecido:

  • O erro failed to start GPU device manager é anterior ao erro MPS is healthy. Esse erro é temporário. Se você vir a mensagem MPS is healthy, o daemon de controle está em execução.
  • A mensagem active thread percentage = 100.0 significa que todo o recurso de GPU física tem uma linha de execução completamente ativa.

Implantar cargas de trabalho que usam MPS

Como um operador de aplicativo que está implantando cargas de trabalho de GPU, é possível dizer ao GKE para compartilhar unidades de compartilhamento de MPS na mesma GPU física. No manifesto a seguir, você solicita uma GPU física e define max-shared-clients-per-gpu=3. A GPU física recebe três unidades de compartilhamento de MPS e inicia um job nvidia/samples:nbody com três pods (contêineres) em execução paralela.

  1. Salve o manifesto como gpu-mps.yaml:

      apiVersion: batch/v1
      kind: Job
      metadata:
        name: nbody-sample
      spec:
        completions: 3
        parallelism: 3
        template:
          spec:
            hostIPC: true
            nodeSelector:
              cloud.google.com/gke-gpu-sharing-strategy: mps
            containers:
              - name: nbody-sample
                image: nvidia/samples:nbody
                command: ["/tmp/nbody"]
                args: ["-benchmark", "-i=5000"]
                resources:
                  limits:
                    nvidia.com/gpu: 1
            restartPolicy: "Never"
        backoffLimit: 1
    

    Nesse manifesto:

    • hostIPC: true permite que os pods se comuniquem com o daemon de controle do MPS. Ele é obrigatório. No entanto, considere que a configuração hostIPC: true permite que o contêiner acesse o recurso do host, o que introduz riscos de segurança.
    • 5.000 iterações são executadas no modo de comparação.
  2. Aplique o manifesto:

    kubectl apply -f gpu-mps.yaml
    
  3. Verifique se todos os pods estão em execução:

    kubectl get pods
    

    O resultado será assim:

    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. Verifique os registros dos pods para confirmar se o job foi concluído:

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

    O resultado será assim:

    ...
    > 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 o GKE executa 50.000 iterações, o registro pode levar vários minutos.

Limpar

Exclua os jobs e todos os pods dele executando o seguinte comando:

kubectl delete job --all

Limite a memória fixada do dispositivo e a linha de execução ativa com o NVIDIA MPS

Por padrão, ao usar GPU com NVIDIA MPS no GKE, as seguintes variáveis de ambiente CUDA são injetadas na carga de trabalho da GPU:

  • CUDA_MPS_ACTIVE_THREAD_PERCENTAGE: essa variável indica a porcentagem de linhas de execução disponíveis que cada unidade de compartilhamento de MPS pode usar. Por padrão, cada unidade de compartilhamento de MPS da GPU é definida como 100 / MaxSharedClientsPerGPU para receber uma fatia igual da computação da GPU em termos de multiprocessador de stream.
  • CUDA_MPS_PINNED_DEVICE_MEM_LIMIT: essa variável limita a quantidade de memória GPU que pode ser alocada por uma unidade de compartilhamento MPS da GPU. Por padrão, cada unidade de compartilhamento de MPS da GPU é definida como total mem / MaxSharedClientsPerGPU para receber uma fração igual da memória da GPU.

Para definir o limite de recursos para suas cargas de trabalho de GPU, configure estas variáveis de ambiente do NVIDIA MPS:

  1. Analise e crie a imagem do exemplo do cuda-mps no GitHub (em inglês).

  2. Salve o seguinte manifesto como cuda-mem-and-sm-count.yaml:

    apiVersion: v1
    kind: Pod
    metadata:
      name: cuda-mem-and-sm-count
    spec:
      hostIPC: true
      nodeSelector:
        cloud.google.com/gke-gpu-sharing-strategy: mps
      containers:
        - name: CUDA_MPS_IMAGE
          image: gcr.io/gracegao-gke-dev/cuda-mem-and-sm-count:latest
          securityContext:
            privileged: true
          resources:
            limits:
              nvidia.com/gpu: 1
    

    Substitua CUDA_MPS_IMAGE pelo nome da imagem que você criou para o exemplo cuda-mps.

    O NVIDIA MPS exige que você defina hostIPC:true nos pods. A configuração hostIPC:true permite que o contêiner acesse o recurso do host, o que apresenta riscos de segurança.

  3. Aplique o manifesto:

    kubectl apply -f cuda-mem-and-sm-count.yaml
    
  4. Verifique os registros deste pod:

    kubectl logs cuda-mem-and-sm-count
    

    Em um exemplo que usa NVIDIA Tesla® L4 com gpu-sharing-strategy=mps e max-shared-clients-per-gpu=3, a saída é semelhante à seguinte:

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

    Neste exemplo, a GPU NVIDIA Tesla® L4 tem 60 SM e 24 GB de memória. Cada unidade de compartilhamento de MPS recebe aproximadamente 33% de thread ativo e 8 GB de memória.

  5. Atualize o manifesto para solicitar a segunda nvidia.com/gpu:

      resources:
            limits:
              nvidia.com/gpu: 2
    

    O resultado será assim:

    For device 0:  Free memory: 15230 M, Total memory: 22491 M
    For device 0:  multiProcessorCount: 38
    
  6. Atualize o manifesto para substituir as variáveis CUDA_MPS_ACTIVE_THREAD_PERCENTAGE e 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"
    

    O resultado será assim:

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

Limitações

  • Os MPS em GPUs pré-Volta (P100) têm recursos limitados em comparação com os tipos de GPU em e após Volta.
  • Com o NVIDIA MPS, o GKE garante que cada contêiner tenha uma linha de execução ativa e uma memória fixada limitada no dispositivo. No entanto, outros recursos, como largura de banda da memória, codificadores ou decodificadores, não são capturados como parte desses limites de recursos. Como resultado, os contêineres poderão afetar negativamente o desempenho de outros contêineres se todos solicitarem o mesmo recurso ilimitado.
  • O NVIDIA MPS tem limitações de contenção de erros e proteção de memória. Recomendamos que você avalie essas limitações para garantir a compatibilidade com suas cargas de trabalho.
  • O NVIDIA MPS exige que você defina hostIPC:true nos pods. A configuração hostIPC:true permite que o contêiner acesse o recurso do host, o que apresenta riscos de segurança.
  • O GKE pode rejeitar determinadas solicitações de GPU ao usar o NVIDIA MPS para evitar comportamentos inesperados durante a alocação de capacidade. Para mais detalhes, consulte Limites de solicitação para soluções de compartilhamento de GPU.
  • O número máximo de contêineres que podem compartilhar uma única GPU física com o NVIDIA MPS é 48. A GPU pré-Volta oferece suporte apenas a 16. Ao planejar a configuração do NVIDIA MPS, considere as necessidades de recursos das cargas de trabalho e a capacidade das GPUs físicas subjacentes para otimizar o desempenho e a capacidade de resposta.
  • A configuração da API NVIDIA MPS só é compatível com o Google Cloud CLI ou o console do Google Cloud.

A seguir