Best Practices für Machine Learning Operations (MLOps) in Azure Kubernetes Service (AKS)

Gilt für: ✔️ AKS Automatic ✔️ AKS Standard

MLOps ist eine Reihe von Methoden für die Bereitstellung, Überwachung und Verwaltung von Machine Learning-Modellen in Produktionsumgebungen.

Wichtige bewährte Methoden für MLOps, die in diesem Artikel behandelt werden:

  • Infrastruktur als Code (IaC)
  • Containerisierung
  • Modellverwaltung und Versionsverwaltung
  • Automatisierung
  • Skalierbarkeit und Ressourcenmanagement
  • Zuverlässigkeit lang laufender Batchaufträge
  • Sicherheit und Compliance

In diesem Artikel werden Best Practices und Überlegungen beschrieben, die Sie bei der Verwendung von MLOps in AKS berücksichtigen sollten. Weitere Informationen zu MLOps finden Sie unter Machine Learning Operations (MLOps) für KI- und Machine Learning-Workflows.

Wählen Sie Ihren AKS-Modus für MLOps aus.

AKS unterstützt zwei Clustermodi: AKS Automatic and AKS Standard. Wählen Sie AKS Automatic aus, wenn Sie eine produktionsbereite Grundkonfiguration mit weniger laufendem Plattformmanagement wünschen. Wählen Sie AKS Standard aus, wenn Sie eine tiefere Kontrolle über die Clusterinfrastruktur- und Plattformkonfiguration benötigen.

Die MLOps-Methoden in diesem Artikel gelten für beide Modi. Die Implementierungsverantwortung unterscheidet sich jedoch vom Modus: AKS Automatic bietet mehr vorkonfigurierte Standardwerte, während AKS Standard in der Regel explizitere Plattformkonfiguration und Lebenszyklusbesitz erfordert.

Area AKS Automatik AKS Standard
Grundkonfiguration des Clusters Vorkonfigurierte Knotenpools, Netzwerkeinstellungen und Überwachung – sofort einsatzbereit Erfordert eine manuelle Konfiguration von Knotenpools, CNI und Observability Stack
Systemknotenpoolvorgänge Automatische Knotenbereitstellung und vom Dienst verwaltete Skalierung Der Operator muss die Größe des Knotenpools, Skalierungsregeln und Wartungsfenster konfigurieren.
Kontrollen der Sicherheitsbasislinie Standardmäßig aktiviert: Netzwerkrichtlinien, Pod-Sicherheitsstandards und Workload-Identität Operatoren müssen Netzwerkrichtlinien, Podsicherheit und Identitätseinstellungen explizit aktivieren und konfigurieren.
Netzwerkbasisplan Vorkonfigurierte Azure CNI-Überlagerung mit Standardingressmustern Volle Flexibilität beim Auswählen des CNI-Plug-Ins, Konfigurieren von benutzerdefinierten Eingangscontrollern und Definieren der Netzwerktopologie
Vorgänge und Upgrades Automatisches Knotenimage und Kubernetes-Versionsupgrades Betreiber planen und verwalten den Zeitpunkt von Upgrades und die Rollout-Strategie
Fokus der MLOps-Implementierung Validieren, verwalten und Standardwerte optimieren Entwerfen und Konfigurieren von Plattformsteuerelementen
GPU-Kapazität und -Planung Geeignete GPU-Kapazität kann anhand der Ressourcenanforderungen von Workloads und der Schedulingeinschränkungen automatisch bereitgestellt werden, vorbehaltlich der regionalen SKU-Verfügbarkeit und des Abonnementkontingents Operatoren erstellen und verwalten GPU-Knotenpools, Bezeichnungen, Taints, Autoscaler-Grenzwerte, Treiber und Geräte-Plug-In-Konfiguration
Verteilte Schulung Die Standardmäßige Kubernetes-Planung und automatische Knotenbereitstellung bieten einen Ausgangspunkt; Schulungsoperatoren, Gangplaner und Topologieoptimierung bleiben die Auswahlmöglichkeiten auf Workloadebene Operatoren installieren explizit Schulungsoperatoren und Scheduler und entwerfen GPU-Knotenpools, Netzwerke, Skalierung und Platzierung für verteilte Aufträge

Infrastruktur als Code (IaC)

Wichtige Erkenntnis: Definieren und Version IaC-Vorlagen für jede Phase Ihrer KI-Pipeline, um Konsistenz, Kosteneffizienz und schnellere Bereitstellungen sicherzustellen.

IaC ermöglicht eine konsistente und reproduzierbare Infrastrukturbereitstellung und -verwaltung für eine Reihe von Anwendungstypen. Bei unterschiedlichen Anwendungsbereitstellungen kann sich Ihre IaC-Implementierung in der gesamten KI-Pipeline ändern, da die Für das Ableiten, Bereitstellen, Trainieren und Optimieren von Modellen benötigte Rechenleistung und Ressourcen variieren können. Die Definition und Versionsverwaltung von IaC-Vorlagen für Ihre KI-Entwicklerteams kann dazu beitragen, Konsistenz und Kosteneffizienz für alle Auftragstypen zu gewährleisten und gleichzeitig Hardwareanforderungen zu klären und den Bereitstellungsprozess zu beschleunigen.

Bei AKS Automatic kann sich IaC stärker auf Workloaddefinitionen, Leitplanken für Richtlinien und die Konsistenz der Umgebung konzentrieren als auf Plattformstandardwerte. In AKS Standard enthält IaC häufig explizitere Clusterplattformeinstellungen wie Netzwerk, Skalierung und betriebliche Konfigurationsoptionen.

Containerisierung

Wichtige Erkenntnis: Paketmodellgewichtungen, Metadaten und Konfigurationen in Containerimages, um Portabilität, vereinfachte Versionsverwaltung und geringere Speicherkosten zu ermöglichen.

Das Verwalten von Modellgewichtungen, Metadaten und Konfigurationen in Containerimages ermöglicht Portabilität, vereinfachte Versionsverwaltung und verringerte Speicherkosten im Laufe der Zeit. Vorteile der Containerisierung:

  • Verwenden Sie vorhandene Container-Images, insbesondere für Large Language Models (LLMs) mit Millionen bis Milliarden von Parametern sowie für Stable-Diffusion-Modelle, die in sicheren Container-Registern gespeichert sind.
  • Vermeiden Sie einen einzelnen Fehlerpunkt in Ihrer Pipeline, indem Sie mehrere einfache Container verwenden, die die eindeutigen Abhängigkeiten für jede Aufgabe enthalten, anstatt ein großes Image zu verwalten.
  • Speichern Sie große Text- und Bilddatensätze außerhalb Des Basiscontainerimages, und verweisen Sie bei Bedarf zur Laufzeit auf sie. Erste Schritte mit dem Kubernetes AI Toolchain Operator (KAITO) zur Bereitstellung eines LLM auf AKS.

Container-Lieferkettenkontrollen bleiben in beiden Modi unerlässlich. Selbst mit standardmäßig vorkonfigurierten Plattformvorgaben in AKS Automatic bleiben die Herkunft von Images, deren Sicherheitsüberprüfung und die Laufzeithärtung zentrale MLOps-Aufgaben.

Dockerfile-Muster für ML-Workloads:

Verwenden Sie ein explizites, CUDA-fähiges Basisimagetag für GPU-Schulungsworkloads (z. B pytorch/pytorch:2.4.1-cuda12.4-cudnn9-runtime. ) anstelle eines nicht markierten Basisimageverweises. Fixieren Sie das Image für die Produktion per Digest, um reproduzierbare Builds und ein vorhersagbares CUDA/cuDNN-Verhalten sicherzustellen. Verwalten Sie getrennte CPU- und GPU-Image-Varianten, sodass die Platzierung durch den Scheduler und die Laufzeitabhängigkeiten explizit bleiben.

Modellverwaltung und Versionsverwaltung

Wichtige Erkenntnis: Versionieren Sie Ihre Modelle systematisch, um Konsistenz in allen Umgebungen zu gewährleisten und eine schnellere Iteration mit parametereffizienten Feinabstimmungsmethoden zu ermöglichen.

Modellverwaltung und Versionsverwaltung sind für das Nachverfolgen von Änderungen an Ihren Modellen im Laufe der Zeit unerlässlich. Durch die Versionierung Ihrer Modelle können Sie:

  • Sorgen Sie für eine einfache Bereitstellung in unterschiedlichen Umgebungen durch Konsistenz in Ihren Modellcontainern.
  • Verwenden Sie PEFT-Methoden (Parameter-Efficient Fine-Tuning), um einen Teil der Modellgewichtungen schneller zu durchlaufen und neue Versionen in schlanken Containern zu verwalten.

In AKS Automatic können vorkonfigurierte Plattformbasispläne die Umgebungsparität vereinfachen. In AKS Standard müssen Teams häufig die Parität durch plattform- und bereitstellungskonfiguration expliziter erzwingen.

Automatisierung

Wichtige Erkenntnis: Automatisieren Sie die Erfassung von Daten, modellieren Sie die Leistungsüberwachung und Umschulungspipelinen, um manuelle Fehler zu reduzieren und die Konsistenz im gesamten ML-Lebenszyklus sicherzustellen.

Die Automatisierung hilft dabei, manuelle Fehler zu reduzieren, die Effizienz zu steigern und die Konsistenz im gesamten ML-Lebenszyklus sicherzustellen. Vorteile der Aufgabenautomatisierung:

  • Integrieren Sie Warnungstools, um einen Vektoreinzug auszulösen, wenn neue Daten in Ihre Anwendung fließen.
  • Legen Sie Modellleistungsschwellenwerte fest, um Beeinträchtigungen nachzuverfolgen und Pipelines für erneutes Training auszulösen.

In beiden AKS-Modi sollten Sie neben Triggern für die Modellqualität auch Automatisierung für die Richtlinienvalidierung, die Erkennung von Konfigurationsabweichungen und die Release-Governance einbeziehen.

Skalierbarkeit und Ressourcenmanagement

Wichtige Herausforderung: Optimieren Sie die Ressourcennutzung durch verteilte Computer, automatische Skalierung und Notfallwiederherstellung, um unterschiedliche KI-Pipelineanforderungen kosteneffizient zu verarbeiten.

Skalierbarkeit und Ressourcenmanagement sind wichtig, um sicherzustellen, dass Ihre KI-Pipeline die Anforderungen Ihrer Anwendung verarbeiten kann. Vorteile der Optimierung des Ressourceneinsatzes:

  • Integrieren Sie Tools, die Ihre zugeordneten CPU-, GPU- und Speicherressourcen effizient über verteilte Computer und mehrere Parallelitätsebenen wie Daten, Modell und Pipeline-Parallelität verwenden.
  • Aktivieren Sie die automatische Skalierung Ihrer Rechenressourcen, um hohe Volumen an Modellanfragen in Spitzenzeiten zu bewältigen und sie in Schwachlastzeiten wieder zu reduzieren.
  • Planen Sie die Notfallwiederherstellung, indem Sie bewährte Methoden zur Ausfallsicherheit und Zuverlässigkeit von AKS ausführen.

AKS Automatic kann den Einrichtungsaufwand für allgemeine Skalierungs- und Betriebsmuster reduzieren, während AKS Standard eine tiefere Kontrolle über benutzerdefinierte Skalierungsarchitekturen bietet.

Lang andauernde Batchaufträge ausführen

Wichtigste Erkenntnis: Führen Sie zeitlich begrenzte Aufgaben als Kubernetes-JobJob aus, speichern Sie den Fortschritt außerhalb des Pods, behandeln Sie Beendigungssignale und gehen Sie davon aus, dass der Job möglicherweise mehr als einmal gestartet wird.

Upgrades von Knoten, Neustarts, Ereignisse beim Herunterskalieren, Ressourcendruck, Präemption und Infrastrukturausfälle können lang laufende Batch-Aufträge unterbrechen. Ein Kubernetes-Job ersetzt einen ausgefallenen oder gelöschten Pod, aber der Ersatz wird auf einem anderen Knoten gestartet, ohne die lokalen Dateien oder den Prozessspeicher des vorherigen Pods. Entwerfen Sie die Anwendung so, dass sie aus einem persistenten Zustand fortgesetzt werden kann und Arbeit sicher wiederholt werden kann.

Verwenden Sie diese Methoden für gewöhnliche CPU-, Arbeitsspeicher- oder E/A-intensive Batchaufträge. Die Anleitung zu Gang-Scheduling und Workload-Warteschlangen gilt, wenn bei einem verteilten Workload mehrere Worker gleichzeitig gestartet werden müssen oder wenn Teams gemeinsame Kontingente benötigen. Ein Einzelarbeits- oder unabhängig paralleler Batchauftrag benötigt normalerweise keine Gangplanung.

Konfigurieren von Prüfpunkten, Wiederholungen und Beenden

Im folgenden Beispiel wird eine Abfolge von Arbeitsaufgaben verarbeitet und das nächste Element auf einem Azure Files persistenten Volume aufgezeichnet. Stellt den Prüfpunkt nach dem Austausch eines Pods wieder her und speichert den Fortschritt, wenn der Prozess SIGTERM empfängt:

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: batch-checkpoints
spec:
  accessModes:
  - ReadWriteMany
  storageClassName: azurefile-csi
  resources:
    requests:
      storage: 10Gi
---
apiVersion: batch/v1
kind: Job
metadata:
  name: checkpointed-batch
spec:
  backoffLimit: 6
  activeDeadlineSeconds: 86400
  ttlSecondsAfterFinished: 86400
  podFailurePolicy:
    rules:
    - action: Ignore
      onPodConditions:
      - type: DisruptionTarget
  template:
    metadata:
      labels:
        app: checkpointed-batch
    spec:
      restartPolicy: Never
      terminationGracePeriodSeconds: 120
      containers:
      - name: worker
        image: ubuntu:24.04
        command:
        - /bin/bash
        - -c
        - |
          set -euo pipefail

          CHECKPOINT=/checkpoints/next-item
          NEXT_ITEM=1

          if [[ -f "${CHECKPOINT}" ]]; then
            NEXT_ITEM="$(cat "${CHECKPOINT}")"
            echo "Resuming at item ${NEXT_ITEM}."
          fi

          save_checkpoint() {
            printf '%s\n' "${NEXT_ITEM}" > "${CHECKPOINT}.tmp"
            mv "${CHECKPOINT}.tmp" "${CHECKPOINT}"
            echo "Saved checkpoint for item ${NEXT_ITEM}."
          }

          terminate() {
            echo "Received a termination signal."
            save_checkpoint
            exit 143
          }

          trap terminate TERM INT

          while (( NEXT_ITEM <= 1000 )); do
            echo "Processing item ${NEXT_ITEM}."
            sleep 10
            NEXT_ITEM=$((NEXT_ITEM + 1))
            save_checkpoint
          done

          echo "Batch completed."
        resources:
          requests:
            cpu: "1"
            memory: 1Gi
          limits:
            cpu: "1"
            memory: 1Gi
        volumeMounts:
        - name: checkpoints
          mountPath: /checkpoints
      volumes:
      - name: checkpoints
        persistentVolumeClaim:
          claimName: batch-checkpoints

Ersetzen Sie die Beispielschleife durch Ihre Batchanwendung, und wählen Sie Speicher aus, der den Durchsatz und die Wiederherstellungsanforderungen erfüllt. Verwenden Sie die Workloadidentität anstelle eingebetteter Anmeldeinformationen, wenn die Anwendungsprüfpunkte direkt an Azure Blob Storage oder einen anderen Azure Dienst übertragen werden.

Wenden Sie diese Zuverlässigkeitskontrollen gezielt an:

  • Checkpointing und Idempotenz: Speichern Sie den Fortschritt in Intervallen, die Ihrem Recovery Point Objective entsprechen. Speichern Sie Checkpoints und persistierte Ausgaben im persistenten Speicher, nicht im Container-Dateisystem, emptyDir, oder im knotenlokalen Speicher. Verwenden Sie atomische Schreibvorgänge, Idempotenzschlüssel, transaktionale Ausgabe oder Deduplizierung, da dasselbe Job-Programm manchmal mehr als einmal starten kann.
  • Neustartverhalten: Ein Job unterstützt restartPolicy: Never oder OnFailure. Mit Never führt ein fehlerhafter Container zu einem fehlgeschlagenen Pod, und der Job-Controller erstellt einen Ersatz. Mit OnFailure kann der Kubelet den Container im selben Pod neu starten. Verwenden Sie Never, wenn separate Pods bei Fehlern und separate Protokolle die Diagnose erleichtern, und sorgen Sie dafür, dass jeder der beiden Wege sicher wiederaufgenommen werden kann.
  • Wiederholungsgrenzwerte: Legen Sie backoffLimit basierend auf der Anzahl der vorübergehenden Fehler fest, die die Workload tolerieren kann. Verwenden Sie podFailurePolicy, um bei bekannten Exit-Codes, für die kein neuer Versuch erfolgen soll, sofort fehlzuschlagen oder, wie im Beispiel, zu verhindern, dass freiwillige Unterbrechungen das Retry-Budget verbrauchen. Eine Störung kann den Pod weiterhin anhalten; die Richtlinie ändert nur, wie der Job diesen Fehler wertet.
  • Stichtage: Legen Sie fest activeDeadlineSeconds , wann der Auftrag nach einer maximalen Gesamtlaufzeit beendet werden muss. Die Frist schließt Wiederholungsversuche ein und hat Vorrang vor backoffLimit. Legen Sie keinen Stichtag fest, der kürzer als die erwartete Laufzeit plus Wiederholungs- und Wiederherstellungszeit ist. Ein fehlgeschlagener Auftrag wird nicht automatisch als neuer Auftrag neu gestartet.
  • Ordnungsgemäße Beendigung: Behandeln Sie SIGTERM die Anwendung und legen terminationGracePeriodSeconds Sie lange genug fest, um die Annahme neuer Arbeiten zu beenden, die aktuelle Einheit sicher abzuschließen oder aufzugeben, die Ausgabe zu leeren und einen Prüfpunkt zu speichern. Kubernetes sendet SIGKILL, wenn der Prozess die Schonfrist überschreitet, sodass regelmäßige Checkpoints weiterhin erforderlich bleiben.
  • Bereinigung: Legen Sie ttlSecondsAfterFinished fest oder konfigurieren Sie CronJob-Verlaufslimits, um zu verhindern, dass sich abgeschlossene Jobs und Pods ansammeln. Bewahren Sie Job-Datensätze lange genug auf, um Protokolle zu erfassen und Vorfälle zu untersuchen.

Planen von Vertreibungen und Knotenwartung

Betrachten Sie das Blockieren von Verdrängungen nicht als wichtigste Wiederherstellungsstrategie für einen lang laufenden Auftrag. Für einen neu startbaren Batch-Job sollten Sie normalerweise kein PodDisruptionBudget (PDB) erstellen; der Job-Controller erstellt nach einer freiwilligen Unterbrechung einen Ersatz-Pod. Ein PDB schützt nur vor freiwilligen Auslagerungen und nicht vor Knotenausfall, ressourcenbedingter Auslagerung oder Präemption. Ein PDB, der null Unterbrechungen zulässt, kann auch Knotenabflüsse blockieren und AKS-Upgrades verzögern.

Verwenden Sie ein PDB nur, wenn eine parallele Batch-Workload eine Mindestanzahl gleichzeitig laufender Worker aufrechterhalten muss und Sie ihr Drain-Verhalten getestet haben. Verwenden Sie cluster-autoscaler.kubernetes.io/safe-to-evict: "false" ebenso nur für außergewöhnliche, nicht neu startbare Arbeiten. Die Anmerkung kann die Skalierung verhindern und die Kosten erhöhen, schützt jedoch nicht vor Knotenfehlern oder jedem Wartungsereignis.

Bei AKS-Upgrades werden alte Knoten vor dem Ersetzen gesperrt und entleert. Konfigurieren Sie geplante Wartungsfenster so, dass unterstützte AKS-Wartungsarbeiten auf Zeiträume mit geringeren Auswirkungen abgestimmt werden, stellen Sie jedoch sicher, dass die Workload weiterhin neu startbar bleibt, da der Zeitpunkt von Wartungen ungeplante Unterbrechungen nicht ausschließt. Führen Sie in AKS Standard Batchaufträge auf einem dedizierten Benutzerknotenpool aus, wenn sie eigene VM-Größen, Skalierungsgrenzwerte, Taints oder separate Upgradezeitpunkte benötigen. Verwenden Sie keine Spot-Knotenpools für Workloads, die sich nicht von einer Unterbrechung erholen können. Stellen Sie vor der Knotenwartung sicher, dass aktuelle Checkpoints nutzbar sind und dass ein anderer geeigneter Knotenpool über genügend Kontingent und Kapazität verfügt, um Ersatz-Pods auszuführen.

Überwachen Sie den Auftragsfortschritt und die Wiederherstellung

Verwenden Sie Kubernetes-Status, -Ereignisse und -Protokolle zusammen, wenn Sie einen lang andauernden Auftrag untersuchen:

kubectl get job checkpointed-batch --watch
kubectl describe job checkpointed-batch
kubectl get pods -l batch.kubernetes.io/job-name=checkpointed-batch
kubectl logs job/checkpointed-batch

Aktivieren Sie den verwalteten Azure Monitor-Dienst für Prometheus und Container Insights, um Protokolle aufzubewahren und den Auftragszustand mit Pod-Neustarts, Verdrängungen, Wartezeit, Knotenintegrität und Ressourcenauslastung zu korrelieren. Instrumentieren Sie die Anwendung mit Signalen für den Geschäftsfortschritt, z. B. abgeschlossene Elemente, fehlgeschlagene Elemente, letzte erfolgreiche Prüfpunktzeit, Verarbeitungsrate, Wiederholungsanzahl und geschätzte Abschlusszeit. Warnung bei einem fehlgeschlagenen Job, einem Job, der seine erwartete Dauer überschreitet, keinem Checkpoint innerhalb des Recovery Point Objective, wiederholten Pod-Ersetzungen, dauerhaft ausstehenden Pods und stagnierendem Verarbeitungsdurchsatz.

Sicherheit und Compliance

Wichtige Erkenntnis: Implementieren Sie CVE-Überprüfungen, Überwachungspfade und Compliance-Kontrollen, um Ihre Daten zu schützen und behördliche Anforderungen wie SOC 2, HIPAA und DSGVO zu erfüllen.

Sicherheit und Compliance sind für den Schutz Ihrer Daten von entscheidender Bedeutung und stellen sicher, dass Ihre KI-Pipeline gesetzliche Vorschriften erfüllt. Vorteile der Implementierung von Best Practices für Sicherheit und Compliance:

  • Integrieren Sie das Scannen nach Common Vulnerabilities and Exposures (CVEs), um häufige Schwachstellen in Container-Images von Open-Source-Modellen zu erkennen.
  • Verwenden Sie Microsoft Defender for Containers für Containerimages von Modellen, die in Ihrer Azure Container Registry gespeichert sind.
  • Pflegen Sie einen Überwachungspfad der erfassten Daten, Modelländerungen und Metriken, um mit Ihren Organisationsrichtlinien konform zu bleiben.
  • Unterstützt Compliance-Frameworks wie SOC 2 (durch die Audit-Protokollierung und Zugriffskontrollen von Azure), HIPAA (durch Verschlüsselung im Ruhezustand und bei der Übertragung sowie Netzwerkisolation) und die DSGVO (durch Optionen zur Datenresidenz und Richtlinien für die Zugriffsverwaltung).

In AKS Automatic verbessern standardmäßig vorkonfigurierte Sicherheitseinstellungen die grundlegende Sicherheitslage, jedoch bleiben Sicherheitskontrollen auf Modell-, Daten- und Pipeline-Ebene weiterhin erforderlich.

Planen von GPU-Workloads

Kernaussage: Stellen Sie GPUs über das NVIDIA-Device-Plugin bereit, fordern Sie GPUs explizit an und isolieren Sie teure GPU-Knoten mit Labels, Taints und Tolerations.

Kubernetes plant GPUs als erweiterte Ressourcen ein. Nachdem das NVIDIA-Geräte-Plug-in die GPUs auf einem Knoten registriert hat, fordert ein Pod eine GPU an, indem nvidia.com/gpu sowohl in resources.requests als auch in resources.limits festgelegt wird. Der folgende Pod fordert eine GPU an und zielt auf einen mit accelerator=nvidia gekennzeichneten Knotenpool ab:

apiVersion: v1
kind: Pod
metadata:
  name: gpu-training-pod
  labels:
    app: gpu-training
spec:
  restartPolicy: Never
  nodeSelector:
    accelerator: nvidia
  tolerations:
  - key: sku
    operator: Equal
    value: gpu
    effect: NoSchedule
  containers:
  - name: trainer
    image: nvcr.io/nvidia/cuda:12.4.1-runtime-ubuntu22.04
    command:
    - /bin/bash
    - -c
    - |
      set -e
      nvidia-smi
      echo "GPU is available to the training container."
    resources:
      requests:
        cpu: "1"
        memory: 2Gi
        nvidia.com/gpu: 1
      limits:
        cpu: "1"
        memory: 2Gi
        nvidia.com/gpu: 1

Erweiterte Ressourcen sind nicht überbelegt. Kubernetes behandelt einen GPU-Grenzwert als GPU-Anforderung, wenn nur der Grenzwert angegeben wird, das Festlegen beider Felder macht die Workloadabsicht jedoch explizit und hilft bei der Richtlinienüberprüfung.

Bereitstellen eines AKS GPU-Knotenpools

Erstellen Sie für AKS Standard einen dedizierten Benutzerknotenpool mit einer unterstützten GPU-VM-Größe von NVIDIA. Im folgenden Azure CLI-Beispiel wird ein automatisch skalierter Standard_NC4as_T4_v3-Knotenpool erstellt, das vom vorherigen Pod verwendete Label angewendet und die Knoten mit Taints versehen, um Workloads abzuhalten, die GPU-Knoten nicht ausdrücklich tolerieren:

RESOURCE_GROUP=myResourceGroup
AKS_CLUSTER=myAKSCluster

az aks nodepool add \
  --resource-group "$RESOURCE_GROUP" \
  --cluster-name "$AKS_CLUSTER" \
  --name gpunp \
  --mode User \
  --node-vm-size Standard_NC4as_T4_v3 \
  --node-count 0 \
  --enable-cluster-autoscaler \
  --min-count 0 \
  --max-count 4 \
  --labels accelerator=nvidia workload=training \
  --node-taints sku=gpu:NoSchedule

Stellen Sie vor dem Erstellen des Knotenpools sicher, dass die VM-SKU in der Clusterregion verfügbar ist und dass das Abonnement über ein ausreichendes regionales und VM-Familienkontingent verfügt. Wählen Sie eine NC-Reihengröße basierend auf GPU-Speicher, GPU-Anzahl, CPU-zu-GPU-Verhältnis, lokalem Speicher, Netzwerk und den vom Schulungsframework benötigten CUDA-Funktionen aus. Beispielsweise eignen sich NCas T4 v3-Größen für viele Einzel-GPU- und kleinere Schulungsarbeitslasten, während NC A100 v4-Größen größere Modelle und GPU-Konfigurationen mit mehreren Instanzen unterstützen.

AKS Automatic kann berechtigte GPU-Kapazität als Reaktion auf ausstehende Pods bereitstellen. Die Arbeitslast muss weiterhin nvidia.com/gpu anfordern, und alle Knotenselektoren, Affinitäten, Tolerationen, Topologieeinschränkungen, Abonnementkontingente und die regionale SKU-Verfügbarkeit müssen erfüllbar sein. Verwenden Sie AKS Standard, wenn Sie eine feste VM-SKU, einen benutzerdefinierten Knotenpoollebenszyklus oder eine detaillierte Kontrolle über die SKALIERUNG und Topologie des GPU-Pools benötigen.

Überprüfen oder Installieren des NVIDIA-Geräte-Plug-Ins

AKS GPU-Konfigurationen können verwaltete GPU-Treiber und Geräte-Plug-In-Integration bereitstellen. Überprüfen Sie die effektive Konfiguration, bevor Sie ein anderes Plug-In installieren:

kubectl get nodes -L accelerator,kubernetes.azure.com/agentpool
kubectl get daemonsets --all-namespaces | grep -i nvidia
kubectl describe node | grep -A5 -E "Capacity:|Allocatable:|nvidia.com/gpu"

Führen Sie nicht mehrere NVIDIA-Geräte-Plug-In DaemonSets auf denselben Knoten aus. Wenn Ihre AKS-Konfiguration das Plug-In nicht verwaltet, registriert das folgende eigenständige DaemonSet NVIDIA GPUs mit dem Kubelet. Installieren Sie eine Geräte-Plug-In-Version, die von Ihrem NVIDIA-Treiber und Kubernetes-Versionen unterstützt wird:

apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: nvidia-device-plugin
  namespace: kube-system
  labels:
    app.kubernetes.io/name: nvidia-device-plugin
spec:
  selector:
    matchLabels:
      app.kubernetes.io/name: nvidia-device-plugin
  updateStrategy:
    type: RollingUpdate
  template:
    metadata:
      labels:
        app.kubernetes.io/name: nvidia-device-plugin
    spec:
      priorityClassName: system-node-critical
      nodeSelector:
        accelerator: nvidia
      tolerations:
      - operator: Exists
      containers:
      - name: nvidia-device-plugin
        image: nvcr.io/nvidia/k8s-device-plugin:v0.17.1
        args:
        - --fail-on-init-error=false
        securityContext:
          allowPrivilegeEscalation: false
          capabilities:
            drop:
            - ALL
        volumeMounts:
        - name: device-plugin
          mountPath: /var/lib/kubelet/device-plugins
      volumes:
      - name: device-plugin
        hostPath:
          path: /var/lib/kubelet/device-plugins
          type: Directory

Vergewissern Sie sich, dass nvidia.com/gpu bei jedem GPU-Knoten unter den zuweisbaren Ressourcen angezeigt wird, bevor Sie Trainingsaufträge übermitteln.

Partition A100- und H100-GPUs mit MIG

NVIDIA Multi-Instance GPU (MIG) kann eine unterstützte A100- oder H100-GPU in isolierte GPU-Instanzen partitionieren. MIG ist nützlich für die Hyperparameteroptimierung und kleinere Feinabstimmungsaufträge, für die keine gesamte physische GPU erforderlich ist. Verwenden Sie vollständige GPUs für Workloads, für die alle GPU-Speicher, maximale Verbindungsbandbreite oder ein Profil erforderlich sind, das von der installierten GPU nicht unterstützt wird.

Verwenden Sie den NVIDIA GPU-Operator und den MIG-Manager, wenn Sie deklarative MIG-Lebenszyklusverwaltung benötigen. Der Betreiber sollte das Geräte-Plug-In und die MIG-Konfiguration für die betroffenen Knoten besitzen; kombinieren Sie es nicht mit einem zweiten eigenständigen Geräte-Plug-In. Die genauen Profilnamen und die Anzahl der Instanzen hängen vom GPU-Modell und der Speicherkapazität ab. Die folgende Konfiguration erstellt sieben 1g.10gb Instanzen für unterstützte 80-GB A100- oder H100-Konfigurationen:

apiVersion: v1
kind: Namespace
metadata:
  name: gpu-operator
---
apiVersion: v1
kind: ConfigMap
metadata:
  name: mig-parted-config
  namespace: gpu-operator
data:
  config.yaml: |
    version: v1
    mig-configs:
      all-disabled:
      - devices: all
        mig-enabled: false
      hptuning-1g10gb:
      - devices: all
        mig-enabled: true
        mig-devices:
          "1g.10gb": 7
---
apiVersion: v1
kind: Namespace
metadata:
  name: ml-training
---
apiVersion: batch/v1
kind: Job
metadata:
  name: mig-hyperparameter-trial
  namespace: ml-training
  labels:
    workload: hyperparameter-tuning
spec:
  backoffLimit: 2
  template:
    metadata:
      labels:
        workload: hyperparameter-tuning
    spec:
      restartPolicy: Never
      nodeSelector:
        accelerator: nvidia
        nvidia.com/mig.config: hptuning-1g10gb
        nvidia.com/mig.config.state: success
      tolerations:
      - key: sku
        operator: Equal
        value: gpu
        effect: NoSchedule
      containers:
      - name: trial
        image: nvcr.io/nvidia/cuda:12.4.1-runtime-ubuntu22.04
        command:
        - /bin/bash
        - -c
        - |
          set -e
          nvidia-smi
          echo "Starting one hyperparameter trial on a MIG instance."
          sleep 30
        resources:
          requests:
            cpu: "2"
            memory: 8Gi
            nvidia.com/mig-1g.10gb: 1
          limits:
            cpu: "2"
            memory: 8Gi
            nvidia.com/mig-1g.10gb: 1

Konfigurieren Sie den MIG-Manager des GPU-Operators für die Nutzung der mig-parted-config ConfigMap, verwenden Sie die mixed MIG-Strategie, wenn Workloads benannte MIG-Profile anfordern, und bezeichnen Sie dann die Zielknoten:

kubectl label nodes \
  -l accelerator=nvidia \
  nvidia.com/mig.config=hptuning-1g10gb \
  --overwrite

Das Ändern eines MIG-Layouts unterbricht Workloads, die bereits die GPU verwenden. Sperren Sie den Zielknoten und leeren Sie ihn, wenden Sie die Konfiguration während eines Wartungsfensters an, und vergewissern Sie sich, dass das angeforderte Profil in den zuweisbaren Ressourcen des Knotens vorhanden ist, bevor Sie Jobs einreichen.

Isolieren von GPU-Workloads mit Taints und Tolerationen

Ein NoSchedule Taint hält gewöhnliche Anwendungspods von teuren GPU-Knoten fern. Im Beispiel zum Erstellen des Knotenpools wird sku=gpu:NoSchedule angewendet. Der folgende vollständige Job enthält die passende Toleration und einen Node-Selector:

apiVersion: batch/v1
kind: Job
metadata:
  name: isolated-gpu-training
spec:
  backoffLimit: 3
  template:
    metadata:
      labels:
        app: isolated-gpu-training
    spec:
      restartPolicy: Never
      nodeSelector:
        accelerator: nvidia
        workload: training
      tolerations:
      - key: sku
        operator: Equal
        value: gpu
        effect: NoSchedule
      containers:
      - name: trainer
        image: nvcr.io/nvidia/cuda:12.4.1-runtime-ubuntu22.04
        command:
        - /bin/bash
        - -c
        - |
          set -e
          nvidia-smi
          sleep 60
        resources:
          requests:
            cpu: "4"
            memory: 16Gi
            nvidia.com/gpu: 1
          limits:
            cpu: "4"
            memory: 16Gi
            nvidia.com/gpu: 1

Eine Toleration erlaubt die Einplanung, erzwingt jedoch nicht, dass der Pod auf einem GPU-Knoten eingeplant wird. Kombinieren Sie Tolerierungen mit einer GPU-Ressourcenanforderung und einem Knotenselektor oder einer Knotenaffinität. Stellen Sie sicher, dass erforderliche Plattformdaemonsets wie Netzwerk, Überwachung, Speicher und das Geräte-Plug-In den GPU-Taint tolerieren.

Verteilte Trainingsworkloads ausführen

Kernaussage: Verwenden Sie einen Trainingsoperator, um Worker-Identitäten und den Joblebenszyklus zu verwalten, die Netzwerkkommunikation zwischen mehreren Knoten zu validieren und Gang Admission zu verwenden, wenn alle Worker gemeinsam starten müssen.

Verteilte Schulungen verwenden mehrere Prozesse, um Daten, Modellstatus oder Pipelinephasen aufzuteilen. In Kubernetes kann ein Operator Worker-Pods erstellen, die Rendezvous-Konfiguration einfügen, den Status der Replikate überwachen, fehlgeschlagene Worker neu starten und den Job bereinigen. Installieren und versionieren Sie Schulungsoperatoren über die IaC- und Release-Prozesse Ihrer Plattform, anstatt einzelnen Teams zu erlauben, unkontrollierte clusterweite CRDs zu installieren.

Erstellen eines tragbaren CUDA-Schulungsimages

Das folgende Dockerfile erweitert das im Abschnitt zur Containerisierung beschriebene Dockerfile-Muster. Das Bild enthält die CUDA-Laufzeit- und PyTorch-Bibliotheken, während der kompatible NVIDIA-Treiber auf dem AKS GPU-Knoten verbleibt:

FROM pytorch/pytorch:2.4.1-cuda12.4-cudnn9-runtime

WORKDIR /workspace

COPY requirements.txt .
RUN python -m pip install --no-cache-dir -r requirements.txt

COPY train.py .

ENTRYPOINT ["python", "/workspace/train.py"]

Legen Sie das Basis-Image in Produktionsumgebungen über einen unveränderlichen Digest fest. Behalten Sie Datasets bei und ändern Sie häufig Prüfpunkte außerhalb des Bilds, und scannen Sie das resultierende Bild, bevor Sie es an Azure Container Registry verschieben.

Einen PyTorchJob ausführen

Der Kubeflow Training Operator stellt die CRD PyTorchJob bereit. Installieren Sie eine Schulungsoperator-Version, die mit Ihrer Kubernetes-Version kompatibel ist, bevor Sie dieses Manifest anwenden. Im folgenden Beispiel mit zwei Replikaten wird eine NCCL-All-Reduce-Operation über einen Master und einen Worker hinweg ausgeführt:

apiVersion: v1
kind: Namespace
metadata:
  name: ml-training
  labels:
    purpose: ml-training
---
apiVersion: kubeflow.org/v1
kind: PyTorchJob
metadata:
  name: pytorch-nccl-example
  namespace: ml-training
spec:
  runPolicy:
    cleanPodPolicy: None
  pytorchReplicaSpecs:
    Master:
      replicas: 1
      restartPolicy: OnFailure
      template:
        metadata:
          labels:
            training-job: pytorch-nccl-example
        spec:
          nodeSelector:
            accelerator: nvidia
          tolerations:
          - key: sku
            operator: Equal
            value: gpu
            effect: NoSchedule
          containers:
          - name: pytorch
            image: pytorch/pytorch:2.4.1-cuda12.4-cudnn9-runtime
            command:
            - python
            - -c
            - |
              import os
              import torch
              import torch.distributed as dist

              torch.cuda.set_device(0)
              dist.init_process_group(backend="nccl")
              value = torch.tensor(
                  [float(dist.get_rank() + 1)],
                  device="cuda"
              )
              dist.all_reduce(value)
              print(
                  f"rank={dist.get_rank()} "
                  f"world_size={dist.get_world_size()} "
                  f"all_reduce_sum={value.item()}"
              )
              dist.destroy_process_group()
            env:
            - name: NCCL_DEBUG
              value: INFO
            - name: NCCL_SOCKET_IFNAME
              value: eth0
            - name: NCCL_IB_DISABLE
              value: "1"
            - name: TORCH_NCCL_ASYNC_ERROR_HANDLING
              value: "1"
            resources:
              requests:
                cpu: "4"
                memory: 16Gi
                nvidia.com/gpu: 1
              limits:
                cpu: "4"
                memory: 16Gi
                nvidia.com/gpu: 1
    Worker:
      replicas: 1
      restartPolicy: OnFailure
      template:
        metadata:
          labels:
            training-job: pytorch-nccl-example
        spec:
          nodeSelector:
            accelerator: nvidia
          tolerations:
          - key: sku
            operator: Equal
            value: gpu
            effect: NoSchedule
          containers:
          - name: pytorch
            image: pytorch/pytorch:2.4.1-cuda12.4-cudnn9-runtime
            command:
            - python
            - -c
            - |
              import os
              import torch
              import torch.distributed as dist

              torch.cuda.set_device(0)
              dist.init_process_group(backend="nccl")
              value = torch.tensor(
                  [float(dist.get_rank() + 1)],
                  device="cuda"
              )
              dist.all_reduce(value)
              print(
                  f"rank={dist.get_rank()} "
                  f"world_size={dist.get_world_size()} "
                  f"all_reduce_sum={value.item()}"
              )
              dist.destroy_process_group()
            env:
            - name: NCCL_DEBUG
              value: INFO
            - name: NCCL_SOCKET_IFNAME
              value: eth0
            - name: NCCL_IB_DISABLE
              value: "1"
            - name: TORCH_NCCL_ASYNC_ERROR_HANDLING
              value: "1"
            resources:
              requests:
                cpu: "4"
                memory: 16Gi
                nvidia.com/gpu: 1
              limits:
                cpu: "4"
                memory: 16Gi
                nvidia.com/gpu: 1

Ersetzen Sie für die Produktionsschulung den Inlinetest durch Ihr Versionsschulungsbild und -skript. Richten Sie die Anzahl der Replikate an die Anzahl der GPUs und Prozesse aus, die vom Trainings-Framework benötigt werden.

Führen Sie einen TFJob aus.

Der Schulungsoperator stellt auch die TFJob CRD bereit. Im folgenden Beispiel werden synchrone TensorFlow-Schulungen über zwei GPU-Mitarbeiter hinweg ausgeführt, indem Sie Folgendes verwenden MultiWorkerMirroredStrategy:

apiVersion: v1
kind: Namespace
metadata:
  name: ml-training
  labels:
    purpose: ml-training
---
apiVersion: kubeflow.org/v1
kind: TFJob
metadata:
  name: tensorflow-multiworker-example
  namespace: ml-training
spec:
  runPolicy:
    cleanPodPolicy: None
  tfReplicaSpecs:
    Worker:
      replicas: 2
      restartPolicy: OnFailure
      template:
        metadata:
          labels:
            training-job: tensorflow-multiworker-example
        spec:
          nodeSelector:
            accelerator: nvidia
          tolerations:
          - key: sku
            operator: Equal
            value: gpu
            effect: NoSchedule
          containers:
          - name: tensorflow
            image: tensorflow/tensorflow:2.16.1-gpu
            command:
            - python
            - -c
            - |
              import tensorflow as tf

              strategy = tf.distribute.MultiWorkerMirroredStrategy()
              print("workers:", strategy.num_replicas_in_sync)

              with strategy.scope():
                  model = tf.keras.Sequential([
                      tf.keras.layers.Input(shape=(32,)),
                      tf.keras.layers.Dense(64, activation="relu"),
                      tf.keras.layers.Dense(1)
                  ])
                  model.compile(
                      optimizer="adam",
                      loss="mean_squared_error"
                  )

              features = tf.random.normal([4096, 32])
              labels = tf.random.normal([4096, 1])
              dataset = (
                  tf.data.Dataset.from_tensor_slices((features, labels))
                  .shuffle(4096)
                  .repeat()
                  .batch(64)
              )
              model.fit(dataset, epochs=2, steps_per_epoch=32)
            resources:
              requests:
                cpu: "4"
                memory: 16Gi
                nvidia.com/gpu: 1
              limits:
                cpu: "4"
                memory: 16Gi
                nvidia.com/gpu: 1

Definieren Sie für das Parameter-Server-Training die Replikattypen Chief, Worker und PS gemäß der TensorFlow-Verteilungsstrategie. Messen Sie die resultierenden Netzwerk- und Parameterserverengpässe, bevor Sie die Replikatanzahl erhöhen.

Optimieren eines Modells mit KAITO

KAITO bietet eine auf AKS ausgerichtete Workspace-Abstraktion für Modellbereitstellung und Feinabstimmung. Aktivieren Sie das KAITO-Add-on oder installieren Sie eine kompatible KAITO-Version, bevor Sie ein Workspace anwenden. Stellen Sie sicher, dass die ausgewählte Voreinstellung, VM-SKU und Feinabstimmungsmethode von der installierten KAITO-Version unterstützt werden.

Der folgende Arbeitsbereich fordert eine A100-GPU-VM an und startet die QLoRA-Feinabstimmung für eine unterstützte Phi-3-Voreinstellung mithilfe eines öffentlich zugänglichen Datensatzes:

apiVersion: kaito.sh/v1alpha1
kind: Workspace
metadata:
  name: workspace-tuning-phi-3-mini
resource:
  instanceType: Standard_NC24ads_A100_v4
  labelSelector:
    matchLabels:
      apps: phi-3-mini-tuning
tuning:
  preset:
    name: phi-3-mini-4k-instruct
  method: qlora
  input:
    urls:
    - https://huggingface.co/datasets/yahma/alpaca-cleaned/resolve/main/alpaca_data_cleaned.json

KAITO kann modellvoreingestellte Konfiguration und die GPU-Infrastruktur koordinieren, die vom Arbeitsbereich benötigt wird. Speichern Sie für den Produktionseinsatz das Trainingsdataset in einem genehmigten Azure-Speicherkonto, verwenden Sie privaten Zugriff und, sofern unterstützt, Workload Identity, und speichern Sie das resultierende Modell in einer verwalteten Modellregistry oder der Azure Container Registry. Behandeln Sie das Arbeitsbereichmanifest, die Eingabedatensatzversion, die voreingestellte Version, die Adapterkonfiguration und den Ausgabeimagedigest als einen versionsierten Schulungsdatensatz.

Konfigurieren der NCCL-Kommunikation mit AKS CNI

NVIDIA Collective Communications Library (NCCL) verarbeitet kollektive Vorgänge wie all-reduce. Verwenden Sie für einen tragbaren TCP-Basisplan auf AKS CNI die Pod-Netzwerkschnittstelle, normalerweise eth0und beginnen Sie mit NCCL_IB_DISABLE=1. Verwenden Sie auf unterstützten RDMA-fähigen VM-Größenklassen die dokumentierten RDMA-Konfigurationen von NVIDIA und Azure, und validieren Sie diese, bevor Sie NCCL_IB_DISABLE=0 festlegen.

Verwenden Sie diese Basisumgebungsvariablen:

  • NCCL_SOCKET_IFNAME=eth0 wählt die Pod-Netzwerkschnittstelle aus.
  • NCCL_DEBUG=INFO bietet während der Validierung Diagnoseinformationen. Reduzieren Sie die Protokollebene nach der Optimierung.
  • NCCL_IB_DISABLE=1 wählt TCP-Sockets aus, wenn RDMA nicht konfiguriert ist.
  • TORCH_NCCL_ASYNC_ERROR_HANDLING=1 sorgt dafür, dass PyTorch beendet wird, anstatt nach asynchronen Kommunikationsfehlern unbegrenzt hängen zu bleiben.

Wenn die Netzwerkrichtlinie aktiviert ist, lassen Sie alle erforderlichen Rendezvous und NCCL-Datenverkehr zwischen Replikaten zu. NCCL kann dynamische Ports aushandeln, sodass eine schmale Richtlinie für feste Ports dazu führen kann, dass Schulungsaufträge hängen bleiben. Die folgende Richtlinie erlaubt die uneingeschränkte Pod-zu-Pod-Kommunikation nur zwischen Replikaten des PyTorch-Auftrags und ermöglicht die DNS-Auflösung:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-pytorch-nccl
  namespace: ml-training
spec:
  podSelector:
    matchLabels:
      training-job: pytorch-nccl-example
  policyTypes:
  - Ingress
  - Egress
  ingress:
  - from:
    - podSelector:
        matchLabels:
          training-job: pytorch-nccl-example
  egress:
  - to:
    - podSelector:
        matchLabels:
          training-job: pytorch-nccl-example
  - to:
    - namespaceSelector:
        matchLabels:
          kubernetes.io/metadata.name: kube-system
      podSelector:
        matchLabels:
          k8s-app: kube-dns
    ports:
    - protocol: UDP
      port: 53
    - protocol: TCP
      port: 53

Benchmarken Sie NCCL unabhängig vom Modelltraining, bevor Sie hochskalieren. Vergleichen Sie die All-Reduce-Bandbreite, die Latenz, die GPU-Auslastung und den Trainingsdurchsatz bei jeder Anzahl von Workern. Mehr Worker können die Leistung beeinträchtigen, wenn die Kommunikation oder das Laden von Daten zum Engpass wird.

Verwenden Sie Gang-Scheduling und Workload-Warteschlangen

Wichtige Erkenntnis: Lassen Sie verteilte Mitarbeiter als Gruppe zu, und erzwingen Sie Mandanten-GPU-Kontingente, sodass teilweise geplante Aufträge keine GPUs reservieren, während sie auf verbleibende Mitarbeiter warten.

Der Standardmäßige Kubernetes-Scheduler platziert Pods einzeln. Bei einem verteilten Auftrag, der keine Fortschritte machen kann, solange nicht alle Worker laufen, kann eine teilweise Platzierung GPU-Kapazität verschwenden. Kueue stellt die Steuerung und Kontingentverwaltung bereit, bevor Pods mit der Ausführung beginnen. Volcano bietet einen Scheduler und ein auf PodGroups basierendes All-or-Nothing-Planungsmodell.

AKS Automatic stellt den Standardmäßigen Kubernetes-Scheduler und die automatische Knotenbereitstellung als Ausgangspunkt bereit. Übermitteln Sie gewöhnliche Jobs oder vom Operator verwaltete Trainings-Jobs mit präzisen Ressourcenanforderungen und lassen Sie die Plattform geeignete Kapazitäten bereitstellen. Dieses Verhalten garantiert keine atomare Zulassung für jeden Worker. Installieren Sie Kueue oder Volcano, wenn ein Workload Gang-Scheduling, Warteschlangen, Teamkontingente, faire Ressourcenverteilung oder benutzerdefiniertes Präemptionsverhalten erfordert.

Zuweisen eines GPU-Kontingents mit mehreren Mandanten mit Kueue

Installieren Sie eine Kueue-Version, die mit der Kubernetes-Version kompatibel ist, und aktivieren Sie Integrationen für die von Ihnen verwendeten Workloadtypen. Das folgende Manifest erstellt Folgendes:

  • Eine GPU ResourceFlavor, die Knoten mit der Bezeichnung accelerator=nvidia zugeordnet ist.
  • Eine Vier-GPU ClusterQueue für Team A.
  • Eine ZWEI-GPU ClusterQueue für Team B.
  • Ein auf einen Namespace beschränktes LocalQueue für jedes Team.
  • Ein Zwei-Mitarbeiter-Job, den Kueue nur zulässt, wenn seine angeforderten Ressourcen verfügbar sind.
apiVersion: v1
kind: Namespace
metadata:
  name: team-a
---
apiVersion: v1
kind: Namespace
metadata:
  name: team-b
---
apiVersion: kueue.x-k8s.io/v1beta1
kind: ResourceFlavor
metadata:
  name: nvidia-gpu
spec:
  nodeLabels:
    accelerator: nvidia
---
apiVersion: kueue.x-k8s.io/v1beta1
kind: ClusterQueue
metadata:
  name: team-a-gpu
spec:
  namespaceSelector:
    matchLabels:
      kubernetes.io/metadata.name: team-a
  queueingStrategy: BestEffortFIFO
  resourceGroups:
  - flavors:
    - name: nvidia-gpu
      resources:
      - name: cpu
        nominalQuota: "64"
      - name: memory
        nominalQuota: 256Gi
      - name: nvidia.com/gpu
        nominalQuota: "4"
---
apiVersion: kueue.x-k8s.io/v1beta1
kind: ClusterQueue
metadata:
  name: team-b-gpu
spec:
  namespaceSelector:
    matchLabels:
      kubernetes.io/metadata.name: team-b
  queueingStrategy: BestEffortFIFO
  resourceGroups:
  - flavors:
    - name: nvidia-gpu
      resources:
      - name: cpu
        nominalQuota: "32"
      - name: memory
        nominalQuota: 128Gi
      - name: nvidia.com/gpu
        nominalQuota: "2"
---
apiVersion: kueue.x-k8s.io/v1beta1
kind: LocalQueue
metadata:
  name: gpu-queue
  namespace: team-a
spec:
  clusterQueue: team-a-gpu
---
apiVersion: kueue.x-k8s.io/v1beta1
kind: LocalQueue
metadata:
  name: gpu-queue
  namespace: team-b
spec:
  clusterQueue: team-b-gpu
---
apiVersion: batch/v1
kind: Job
metadata:
  name: team-a-two-gpu-training
  namespace: team-a
  labels:
    kueue.x-k8s.io/queue-name: gpu-queue
spec:
  suspend: true
  completions: 2
  parallelism: 2
  backoffLimit: 2
  template:
    metadata:
      labels:
        app: team-a-two-gpu-training
    spec:
      restartPolicy: Never
      tolerations:
      - key: sku
        operator: Equal
        value: gpu
        effect: NoSchedule
      containers:
      - name: worker
        image: nvcr.io/nvidia/cuda:12.4.1-runtime-ubuntu22.04
        command:
        - /bin/bash
        - -c
        - |
          set -e
          nvidia-smi
          echo "Kueue admitted this worker."
          sleep 120
        resources:
          requests:
            cpu: "4"
            memory: 16Gi
            nvidia.com/gpu: 1
          limits:
            cpu: "4"
            memory: 16Gi
            nvidia.com/gpu: 1

Ein ClusterQueue Kontingent ist ein Planungskontingent, kein Azure Abonnementkontingent. Stellen Sie sicher, dass der AKS-Cluster die zugrunde liegende VM-Kapazität bereitstellen kann und dass Azure GPU-Kontingent für die aggregierte zugelassene Workload ausreicht. Verwenden Sie Kohorten und Kueue Fair-Sharing-Funktionen, wenn Teams sich gegenseitig ungenutzte Kontingente ausleihen können.

Verwenden Sie Vulkan als Alternative

Volcano ist eine Alternative, wenn Sie einen dedizierten Batchplaner mit Gang-Scheduling, Warteschlangen und Richtlinien für den Joblebenszyklus benötigen. Installieren Sie ein mit dem Cluster kompatibles Volcano-Release, bevor Sie dessen CRDs installieren. Der folgende Vulkanauftrag erfordert, dass beide GPU-Worker verfügbar sein müssen, bevor der Auftrag ausgeführt wird:

apiVersion: batch.volcano.sh/v1alpha1
kind: Job
metadata:
  name: volcano-gpu-training
  namespace: default
spec:
  minAvailable: 2
  schedulerName: volcano
  policies:
  - event: PodEvicted
    action: RestartJob
  - event: PodFailed
    action: RestartJob
  tasks:
  - replicas: 2
    name: worker
    template:
      metadata:
        labels:
          app: volcano-gpu-training
      spec:
        restartPolicy: OnFailure
        nodeSelector:
          accelerator: nvidia
        tolerations:
        - key: sku
          operator: Equal
          value: gpu
          effect: NoSchedule
        containers:
        - name: worker
          image: nvcr.io/nvidia/cuda:12.4.1-runtime-ubuntu22.04
          command:
          - /bin/bash
          - -c
          - |
            set -e
            nvidia-smi
            echo "All Volcano workers were admitted."
            sleep 120
          resources:
            requests:
              cpu: "4"
              memory: 16Gi
              nvidia.com/gpu: 1
            limits:
              cpu: "4"
              memory: 16Gi
              nvidia.com/gpu: 1

Standardisieren Sie auf ein einziges primäres Warteschlangen- und Gang-Scheduling-System, es sei denn, Sie verfügen über ein getestetes Interoperabilitätsdesign. Mehrere Admission Controller oder Scheduler können es andernfalls erschweren, ausstehendes und Präemptionsverhalten nachzuvollziehen.

Konfigurieren von Schulungs- und Ableitungsprioritäten

Latenzkritische Inferenz erfordert häufig eine höhere Priorität als unterbrechbares Batch-Training. Die folgenden PriorityClass Ressourcen ermöglichen es Inference-Pods, Pods mit niedrigerer Priorität zu verdrängen, und verhindern gleichzeitig, dass Batch-Trainings-Pods andere Workloads verdrängen:

apiVersion: scheduling.k8s.io/v1
kind: PriorityClass
metadata:
  name: training-batch
value: 10000
globalDefault: false
preemptionPolicy: Never
description: Batch training can wait and can be preempted by higher-priority workloads.
---
apiVersion: scheduling.k8s.io/v1
kind: PriorityClass
metadata:
  name: inference-critical
value: 100000
globalDefault: false
preemptionPolicy: PreemptLowerPriority
description: Latency-sensitive production inference can preempt lower-priority workloads.

Weisen Sie die Klasse über spec.priorityClassName in der Podvorlage zu. Die Pod-Priorität in Kubernetes und die Workload-Priorität in Kueue wirken sich auf unterschiedliche Phasen aus: Kueue steuert die Aufnahme in die Warteschlange, während die Kubernetes-Priorität nach der Zulassung die Planung und Verdrängung von Pods beeinflusst. Koordinieren Sie beide Richtlinien, sodass sie dieselbe Geschäftliche Priorität ausdrücken.

Präemption beendet Pods mit niedrigerer Priorität. Trainings-Workloads, die unterbrochen werden können, müssen Checkpoints in persistenten Speicher schreiben, doppelt ausgeführte Arbeit tolerieren und ohne auf knotenlokale Dateien angewiesen zu sein wieder anlaufen können.

Konfigurieren des freigegebenen Speichers und der Ressourcenverwaltung

Wichtige Erkenntnis: Ersetzen Sie die kleine Standardeinstellung /dev/shmder Containerlaufzeit, legen Sie Ressourcenanforderungen aus der beobachteten Auslastung fest, und konfigurieren Sie die Knotenskalierung um vollständige GPU-Workloadanforderungen.

Erhöhen /dev/shm für ML-Datenlader

PyTorch-Datenladeprogramme, TensorFlow-Eingabepipelines, NCCL und Python Multiprocessing können POSIX shared memory verwenden. Die Standardgröße des Containers für /dev/shm ist für das Training mit mehreren Prozessen häufig zu klein, was zu Abstürzen von Worker-Prozessen, Busfehlern oder einem scheinbaren Stillstand des Trainings führen kann.

Binden Sie ein arbeitsspeichergestütztes emptyDir unter /dev/shm ein und legen Sie ein explizites sizeLimit fest:

apiVersion: batch/v1
kind: Job
metadata:
  name: shared-memory-training
spec:
  backoffLimit: 2
  template:
    metadata:
      labels:
        app: shared-memory-training
    spec:
      restartPolicy: Never
      nodeSelector:
        accelerator: nvidia
      tolerations:
      - key: sku
        operator: Equal
        value: gpu
        effect: NoSchedule
      containers:
      - name: trainer
        image: pytorch/pytorch:2.4.1-cuda12.4-cudnn9-runtime
        command:
        - /bin/bash
        - -c
        - |
          set -e
          df -h /dev/shm
          python -c '
          import multiprocessing as mp
          import torch

          def worker(index):
              value = torch.ones(1024, 1024)
              print(f"worker={index}, sum={value.sum().item()}")

          if __name__ == "__main__":
              processes = [mp.Process(target=worker, args=(i,)) for i in range(4)]
              for process in processes:
                  process.start()
              for process in processes:
                  process.join()
                  if process.exitcode != 0:
                      raise SystemExit(process.exitcode)
          '
        volumeMounts:
        - name: shared-memory
          mountPath: /dev/shm
        resources:
          requests:
            cpu: "8"
            memory: 16Gi
            nvidia.com/gpu: 1
          limits:
            cpu: "8"
            memory: 16Gi
            nvidia.com/gpu: 1
      volumes:
      - name: shared-memory
        emptyDir:
          medium: Memory
          sizeLimit: 8Gi

Die speichergestützte emptyDir Nutzung zählt zur Speicherauslastung des Pods. Schließen Sie die maximale erwartete Nutzung des gemeinsam genutzten Speichers beim Festlegen des Grenzwerts für den Containerspeicher ein. Wenn /dev/shm über das effektive Speicherbudget hinaus anwächst, kann der Pod wegen Überschreitung seines Speicherlimits entfernt oder beendet werden.

Größe der CPU und des Arbeitsspeichers aus beobachteter Auslastung

Beginnen Sie mit einem Benchmark basierend auf repräsentativem Modell, Batchgröße, Sequenzlänge, Datenladeprogramm-Parallelität, Augmentation und Prüfpunktverhalten. Verwenden Sie dann Überwachungsdaten, um Folgendes anzupassen:

  • Legen Sie CPU-Anforderungen hoch genug fest, um gpu-Eingabepipelines bereitgestellt zu halten. Beständige GPU-Leerlaufperioden können auf CPU- oder Speicherhunger hinweisen.
  • Legen Sie Speicheranforderungen in der Nähe der stabilen Arbeitssatznutzung sowie einen Sicherheitsrand fest. Berücksichtigen Sie /dev/shm, das Verhalten des Seiten-Caches, den Overhead des Framework-Allocators und Spitzen bei der Prüfpunktserialisierung.
  • Legen Sie Speichergrenzwerte oberhalb des beobachteten Höchstwerts fest. Untersuchen Sie OOMKilled Ereignisse, anstatt immer mehr Grenzwerte zu erhöhen, ohne die Ursache zu finden.
  • Prüfen Sie die CPU-Drosselung, bevor Sie restriktive CPU-Limits verwenden. CPU-Grenzwerte, die zu niedrig sind, können die GPU-Auslastung auch dann verringern, wenn die durchschnittliche CPU-Auslastung akzeptabel erscheint.
  • Legen Sie Anforderungen und Grenzwerte für vorhersehbare, hochwertige Schulungsaufträge fest, wenn garantierte Servicequalität wichtiger ist als Knotenverpackung.
  • Profilieren Sie jede signifikante Änderung der Modellgröße, Genauigkeit, Batchgröße, Arbeitsanzahl und Datenpipeline.

Verwenden Sie Perzentile über vollständige Trainingsläufe anstelle eines kurzfristigen Durchschnitts. Start-, Validierungs-, Prüfpunkt- und Daten-Shuffle-Phasen weisen häufig unterschiedliche Ressourcenspitzen auf.

Konfigurieren der automatischen Clusterkalierung für einen GPU-Knotenpool

Aktivieren Sie für AKS Standard die Cluster-Autoskaler im GPU-Benutzerknotenpool, und definieren Sie mindeste und maximale Kapazität:

RESOURCE_GROUP=myResourceGroup
AKS_CLUSTER=myAKSCluster
GPU_NODE_POOL=gpunp

az aks nodepool update \
  --resource-group "$RESOURCE_GROUP" \
  --cluster-name "$AKS_CLUSTER" \
  --name "$GPU_NODE_POOL" \
  --enable-cluster-autoscaler \
  --min-count 0 \
  --max-count 8

Sie können unterstützte Profileinstellungen des Cluster-Autoscalers auf Clusterebene anpassen. Testen Sie Profiländerungen sowohl mit Trainings- als auch mit Serving-Workloads:

az aks update \
  --resource-group "$RESOURCE_GROUP" \
  --name "$AKS_CLUSTER" \
  --cluster-autoscaler-profile \
    scan-interval=20s \
    scale-down-unneeded-time=10m \
    scale-down-delay-after-add=15m \
    max-graceful-termination-sec=120

Ein ausstehender GPU-Pod löst die Skalierung nur aus, wenn die vollständigen Planungsanforderungen einer Knotenpoolvorlage entsprechen. Überprüfen Sie die GPU-Ressourcenanforderung, VM-Kapazität, Bezeichnungen, Taints, Tolerationen, Affinität, Topologieeinschränkungen, persistente Volumetopologie, maximale Knotenanzahl und Azure Kontingent, wenn die Skalierung nicht auftritt.

Scale-to-Zero eignet sich für unterbrechbare Trainingspools, führt jedoch zu zusätzlicher Latenz bei der VM-Bereitstellung, beim Abrufen von Images, beim Einbinden von Datensätzen und bei der Modellinitialisierung. Behalten Sie einen Mindestwert ungleich null bei, wenn sich die Startlatenz wesentlich auf die Serviceziele auswirkt. Pod-Disruption-Budgets, nicht evakuierbare Pods, lokaler Speicher und lange Kulanzfristen für die Beendigung können das Herunterskalieren verzögern.

AKS Automatic verwaltet die Bereitstellung und Skalierung von Knoten als Teil der Dienstbasispläne. Konzentrieren Sie sich auf genaue Podanforderungen und unterstützte Einschränkungen, anstatt eine manuelle GPU-Knotenpool-Autoskalierung zu konfigurieren.

Speichern von Datasets und Prüfpunkten

Kernaussage: Wählen Sie den Speicher anhand von Zugriffsmuster, Durchsatz, gemeinsamer Nutzung und Wiederherstellungsanforderungen aus, und speichern Sie Checkpoints dauerhaft außerhalb des Knotens, damit das Training nach Verdrängung oder Präemption wieder aufgenommen werden kann.

Halten Sie große Datensätze, Modellartefakte und Checkpoints aus Container-Images heraus. Wählen Sie eine Speicherschnittstelle basierend auf der Workload aus:

  • Verwenden Sie Azure Blob Storage für große Objektdatensets, Modellartefakte und Hochkapazitätsdatenrepositorys.
  • Verwenden Sie Azure Files, wenn mehrere Arbeitsknoten ein gemeinsam genutztes POSIX-Dateisystem erfordern.
  • Verwenden Sie Azure-Datenträger für leistungsstarke Lese-/Schreib-Checkpoint- oder Cacheworkloads mit einem einzelnen Knoten, bei denen ReadWriteOnce ausreicht.
  • Verwenden Sie node-local ephemeral Speicher nur für rekonstruierbare Caches und temporäre Dateien.

Zugreifen auf große Datasets mit dem Azure Blob-CSI-Treiber

Aktivieren Sie den Azure Blob CSI-Treiber auf AKS Standard, wenn er noch nicht aktiviert ist:

RESOURCE_GROUP=myResourceGroup
AKS_CLUSTER=myAKSCluster

az aks update \
  --resource-group "$RESOURCE_GROUP" \
  --name "$AKS_CLUSTER" \
  --enable-blob-driver

Das folgende Beispiel erstellt dynamisch einen Premium-Azure Blob-Container, der über NFS verfügbar gemacht wird, und stellt ihn in einem Schulungs pod bereit. Verwenden Sie für ein vorhandenes gesteuertes Dataset ein statisch definiertes Volume oder eine BlobFuse-Konfiguration mit den entsprechenden Identitäts- und privaten Netzwerksteuerelementen, anstatt einen leeren dynamischen Container zu erstellen:

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: ml-azureblob-nfs
provisioner: blob.csi.azure.com
parameters:
  protocol: nfs
  skuName: Premium_LRS
reclaimPolicy: Delete
allowVolumeExpansion: true
volumeBindingMode: Immediate
mountOptions:
- -o attr_timeout=120
- -o entry_timeout=120
- -o negative_timeout=120
- -o actimeo=120
- -o noresvport
- -o nconnect=4
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: blob-training-dataset
  namespace: default
spec:
  accessModes:
  - ReadWriteMany
  storageClassName: ml-azureblob-nfs
  resources:
    requests:
      storage: 1Ti
---
apiVersion: batch/v1
kind: Job
metadata:
  name: inspect-blob-dataset
  namespace: default
spec:
  backoffLimit: 2
  template:
    metadata:
      labels:
        app: inspect-blob-dataset
    spec:
      restartPolicy: Never
      containers:
      - name: dataset-reader
        image: ubuntu:24.04
        command:
        - /bin/bash
        - -c
        - |
          set -e
          echo "Mounted dataset path:"
          df -h /mnt/dataset
          find /mnt/dataset -maxdepth 2 -type f | head -100
        volumeMounts:
        - name: dataset
          mountPath: /mnt/dataset
      volumes:
      - name: dataset
        persistentVolumeClaim:
          claimName: blob-training-dataset

Vergleichen Sie die Protokoll- und Bereitstellungsoptionen mit repräsentativen Shardgrößen und Zugriffsmustern. Viele Mitarbeiter, die viele kleine Dateien lesen, können unterschiedliche Ergebnisse von sequenziellem Streaming großer Shards erzeugen. Erwägen Sie, Datensätze in Shards geeigneter Größe vorzuverarbeiten und knotenlokalen Cache-Speicher zu verwenden, wenn bei wiederholten Epochen andernfalls dieselben Objekte erneut heruntergeladen würden.

Teilen von Schulungsdaten mit Azure Files

Azure Files unterstützt ReadWriteMany, sodass verteilte Worker auf verschiedenen Knoten dieselbe Freigabe einbinden können. Premium-Azure Files ist geeignet, wenn die Workload vorhersehbare Dateisystemleistung erfordert und die ausgewählte Region die erforderliche Redundanzoption unterstützt.

Das folgende Manifest erstellt eine Premium-Azure-Files-Freigabe und bindet sie in zwei parallele Worker-Pods ein:

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: ml-azurefile-premium
provisioner: file.csi.azure.com
parameters:
  skuName: Premium_LRS
reclaimPolicy: Delete
allowVolumeExpansion: true
volumeBindingMode: Immediate
mountOptions:
- dir_mode=0770
- file_mode=0660
- uid=1000
- gid=1000
- mfsymlinks
- cache=strict
- actimeo=30
- nosharesock
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: shared-training-data
  namespace: default
spec:
  accessModes:
  - ReadWriteMany
  storageClassName: ml-azurefile-premium
  resources:
    requests:
      storage: 100Gi
---
apiVersion: batch/v1
kind: Job
metadata:
  name: shared-data-workers
  namespace: default
spec:
  completions: 2
  parallelism: 2
  completionMode: Indexed
  backoffLimitPerIndex: 2
  template:
    metadata:
      labels:
        app: shared-data-workers
    spec:
      restartPolicy: Never
      securityContext:
        runAsUser: 1000
        runAsGroup: 1000
        fsGroup: 1000
      containers:
      - name: worker
        image: ubuntu:24.04
        command:
        - /bin/bash
        - -c
        - |
          set -e
          WORKER_INDEX="${JOB_COMPLETION_INDEX:-0}"
          echo "worker=${WORKER_INDEX}" \
            > "/mnt/shared/worker-${WORKER_INDEX}.txt"
          ls -la /mnt/shared
        volumeMounts:
        - name: shared-data
          mountPath: /mnt/shared
      volumes:
      - name: shared-data
        persistentVolumeClaim:
          claimName: shared-training-data

Vermeiden Sie, dass jeder Mitarbeiter wiederholt ein Verzeichnis aufzählt, das Millionen von Dateien enthält. Verwenden Sie ein Manifest oder eine deterministische Shard-Zuweisung, damit jeder Worker weiß, welche Objekte er lesen soll.

Prüfpunkt für beständigen Speicher

Ein Prüfpunkt sollte genügend Zustand enthalten, um ordnungsgemäß fortzusetzen, z. B. Modellgewichtungen, Optimierzustand, Zeitplanzustand, Skalierungszustand, Epoche oder Schrittzahl, Zufallszahlengeneratorstatus und Datenladeposition, wenn unterstützt. Schreiben Sie Prüfpunkte regelmäßig und vor der ordnungsgemäßen Beendigung.

Der folgende Job schreibt atomare PyTorch-Checkpoints in Azure Files, stellt den neuesten Checkpoint nach einem Containerneustart oder einer Pod-Ersetzung wieder her und behandelt SIGTERM so, dass Präemption jüngste Fortschritte bewahren kann:

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: ml-checkpoints-azurefile
provisioner: file.csi.azure.com
parameters:
  skuName: Premium_LRS
reclaimPolicy: Retain
allowVolumeExpansion: true
volumeBindingMode: Immediate
mountOptions:
- dir_mode=0770
- file_mode=0660
- uid=1000
- gid=1000
- mfsymlinks
- cache=strict
- actimeo=30
- nosharesock
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: model-checkpoints
  namespace: default
spec:
  accessModes:
  - ReadWriteMany
  storageClassName: ml-checkpoints-azurefile
  resources:
    requests:
      storage: 100Gi
---
apiVersion: batch/v1
kind: Job
metadata:
  name: checkpointed-pytorch-training
  namespace: default
spec:
  backoffLimit: 6
  template:
    metadata:
      labels:
        app: checkpointed-pytorch-training
    spec:
      restartPolicy: OnFailure
      terminationGracePeriodSeconds: 120
      securityContext:
        runAsUser: 1000
        runAsGroup: 1000
        fsGroup: 1000
      containers:
      - name: trainer
        image: pytorch/pytorch:2.4.1-cuda12.4-cudnn9-runtime
        command:
        - python
        - -c
        - |
          import os
          import signal
          import sys
          import time
          import torch

          checkpoint_path = "/checkpoints/latest.pt"
          temporary_path = "/checkpoints/latest.pt.tmp"
          state = {"epoch": 0, "value": torch.tensor([0.0])}

          if os.path.exists(checkpoint_path):
              state = torch.load(checkpoint_path, map_location="cpu")
              print(f"Restored epoch {state['epoch']}", flush=True)

          def save_checkpoint():
              torch.save(state, temporary_path)
              os.replace(temporary_path, checkpoint_path)
              print(f"Saved epoch {state['epoch']}", flush=True)

          def terminate(signum, frame):
              print(f"Received signal {signum}", flush=True)
              save_checkpoint()
              sys.exit(143)

          signal.signal(signal.SIGTERM, terminate)
          signal.signal(signal.SIGINT, terminate)

          for epoch in range(state["epoch"] + 1, 21):
              state["epoch"] = epoch
              state["value"] += 1
              time.sleep(10)
              if epoch % 2 == 0:
                  save_checkpoint()

          save_checkpoint()
          print("Training completed.", flush=True)
        volumeMounts:
        - name: checkpoints
          mountPath: /checkpoints
        resources:
          requests:
            cpu: "1"
            memory: 2Gi
          limits:
            cpu: "1"
            memory: 2Gi
      volumes:
      - name: checkpoints
        persistentVolumeClaim:
          claimName: model-checkpoints

Verwenden Sie eine Retain Rückforderungsrichtlinie oder ein unabhängig verwaltetes Speicherkonto für Produktionsprüfpunkte, sodass das Löschen eines PVC den einzigen Wiederherstellungspunkt nicht unbeabsichtigt löscht. Für verteiltes datenparalleles Training sollten normalerweise Rang 0 den globalen Checkpoint schreiben, oder Sie verwenden ein vom Framework unterstütztes Sharded-Checkpoint-Format. Verhindern, dass mehrere Rangfolgen gleichzeitig dieselbe Datei schreiben.

Testen Sie die Wiederherstellung, indem Sie während eines Testlaufs außerhalb der Produktion absichtlich einen Worker-Pod löschen. Stellen Sie sicher, dass der Controller die Arbeitsauslastung neu erstellt, der neue Pod das gleiche persistente Volume einhält, und die Schulung wird aus dem erwarteten Schritt fortgesetzt.

Überwachen der GPU-Auslastung und des Schulungsdurchsatzes

Wichtige Erkenntnis: Überwachen Sie GPU-Compute, GPU-Speicher, Datenpipelinedurchsatz, Schrittdauer und Prüfpunktaktivität zusammen, sodass Sie die Berechnungssättigung von CPU, Netzwerk oder Speicherengpässen unterscheiden können.

Aktivieren Sie Azure Monitor verwalteten Dienst für Prometheus und Containereinblicke, um Kubernetes-Status, Containerprotokolle, Knotenintegrität und Prometheus-Metriken zu korrelieren. Verwenden Sie bewährte Methoden für die Überwachung von AKS, wenn Sie Alarmierung, Datenaufbewahrung und operative Dashboards entwerfen.

NVIDIA-GPU-Metriken erfassen

NVIDIA Data Center GPU Manager (DCGM) Exporter macht Prometheus-Metriken für NVIDIA GPUs verfügbar. Wenn der NVIDIA GPU-Operator bereits DCGM-Exporter bereitstellt, stellen Sie keinen doppelten Exporter bereit. Konfigurieren Sie Azure Monitor verwalteten Prometheus, um den vorhandenen Exporter zu verschrotten.

Das folgende PodMonitor verwendet die von Azure Monitor verwaltete Prometheus-CRD und zielt auf DCGM-Exporter-Pods im gpu-operator-Namespace ab. Passen Sie die Bezeichnungsauswahl an die Bezeichnungen an, die von Ihrem installierten Exporter verwendet werden:

apiVersion: azmonitoring.coreos.com/v1
kind: PodMonitor
metadata:
  name: nvidia-dcgm-exporter
  namespace: gpu-operator
spec:
  selector:
    matchLabels:
      app: nvidia-dcgm-exporter
  podMetricsEndpoints:
  - port: metrics
    interval: 30s
    scrapeTimeout: 10s

Überprüfen Sie den Portnamen des Exporter-Dienstes oder Pods, bevor Sie den Monitor anwenden. Zu den allgemeinen DCGM-Metriken gehören:

Metric Purpose
DCGM_FI_DEV_GPU_UTIL Prozentsatz der Zeit, in der die GPU aktiv ist
DCGM_FI_DEV_FB_USED Verwendeter Framepufferspeicher
DCGM_FI_DEV_FB_FREE Freier Framepufferspeicher
DCGM_FI_DEV_MEM_COPY_UTIL Auslastung der Speicherkopier-Engine
DCGM_FI_DEV_POWER_USAGE GPU-Stromverbrauch
DCGM_FI_DEV_GPU_TEMP GPU-Temperatur
DCGM_FI_DEV_XID_ERRORS NVIDIA XID-Fehlerereignisse

Die Metrikverfügbarkeit hängt von der GPU, dem Treiber, dcGM und den Exporteurversionen ab.

Exportieren von Trainingsdurchsatzmetriken

Statten Sie die Trainingsanwendung mit Metriken auf Workload-Ebene aus. Zu den nützlichen Metriken gehören:

  • ml_training_samples_total für kumulativ verarbeitete Proben oder Token.
  • ml_training_steps_total für abgeschlossene Optimizer-Schritte.
  • ml_training_step_duration_seconds für schrittlatenz.
  • ml_training_checkpoint_duration_seconds für prüfpunktlatenz.
  • ml_training_data_wait_seconds für die Wartezeit auf Eingabedaten.
  • Modellspezifische Verlustwerte, Lernrate, Gradientennorm und Validierungsmetriken.

Das folgende ausführbare Beispiel stellt synthetische Trainingsmetriken am Port 8000 bereit. Ersetzen Sie die Simulation durch Metriken, die von der realen Trainingsschleife ausgegeben werden:

apiVersion: v1
kind: Namespace
metadata:
  name: ml-observability
---
apiVersion: v1
kind: ConfigMap
metadata:
  name: training-metrics-server
  namespace: ml-observability
data:
  server.py: |
    import http.server
    import threading
    import time

    state = {
        "samples": 0,
        "steps": 0,
        "last_step_duration": 0.0,
    }

    def train():
        while True:
            started = time.time()
            time.sleep(1)
            state["samples"] += 256
            state["steps"] += 1
            state["last_step_duration"] = time.time() - started

    class MetricsHandler(http.server.BaseHTTPRequestHandler):
        def do_GET(self):
            if self.path != "/metrics":
                self.send_response(404)
                self.end_headers()
                return

            body = (
                "# HELP ml_training_samples_total Samples processed.\n"
                "# TYPE ml_training_samples_total counter\n"
                f"ml_training_samples_total{{job_name=\"metrics-demo\"}} "
                f"{state['samples']}\n"
                "# HELP ml_training_steps_total Training steps completed.\n"
                "# TYPE ml_training_steps_total counter\n"
                f"ml_training_steps_total{{job_name=\"metrics-demo\"}} "
                f"{state['steps']}\n"
                "# HELP ml_training_step_duration_seconds "
                "Duration of the most recent training step.\n"
                "# TYPE ml_training_step_duration_seconds gauge\n"
                f"ml_training_step_duration_seconds"
                f"{{job_name=\"metrics-demo\"}} "
                f"{state['last_step_duration']}\n"
            ).encode("utf-8")

            self.send_response(200)
            self.send_header("Content-Type", "text/plain; version=0.0.4")
            self.send_header("Content-Length", str(len(body)))
            self.end_headers()
            self.wfile.write(body)

        def log_message(self, format, *args):
            return

    threading.Thread(target=train, daemon=True).start()
    http.server.ThreadingHTTPServer(("0.0.0.0", 8000), MetricsHandler).serve_forever()
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: training-metrics-demo
  namespace: ml-observability
spec:
  replicas: 1
  selector:
    matchLabels:
      app: training-metrics-demo
  template:
    metadata:
      labels:
        app: training-metrics-demo
    spec:
      containers:
      - name: metrics
        image: python:3.12-slim
        command:
        - python
        - /app/server.py
        ports:
        - name: metrics
          containerPort: 8000
        readinessProbe:
          httpGet:
            path: /metrics
            port: metrics
          initialDelaySeconds: 2
          periodSeconds: 10
        resources:
          requests:
            cpu: 50m
            memory: 64Mi
          limits:
            cpu: 200m
            memory: 128Mi
        volumeMounts:
        - name: application
          mountPath: /app
          readOnly: true
      volumes:
      - name: application
        configMap:
          name: training-metrics-server
---
apiVersion: azmonitoring.coreos.com/v1
kind: PodMonitor
metadata:
  name: training-metrics-demo
  namespace: ml-observability
spec:
  selector:
    matchLabels:
      app: training-metrics-demo
  podMetricsEndpoints:
  - port: metrics
    path: /metrics
    interval: 30s
    scrapeTimeout: 10s

Fügen Sie für einen echten verteilten Auftrag stabile Bezeichnungen wie Modellname, Modellversion, Auftragsname, Ausführungs-ID, Replikatrolle und Team ein. Verwenden Sie keine unbegrenzten Labels wie Beispiel-IDs, Anfrage-IDs oder Pfade zu Rohdatensätzen, da Labels mit hoher Kardinalität die Überwachungskosten und die Abfragelatenz erhöhen.

Erstellen von GPU- und Durchsatzdashboards

Verwenden Sie PromQL-Abfragen wie die folgenden als Ausgangspunkte, und überprüfen Sie die Metrik-Labels anhand Ihres Exporters:

avg by (namespace, pod, gpu) (
  avg_over_time(DCGM_FI_DEV_GPU_UTIL[5m])
)

Die folgende Abfrage berechnet die Auslastung des Framebuffer-Speichers als Prozentsatz:

100 *
DCGM_FI_DEV_FB_USED
/
(DCGM_FI_DEV_FB_USED + DCGM_FI_DEV_FB_FREE)

Die folgende Abfrage berechnet Schulungsbeispiele pro Sekunde:

sum by (job_name) (
  rate(ml_training_samples_total[5m])
)

Die folgende Abfrage zeigt die durchschnittliche Schrittdauer an:

avg by (job_name) (
  ml_training_step_duration_seconds
)

Interpretieren Sie diese Signale zusammen:

  • Niedrige GPU-Auslastung und hohe Datenwartezeit geben in der Regel Speicher, Netzwerk, Vorverarbeitung oder CPU-Hunger an.
  • Hohe GPU-Auslastung mit erwartetem Durchsatz gibt an, dass der Accelerator effektiv verwendet wird.
  • Eine hohe GPU-Speicherauslastung und wiederholte Out-of-Memory-Fehler deuten darauf hin, dass Batchgröße, Sequenzlänge, Aktivierungsspeicher, Optimiererzustand oder Fragmentierung angepasst werden müssen.
  • Ein sinkender Durchsatz bei stabiler GPU-Auslastung kann auf längere Sequenzen, Kommunikationsaufwand, thermisches Verhalten oder eine Änderung der Modellberechnung hindeuten.
  • Eine niedrige GPU-Auslastung bei zugewiesenen GPUs weist auf ungenutzte Kapazität hin, die möglicherweise verringert, anders in die Warteschlange eingeordnet oder mit MIG partitioniert werden kann.
  • Lange Prüfpunktdauer kann die Vorbehebung teuer machen und die Menge der wiederholten Arbeit nach der Wiederherstellung erhöhen.

Erstellen Sie Warnungen für dauerhafte GPU-Unterverwendung, GPU-Speicher in der Nähe von Kapazität, XID-Fehler, verzögerten Schulungsdurchsatz, fehlgeschlagene Mitarbeiter, ausstehende arbeitsgangplanmäßige Workloads und Prüfpunkte, die nicht innerhalb des erwarteten Wiederherstellungspunktziels abgeschlossen wurden.

Häufig gestellte Fragen (FAQ)

Was ist der Unterschied zwischen AKS Automatic und AKS Standard for MLOps?

AKS Automatic stellt vorkonfigurierte Standardwerte für Knotenpools, Netzwerke, Sicherheit und Upgrades bereit, sodass MLOps-Teams sich auf Workloaddefinitionen und Governance konzentrieren können. AKS Standard erfordert eine manuelle Konfiguration dieser Plattformkomponenten, bietet jedoch eine tiefere Kontrolle über benutzerdefinierte Architekturen und Skalierungsanforderungen.

Wie kann ich ML-Modelle in AKS versionieren?

Versionieren Sie Ihre Modelle, indem Sie Modellgewichte, Metadaten und Konfigurationen in Container-Images mit semantischen Versions-Tags verpacken. Speichern Sie diese Images in Azure Container Registry, und verweisen Sie auf bestimmte Versionen in Ihren Kubernetes-Bereitstellungsmanifesten, um die Konsistenz in allen Umgebungen sicherzustellen.

Wie kann ich GPU-Workloads auf AKS planen?

Stellen Sie sicher, dass das NVIDIA-Device-Plugin verfügbar ist, damit GPU-Knoten nvidia.com/gpu ankündigen. Stellen Sie in AKS Standard einen dedizierten GPU-Benutzerknotenpool mit einer unterstützten VM-Größe bereit, z. B. eine SKU der Standard_NC-Serie, und konfigurieren Sie Knotenbeschriftungen, einen NoSchedule Taint- und Cluster-Autoscaler-Grenzwert. Fordern Sie in jedem Training-Pod nvidia.com/gpu an, wählen Sie einen dafür geeigneten GPU-Knoten aus und fügen Sie die passende Toleration hinzu. AKS Automatic kann anhand von Pod-Anforderungen geeignete GPU-Kapazität bereitstellen, abhängig von unterstützten SKUs, der regionalen Verfügbarkeit, Schedulingeinschränkungen und dem Kontingent des Azure-Abonnements.

Wie kann ich verteilte Schulungsaufträge auf AKS ausführen?

Installieren Sie den Kubeflow Training Operator und übermitteln Sie ein PyTorchJob oder TFJob, um verteilte Replikate, die Rendezvous-Konfiguration, das Neustartverhalten und den Jobstatus zu verwalten. KAITO bietet eine auf AKS ausgerichtete Arbeitsbereichsabstraktion für unterstützte Workflows zur Modell-Feinabstimmung und kann die GPU-Infrastruktur mit Modellvorgaben koordinieren. Konfigurieren Sie für das PyTorch-Training mit mehreren Knoten NCCL über das AKS CNI-Podnetzwerk und testen Sie dessen Leistung, erlauben Sie den Datenverkehr zwischen den Workern durch entsprechende Netzwerkrichtlinien, und verwenden Sie eine unterstützte RDMA-Konfiguration, wenn die ausgewählte VM-SKU und die Workload dies erfordern.

Wie verwalte ich das GPU-Kontingent für mehrere Teams?

Installieren Sie Kueue und definieren Sie ClusterQueue Ressourcen mit CPU-, Arbeitsspeicher- und nvidia.com/gpu Quoten. Weisen Sie dann den Namespace jedes Teams über ein LocalQueue der jeweils zugewiesenen Quote zu. Verwenden Sie Kueue-Kohorten oder faire Ressourcenteilung, wenn Teams ungenutzte Kapazitäten nutzen können. Vulkan ist eine Alternative, wenn Sie einen Batchplaner mit all-oder-nichts-Arbeiter-Zulassung benötigen. Koordinieren Sie die Priorität der Warteschlange mit Kubernetes-PriorityClassRessourcen, sodass latenzempfindliche Inferenz unterbrechbares Training verdrängen kann, und stellen Sie sicher, dass verdrängbare Trainingsjobs anhand von Checkpoints im persistenten Speicher wiederhergestellt werden.

Was sind die bewährten Methoden zum Ausführen lang laufender Batchaufträge auf AKS?

Verwenden Sie ein Kubernetes Job für endliche Arbeit und eine CronJob für geplante Arbeit. Speichern Sie Prüfpunkte und bestätigte Ausgaben außerhalb des Pods, gestalten Sie die Verarbeitung idempotent, behandeln Sie SIGTERM, und legen Sie explizite Ressourcenanforderungen, Wiederholungsgrenzen, Fristen und Bereinigungsrichtlinien fest. Gehen Sie davon aus, dass Knotenwartung oder -ausfall zum Ersetzen des Pods führen können, und testen Sie, ob der Ersatz-Pod seinen Checkpoint ordnungsgemäß wiederherstellt. Überwachen Sie den Auftragsstatus, Pod-Ereignisse, Protokolle, das Alter des Prüfpunkts, den Durchsatz und das Wiederholungsverhalten. Normale neustartbare Jobs erfordern weder Gang-Scheduling noch ein PDB. Verwenden Sie diese Steuerelemente nur, wenn parallel laufende Worker gleichzeitig starten oder eine getestete Mindestparallelität aufrechterhalten müssen.

Erfahren Sie mehr über Best Practices in anderen Bereichen Ihrer Anwendungsbereitstellung und -vorgänge in AKS: