GPUs mit NVIDIA MPS für mehrere Arbeitslasten freigeben

Auf dieser Seite wird erläutert, wie Sie den Multi-Process-Dienst (MPS) von CUDA verwenden, damit mehrere Arbeitslasten einen einzelnen NVIDIA-GPU-Hardwarebeschleuniger in Ihren Google Kubernetes Engine-Knoten (GKE) gemeinsam nutzen können.

Übersicht

NVIDIA MPS ist eine GPU-Freigabelösung, mit der mehrere Container eine einzelne physische NVIDIA-GPU-Hardware nutzen können, die an einen Knoten angehängt ist.

NVIDIA MPS basiert auf dem Multi-Process Service von NVIDIA für CUDA. NVIDIA MPS ist eine alternative, binär kompatible Implementierung der CUDA API, die dafür entwickelt wurde, kooperative Multiprozess-CUDA-Anwendungen transparent für die gleichzeitige Ausführung auf einem einzelnen GPU-Gerät zu ermöglichen.

Mit NVIDIA MPS können Sie die maximal gemeinsam genutzten Container einer physischen GPU angeben. Dieser Wert bestimmt, wie viel Leistung der physischen GPU jeder Container erhält, und zwar in Bezug auf die folgenden Merkmale:

Weitere Informationen zur Planung von GPUs mit NVIDIA MPS und der Verwendung von CUDA MPS finden Sie unter GPU-Freigabelösungen in GKE.

Zielgruppe dieses Leitfadens

Die Anleitung in diesem Abschnitt gilt für folgende Themen:

  • Plattformadministrator: Erstellt und verwaltet einen GKE-Cluster, plant Anforderungen für Infrastruktur und Ressourcen und überwacht die Leistung des Clusters.
  • Anwendungsentwickler: Entwirft Arbeitslasten in GKE-Clustern und stellt sie bereit. Eine Anleitung zum Anfordern von NVIDIA MPS mit GPUs finden Sie unter Arbeitslasten bereitstellen, die NVIDIA MPS mit GPUs verwenden.

Voraussetzungen

  • GKE-Version: Sie können die GPU-Freigabe mit NVIDIA MPS in GKE Standard-Clustern aktivieren, auf denen GKE-Version 1.27.7-gke.1088000 und höher ausgeführt wird.
  • GPU-Typ: Sie können NVIDIA MPS für alle NVIDIA-GPU-Typen aktivieren.

Hinweise

Führen Sie die folgenden Aufgaben aus, bevor Sie beginnen:

  • Aktivieren Sie die Google Kubernetes Engine API.
  • Google Kubernetes Engine API aktivieren
  • Wenn Sie die Google Cloud CLI für diesen Task verwenden möchten, müssen Sie die gcloud CLI installieren und dann initialisieren. Wenn Sie die gcloud CLI bereits installiert haben, rufen Sie die neueste Version mit dem Befehl gcloud components update ab. In früheren gcloud CLI-Versionen werden die Befehle in diesem Dokument möglicherweise nicht unterstützt.
  • Achten Sie darauf, dass Ihr NVIDIA-GPU-Kontingent ausreicht. Informationen zum Anfordern eines höheren Kontingents finden Sie unter Höheres Kontingent anfordern.
  • Planen Sie die GPU-Kapazität anhand der Ressourcenanforderungen der Arbeitslasten und der Kapazität der zugrunde liegenden GPU.
  • Lesen Sie die Einschränkungen für NVIDIA MPS mit GPUs.

NVIDIA MPS mit GPUs in GKE-Clustern aktivieren

Als Plattformadministrator müssen Sie NVIDIA MPS mit GPUs in einem GKE Standard-Cluster aktivieren. Anwendungsentwickler können dann Arbeitslasten bereitstellen, um NVIDIA MPS mit GPUs zu verwenden. So aktivieren Sie NVIDIA MPS mit GPUs in GKE:

  1. NVIDIA MPS mit GPUs in einem neuen GKE-Cluster aktivieren
  2. NVIDIA-GPU-Gerätetreiber installieren (falls erforderlich)
  3. Prüfen Sie die GPU-Ressourcen, die auf Ihren Knoten verfügbar sind.

NVIDIA MPS mit GPUs in einem GKE-Cluster aktivieren

Sie können NVIDIA MPS mit GPUs aktivieren, wenn Sie GKE-Standardcluster erstellen. Diese Funktion ist im Standardknotenpool im Cluster aktiviert. NVIDIA MPS mit GPUs muss noch aktiviert werden, wenn Sie neue Knotenpools in diesem Cluster manuell erstellen.

So erstellen Sie einen Cluster mit aktiviertem NVIDIA MPS mit der Google Cloud CLI:

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

Ersetzen Sie dabei Folgendes:

  • CLUSTER_NAME: Der Name des neuen Clusters.
  • CONTROL_PLANE_LOCATION: Der Compute Engine-Standort der Steuerungsebene des Clusters. Geben Sie für regionale Cluster eine Region und für zonale Cluster eine Zone an. Der verwendete GPU-Typ muss in der ausgewählten Region verfügbar sein.
  • CLUSTER_VERSION ist die GKE-Version für die Steuerungsebene und die Knoten des Clusters. Verwenden Sie die GKE-Version 1.27.7-gke.1088000 oder höher. Alternativ können Sie mit dem Flag --release-channel=RELEASE_CHANNEL eine Release-Version mit dieser GKE-Version angeben.
  • MACHINE_TYPE ist der Compute Engine-Maschinentyp für Ihre Knoten.
  • GPU_TYPE ist der GPU-Typ, der eine NVIDIA-GPU-Plattform wie nvidia-tesla-v100 sein muss.
  • GPU_QUANTITY ist die Anzahl der physischen GPUs, die an jeden Knoten im Standardknotenpool angehängt werden.
  • CLIENTS_PER_GPU ist die maximale Anzahl von Containern, die jede physische GPU gemeinsam nutzen können.
  • DRIVER_VERSION: die zu installierende NVIDIA-Treiberversion. Kann eines der Folgenden sein:
    • default: Installieren Sie die Standardtreiberversion für Ihre GKE-Version.
    • latest: Installieren Sie die neueste verfügbare Treiberversion für Ihre GKE-Version. Nur für Knoten verfügbar, die Container-Optimized OS verwenden.
    • disabled: Automatische Treiberinstallation überspringen. Sie müssen einen Treiber manuell installieren, nachdem Sie den Knotenpool erstellt haben. Wenn Sie gpu-driver-version weglassen, ist dies die Standardoption.

NVIDIA MPS mit GPUs in einem neuen Knotenpool aktivieren

Sie können NVIDIA MPS mit GPUs aktivieren, wenn Sie neue Knotenpools in einem GKE-Cluster manuell erstellen. Erstellen Sie mit der Google Cloud CLI einen Knotenpool, für den NVIDIA MPS aktiviert ist:

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

Ersetzen Sie dabei Folgendes:

  • NODEPOOL_NAME ist der Name des neuen Knotenpools.
  • CLUSTER_NAME ist der Name Ihres Clusters, auf dem die GKE-Version 1.27.7-gke.1088000 oder höher ausgeführt werden muss.
  • CONTROL_PLANE_LOCATION: Der Compute Engine-Standort der Steuerungsebene des Clusters. Geben Sie für regionale Cluster eine Region und für zonale Cluster eine Zone an.
  • MACHINE_TYPE ist der Compute Engine-Maschinentyp für Ihre Knoten. Verwenden Sie für A100-GPUs einen A2-Maschinentyp. Verwenden Sie für alle anderen GPUs einen N1-Maschinentyp.
  • GPU_TYPE ist der GPU-Typ, der eine NVIDIA-GPU-Plattform wie nvidia-tesla-v100 sein muss.
  • GPU_QUANTITY ist die Anzahl der physischen GPUs, die an jeden Knoten im Knotenpool angehängt werden sollen.
  • CONTAINER_PER_GPU ist die maximale Anzahl von Containern, die jede physische GPU gemeinsam nutzen können.
  • DRIVER_VERSION: die zu installierende NVIDIA-Treiberversion. folgende Arten von Werten sind möglich:

    • default: Installieren Sie die Standardtreiberversion für Ihre GKE-Version.
    • latest: Installieren Sie die neueste verfügbare Treiberversion für Ihre GKE-Version. Nur für Knoten verfügbar, die Container-Optimized OS verwenden.
    • disabled: Automatische Treiberinstallation überspringen. Sie müssen einen Treiber manuell installieren, nachdem Sie den Knotenpool erstellt haben. Wenn Sie gpu-driver-version weglassen, ist dies die Standardoption.

NVIDIA-GPU-Gerätetreiber installieren

Wenn Sie bei der Erstellung des Clusters die automatische Treiberinstallation deaktiviert haben oder wenn Sie eine GKE-Version vor 1.27.2-gke.1200 verwenden, müssen Sie manuell einen kompatiblen NVIDIA-Treiber installieren, der die NVIDIA MPS-Division der physischen GPUs verwaltet. Zum Installieren der Treiber stellen Sie ein GKE-Installations-DaemonSet bereit, das die Treiber einrichtet.

Eine Anleitung finden Sie unter NVIDIA-GPU-Gerätetreiber installieren.

Verfügbare GPU-Ressourcen prüfen

Sie können prüfen, ob die Anzahl der GPUs in Ihren Knoten mit der Anzahl übereinstimmt, die Sie bei der Aktivierung von NVIDIA MPS angegeben haben. Sie können auch prüfen, ob der NVIDIA MPS-Steuerungs-Daemon ausgeführt wird.

Prüfen Sie die auf Ihren Knoten verfügbaren GPU-Ressourcen

Führen Sie den folgenden Befehl aus, um die auf Ihren Knoten verfügbaren GPU-Ressourcen zu prüfen:

kubectl describe nodes NODE_NAME

Ersetzen Sie NODE_NAME durch Ihren Ingress-Namen.

Die Ausgabe sieht in etwa so aus:

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

In dieser Ausgabe beträgt die Anzahl der GPU-Ressourcen auf dem Knoten 3, da die folgenden Werte angegeben wurden:

  • Der Wert in max-shared-clients-per-gpu ist 3.
  • Die count der physischen GPUs, die dem Knoten hinzugefügt werden sollen, ist 1. Wenn die count der physischen GPUs 2 wäre, würde die Ausgabe 6 zuweisbare GPU-Ressourcen anzeigen, drei pro jeder physischen GPU.

Prüfen, ob der MPS-Kontrolldämon ausgeführt wird

Das GPU-Geräte-Plug-in führt eine Systemdiagnose für den MPS-Steuerungsdaemon durch. Wenn der MPS-Kontrolldämon fehlerfrei funktioniert, können Sie einen Container bereitstellen.

Führen Sie den folgenden Befehl aus, um den Status des MPS zu prüfen:

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

Die Ausgabe sieht in etwa so aus:

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

In der Ausgabe sehen Sie möglicherweise, dass die folgenden Ereignisse eingetreten sind:

  • Der Fehler failed to start GPU device manager geht dem Fehler MPS is healthy voraus. Dieser Fehler ist vorübergehend. Wenn die Meldung MPS is healthy angezeigt wird, wird der Steuerungs-Daemon ausgeführt.
  • Die Meldung active thread percentage = 100.0 bedeutet, dass die gesamte physische GPU-Ressource einen vollständig aktiven Thread hat.

Arbeitslasten bereitstellen, die MPS verwenden

Als Anwendungsoperator, der GPU-Arbeitslasten bereitstellt, können Sie GKE anweisen, MPS-Freigabeeinheiten auf derselben physischen GPU freizugeben. Im folgenden Manifest fordern Sie eine physische GPU an und legen max-shared-clients-per-gpu=3 fest. Die physische GPU erhält drei MPS-Freigabeeinheiten und startet einen nvidia/samples:nbody-Job mit drei parallel ausgeführten Pods (Containern).

  1. Speichern Sie das Manifest als 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
    

    In diesem Manifest:

    • hostIPC: true ermöglicht Pods die Kommunikation mit dem MPS-Steuerungs-Daemon. Das ist ein Pflichtfeld. Beachten Sie jedoch, dass die hostIPC: true-Konfiguration einem Container den Zugriff auf die Hostressource ermöglicht, was Sicherheitsrisiken mit sich bringt.
    • 5.000 Iterationen werden im Benchmark-Modus ausgeführt.
  2. Wenden Sie das Manifest an:

    kubectl apply -f gpu-mps.yaml
    
  3. Prüfen Sie, ob alle Pods ausgeführt werden:

    kubectl get pods
    

    Die Ausgabe sieht in etwa so aus:

    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. Prüfen Sie die Logs der Pods, um zu bestätigen, dass der Job abgeschlossen wurde:

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

    Die Ausgabe sieht in etwa so aus:

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

    Da GKE 50.000 Iterationen ausführt,kann es einige Minuten dauern, bis das Log fertig ist.

Bereinigen

Löschen Sie die Jobs und alle zugehörigen Pods mit dem folgenden Befehl:

kubectl delete job --all

Angepinnten Gerätespeicher und aktiven Thread mit NVIDIA MPS begrenzen

Wenn Sie GPUs mit NVIDIA MPS in GKE verwenden, werden standardmäßig die folgenden CUDA-Umgebungsvariablen in die GPU-Arbeitslast eingefügt:

  • CUDA_MPS_ACTIVE_THREAD_PERCENTAGE: Diese Variable gibt den Prozentsatz der verfügbaren Threads an, die jede MPS-Sharing-Einheit verwenden kann. Standardmäßig ist jede MPS-Freigabeeinheit der GPU auf 100 / MaxSharedClientsPerGPU festgelegt, um einen gleichmäßigen Anteil der GPU-Rechenleistung in Bezug auf den Streaming-Multiprozessor zu erhalten.
  • CUDA_MPS_PINNED_DEVICE_MEM_LIMIT: Diese Variable begrenzt die Menge an GPU-Arbeitsspeicher, die von einer MPS-Sharing-Einheit der GPU zugewiesen werden kann. Standardmäßig ist jede MPS-Freigabeeinheit der GPU auf total mem / MaxSharedClientsPerGPU festgelegt, um einen gleich großen Teil des GPU-Arbeitsspeichers zu erhalten.

Wenn Sie ein Ressourcenlimit für Ihre GPU-Arbeitslasten festlegen möchten, konfigurieren Sie die folgenden NVIDIA MPS-Umgebungsvariablen:

  1. Sehen Sie sich das cuda-mps-Beispiel in GitHub an und erstellen Sie das zugehörige Image.

  2. Speichern Sie das folgende Manifest als 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
    

    Ersetzen Sie CUDA_MPS_IMAGE durch den Namen des Images, das Sie für das cuda-mps-Beispiel erstellt haben.

    Für NVIDIA MPS müssen Sie hostIPC:true für Pods festlegen. Die hostIPC:true-Konfiguration ermöglicht einem Container den Zugriff auf die Hostressource, was Sicherheitsrisiken mit sich bringt.

  3. Wenden Sie das Manifest an:

    kubectl apply -f cuda-mem-and-sm-count.yaml
    
  4. Prüfen Sie die Logs für diesen Pod:

    kubectl logs cuda-mem-and-sm-count
    

    In einem Beispiel, in dem NVIDIA Tesla L4 mit gpu-sharing-strategy=mps und max-shared-clients-per-gpu=3 verwendet wird, sieht die Ausgabe so aus:

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

    In diesem Beispiel hat die NVIDIA Tesla L4-GPU 60 SM und 24 GB Arbeitsspeicher. Jede MPS-Sharing-Einheit erhält ungefähr 33% aktive Threads und 8 GB Arbeitsspeicher.

  5. Aktualisieren Sie das Manifest, um 2 nvidia.com/gpu anzufordern:

      resources:
            limits:
              nvidia.com/gpu: 2
    

    Die Ausgabe sieht in etwa so aus:

    For device 0:  Free memory: 15230 M, Total memory: 22491 M
    For device 0:  multiProcessorCount: 38
    
  6. Aktualisieren Sie das Manifest, um die Variablen CUDA_MPS_ACTIVE_THREAD_PERCENTAGE und CUDA_MPS_PINNED_DEVICE_MEM_LIMIT zu überschreiben:

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

    Die Ausgabe sieht in etwa so aus:

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

Beschränkungen

  • MPS auf GPUs vor Volta (P100) hat im Vergleich zu GPU-Typen in und nach Volta eingeschränkte Funktionen.
  • Mit NVIDIA MPS sorgt GKE dafür, dass jeder Container nur begrenzt angepinnten Gerätespeicher und aktive Threads erhält. Andere Ressourcen wie Speicherbandbreite, Encoder oder Decoder werden jedoch nicht als Teil dieser Ressourcenlimits erfasst. Daher können sich Container negativ auf die Leistung anderer Container auswirken, wenn sie alle dieselbe unbegrenzte Ressource anfordern.
  • NVIDIA MPS hat Einschränkungen für Speicherschutz und Fehlereindämmung. Wir empfehlen, diese Einschränkungen zu prüfen, um die Kompatibilität mit Ihren Arbeitslasten sicherzustellen.
  • Für NVIDIA MPS müssen Sie hostIPC:true für Pods festlegen. Die hostIPC:true-Konfiguration ermöglicht einem Container den Zugriff auf die Hostressource, was Sicherheitsrisiken mit sich bringt.
  • GKE kann bestimmte GPU-Anfragen bei Verwendung von NVIDIA MPS ablehnen, um unerwartetes Verhalten während der Kapazitätszuweisung zu verhindern.
  • Die maximale Anzahl von Containern, die eine einzelne physische GPU mit NVIDIA MPS gemeinsam nutzen können, beträgt 48 (Vor-Volta-GPUs unterstützen nur 16). Berücksichtigen Sie bei der Planung der NVIDIA MPS-Konfiguration den Ressourcenbedarf Ihrer Arbeitslasten und die Kapazität der zugrunde liegenden physischen GPUs, um die Leistung und die Reaktionsfähigkeit zu optimieren.

Nächste Schritte