Dynamic Resource Allocation, kurz DRA, macht das Kubernetes-Projekt HAMi zur gemeinsamen Nutzung von Grafikprozessoren nicht überflüssig, verändert jedoch die Aufgabenverteilung zwischen beiden. Nachdem DRA in Kubernetes v1.34 allgemein verfügbar wurde und seit v1.35 standardmäßig aktiviert ist, kann Kubernetes Teilanforderungen an Geräteressourcen nativ verstehen und planen. Die Durchsetzung dieser Anteile innerhalb des Containers bei CUDA-Aufrufen gehört jedoch nicht zu den Aufgaben von DRA; hier bleibt HAMi-core von zentraler Bedeutung.
Ein von Mesut Oezdil verfasster CNCF-Artikel analysiert den Unterschied zwischen den beiden Ebenen und erklärt, wie HAMi einen Teil seiner Architektur auf DRA neu aufbaut, anstatt das Projekt aufzugeben. Der Autor weist darauf hin, dass die Frage nicht lautet, ob eines der beiden Projekte das andere ersetzen wird, sondern welche Funktionen von HAMi inzwischen durch native Kubernetes-Funktionen abgedeckt werden.
Warum benötigte die GPU-Aufteilung spezielle Lösungen?
Die Device-Plugin-Schnittstelle von Kubernetes konnte ursprünglich im Wesentlichen Geräte zählen. Eine herkömmliche Anforderung wie nvidia.com/gpu: 1 bedeutete, eine vollständige Karte zu reservieren, ohne eine native Sprache für die Anforderung von 8.000 Megabyte Kartenspeicher oder 10 % ihrer Rechenleistung.
Um damit umzugehen, verwendet HAMi erweiterte Ressourcen wie nvidia.com/gpumem und nvidia.com/gpucores. Der standardmäßige Kubernetes-Scheduler behandelt diese Werte jedoch als undurchsichtige Zahlen. Er weiß nicht, dass Speicher und Rechenleistung von derselben physischen Karte stammen müssen, und kann allein nicht bestimmen, ob mehrere Anteile die Kapazität einer bestimmten Karte überschreiten.
Daher setzt der herkömmliche Ansatz auf einen Webhook zur Änderung der Anforderung, eine Scheduler-Erweiterung, die Knoten filtert und eine Geräte-ID auswählt, sowie die Speicherung der Entscheidung in einer Annotation. Das Device Plugin liest diese Entscheidung später aus, um Grenzen wie CUDA_DEVICE_MEMORY_LIMIT_0=8000m und CUDA_DEVICE_SM_LIMIT=10 einzufügen und zusätzlich die Bibliothek libvgpu.so vorab zu laden, um die Grenzen durchzusetzen.
Laut dem Artikel betrieb DaoCloud mit diesem Ansatz mehr als 10.000 GPUs über mehr als 10 Rechenzentren hinweg. Seine Architektur bleibt jedoch an ein spezielles Annotation-Format und an von HAMi verstandene Komponenten gebunden. Diese Lücke soll DRA schließen.
Was fügt DRA hinzu?
DRA ersetzt das Modell der Gerätezählung durch ein Anspruchsmodell mit vier zentralen Objekten in der Schnittstelle resource.k8s.io/v1:
- ResourceSlice: Wird vom Gerätetreiber veröffentlicht und beschreibt die tatsächliche Hardware jedes Knotens, einschließlich Modell, Speicher und Architektur.
- DeviceClass: Definiert Geräteklassen und ihre Filter mithilfe von CEL-Ausdrücken.
- ResourceClaim: Wird vom Besitzer der Workload erstellt, um ein Gerät anhand der Klasse, Selektoren und Einschränkungen anzufordern.
- ResourceClaimTemplate: Erstellt für jede Replik der Workload einen unabhängigen Anspruch.
Der Scheduler weist dem Anspruch ein bestimmtes Gerät zu, bevor der Container gebunden wird. Das Ergebnis erscheint im Status des ResourceClaim als strukturiertes API-Objekt. Dadurch erhält die Entscheidung einen nativen Speicherort, den kubectl lesen kann, der durch RBAC geschützt werden kann und auf dem andere Controller aufbauen können, anstatt sie als Zeichenfolge in einer Annotation zu speichern.
Das grundlegende DRA allein reicht jedoch nicht aus, um Speicher im HAMi-Stil gemeinsam zu nutzen. Die wichtige Erweiterung ist hier Consumable Capacity, die in v1.34 hinter dem Feature-Gate DRAConsumableCapacity experimentell eingeführt wurde und seit v1.36 experimentell sowie standardmäßig aktiviert ist. Diese Erweiterung ermöglicht es dem Treiber, anzugeben, dass ein Gerät mehrere Zuweisungen akzeptiert. Außerdem kann ein Anspruch eine bestimmte Menge einer benannten Ressource auf dem Gerät anfordern, etwa Speicher oder Rechenleistung.
Damit wird die Zuordnung der HAMi-Ressourcen nahezu direkt: gpumem wird zu einer Kapazitätsanforderung für Speicher, gpucores zu einer Kapazitätsanforderung für Rechenleistung, während die Prüfung, ob die Karte noch über die erforderliche Kapazität verfügt, von der HAMi-Scheduler-Erweiterung auf den Kubernetes-Scheduler selbst übergeht.
Planung bedeutet nicht Durchsetzung der Grenzen
Die Analyse betont, dass DRA die Zusagen verfolgt, die der Scheduler macht, den Container aber während der Laufzeit nicht daran hindert, diese zu überschreiten. CUDA-Aufrufe kennen den Inhalt des ResourceClaim nicht, und eine speicherhungrige Workload könnte versuchen, zusätzlichen Speicher auf Kosten eines anderen Containers zu verbrauchen.
HAMi-core übernimmt diese Aufgabe über die C-Bibliothek libvgpu.so, die CUDA- und NVIDIA-Management-Library-Aufrufe abfängt und die Grenzen aus dem Userspace heraus anwendet. Laut dem im Artikel enthaltenen Beispiel erhalten zwei Container jeweils 8.000 Megabyte. Überschreitet ein Container seinen Anteil, erhält er an seiner Grenze einen CUDA-Fehler wegen Speichermangels, während der andere Container weiterläuft.
Dieser Softwareschutz ist in feindlichen Multi-Tenant-Umgebungen kein Ersatz für eine hardwarebasierte Aufteilung. Eine Workload, die das Preloading der Bibliothek umgeht, eine statische Bindung an den CUDA-Treiber verwendet oder Einstellungen wie CUDA_DISABLE_CONTROL ausnutzt, kann der Abfanglogik entgehen. Der Autor erklärt, dass NVIDIA Multi-Instance GPU, kurz MIG, besser für eine hardwarebasierte Isolation geeignet ist, während die Software-Abfanglogik im Vergleich zu festen MIG-Profilen eine Granularität von bis zu 1 MB beim Speicher und 1 % bei der Rechenleistung bietet.
Wie baut HAMi seine Architektur auf DRA neu auf?
Die neue Architektur verteilt sich auf drei Repositories mit unterschiedlichen Aufgaben:
- k8s-dra-driver: Veröffentlicht den Speicher und die Rechenleistung jeder GPU als verbrauchbare Kapazität in ResourceSlices, betreibt das kubelet-Plugin, verbindet Container über CDI und fügt die HAMi-core-Durchsetzung hinzu.
- HAMi-DRA: Ein Admission-Mutating-Webhook, der die herkömmlichen erweiterten Ressourcen aus den Anforderungen entfernt und entsprechende ResourceClaims erstellt, wobei Annotationen für die UUID- und Gerätetypauswahl erhalten bleiben. Laut dem Artikel war HAMi-DRA v0.2.0 zusammen mit HAMi v2.9 produktionsbereit; anschließend wechselte die Versionsreihe zu v0.2.1.
- HAMi: Dokumentiert den DRA-Modus seit Version v2.8 als Installationsoption, aktiviert außerdem standardmäßig die Überwachungskomponente und stellt über Prometheus auf Port 31995 Gerätemetriken pro Container bereit.
Ein Vorteil von HAMi-DRA besteht darin, dass die Planung dem Scheduler überlassen wird, der den Cluster verwaltet. Dadurch können Volcano, KAI Scheduler oder jeder andere Scheduler verwendet werden, der DRA versteht, ohne eine spezielle HAMi-Integration hinzufügen zu müssen. Diese Entscheidung hat jedoch einen Preis: HAMi-DRA besitzt keinen eigenen Scheduler und garantiert daher keine topologiebewussten Entscheidungen, etwa die Auswahl eines über NVLink verbundenen GPU-Paars.
Anforderungen und praktische Auswahl
Der DRA-Modus erfordert Kubernetes v1.34 oder neuer mit aktiviertem DRAConsumableCapacity. In den Versionen v1.34 und v1.35 ist das Feature-Gate experimentell und standardmäßig deaktiviert, was seine Nutzung bei Managed Services verhindern kann, die keine Änderung der API-Server-Einstellungen erlauben. In v1.36 wurde es experimentell und standardmäßig aktiviert. Außerdem ist eine Container-Runtime mit CDI-Unterstützung erforderlich, etwa containerd oder CRI-O mit aktivierter CDI, ebenso ein NVIDIA-Treiber ab Version 440 sowie ein geeigneter DRA-Treiber für den jeweiligen Beschleunigertyp.
Der Artikel beschreibt den NVIDIA-Pfad als den ausgereiftesten. Unterstützung für Ascend und Enflame ist verfügbar, und Hygon DCU wird über k8s-dcu-dra-driver dokumentiert. Der herkömmliche HAMi-Modus deckt dagegen seit v2.9 mehr als 12 Gerätefamilien ab, darunter Cambricon MLUs, Iluvatar, MetaX, Moore Threads, Kunlunxin, AWS Neuron und Vastai. Daher dürften Cluster mit mehreren Anbietertypen weiterhin den herkömmlichen Pfad verwenden, bis die Abdeckung durch DRA-Treiber erweitert wird.
Der DRA-Modus und der herkömmliche Device-Plugin-Modus sollten nicht im selben Cluster betrieben werden, da die beiden Scheduler den Eindruck erwecken würden, dieselbe Kapazität zu verwalten, ohne die Zusagen des jeweils anderen zu sehen. Nach Einschätzung des Autors eignet sich der herkömmliche Modus für verwaltete Cluster, die keine Feature-Gates bereitstellen, für ältere Versionen und für Flotten mit mehreren Anbietern. NVIDIA-Cluster, die die Kubernetes-Steuerebene kontrollieren, insbesondere unter v1.36, können den DRA-Modus in einer Testumgebung erproben, beginnend mit HAMi-DRA, um Änderungen an den bestehenden Bereitstellungsdateien zu vermeiden.
Consumable Capacity ist in Kubernetes weiterhin insgesamt instabil, und das Helm-Chart für k8s-dra-driver ist nach wie vor als in Arbeit gekennzeichnet. Außerdem ist die Unterstützung durch Anbieter im DRA-Bereich weiterhin geringer als im herkömmlichen Modus. Die Quintessenz des Artikels lautet, dass DRA die Sprache für Anforderungen und die Planung übernimmt, während HAMi-core die Ausführungsfähigkeit innerhalb des Containers beibehält; das Verhältnis zwischen beiden entwickelt sich also in Richtung Integration und nicht Ablösung.