Calcolo e scalabilità automatica - Amazon EKS

Le traduzioni sono generate tramite traduzione automatica. In caso di conflitto tra il contenuto di una traduzione e la versione originale in Inglese, quest'ultima prevarrà.

Calcolo e scalabilità automatica

Ottimizzazione delle risorse GPU e gestione dei costi

Pianifica i carichi di lavoro con i requisiti della GPU utilizzando etichette Known

Per i AI/ML carichi di lavoro sensibili alle diverse caratteristiche della GPU (ad esempio GPU, memoria GPU), consigliamo di specificare i requisiti della GPU utilizzando etichette di pianificazione note supportate dai tipi di nodi utilizzati con Karpenter e i gruppi di nodi gestiti. La mancata definizione di questi parametri può comportare la pianificazione dei pod su istanze con risorse GPU inadeguate, con conseguenti guasti o un peggioramento delle prestazioni. Ti consigliamo di utilizzare NodeSelector o Node affinity per specificare su quale nodo deve essere eseguito un pod e di impostare le risorse di calcolo (CPU, memoria, ecc.) nella sezione delle risorse del pod. GPUs

Esempio

Ad esempio, utilizzando il selettore di nodi di nomi GPU quando si utilizza Karpenter:

apiVersion: v1 kind: Pod metadata: name: gpu-pod-example spec: containers: - name: ml-workload image: <image> resources: limits: nvidia.com/gpu: 1 # Request one NVIDIA GPU nodeSelector: karpenter.k8s.aws/instance-gpu-name: "l40s" # Run on nodes with NVIDIA L40S GPUs

Usa Kubernetes Device Plugin per esporre GPUs

Per eseguire l'esposizione GPUs sui nodi, il driver GPU NVIDIA deve essere installato sul sistema operativo del nodo e il runtime del contenitore deve essere configurato per consentire allo scheduler Kubernetes di assegnare i pod ai nodi se disponibili. GPUs Il processo di configurazione per il plugin per dispositivi NVIDIA Kubernetes dipende dall'AMI EKS Accelerated che stai utilizzando:

Per verificare che i NVIDIA Device Plugin siano attivi e GPUs siano esposti correttamente, esegui:

kubectl describe node | grep nvidia.com/gpu

Questo comando verifica se la nvidia.com/gpu risorsa rientra nella capacità del nodo e nelle risorse allocabili. Ad esempio, dovrebbe essere visualizzato un nodo con una GPU. nvidia.com/gpu: 1 Per ulteriori informazioni, consulta la Kubernetes GPU Scheduling Guide.

Usa ML Capacity Blocks per garantire la capacità delle istanze P e Trainium

I Capacity Blocks for ML ti consentono di prenotare istanze GPU molto richieste, in particolare istanze P (ad esempio, p6-b200, p5, p5e, p5en, p4d, p4de) e istanze Trainium (ad es. trn1, trn2), da avviare quasi immediatamente o in date future per supportare i carichi di lavoro di machine learning (ML) di breve durata. Queste prenotazioni sono ideali per garantire la capacità necessaria per attività ad alta intensità di calcolo come la formazione dei modelli e la messa a punto. EC2 I prezzi di Capacity Blocks sono costituiti da una commissione di prenotazione e da una commissione per il sistema operativo. Per ulteriori informazioni sui prezzi, consulta i prezzi EC2 di Capacity Blocks for ML.

GPUs Per riservare i AI/ML carichi di lavoro su Amazon EKS per una garanzia di capacità prevedibile, consigliamo di utilizzare i blocchi di capacità ML per le prenotazioni di capacità a breve termine o le prenotazioni di capacità su richiesta (ODCRs) per la garanzia della capacità generica.

  • ODCRs consentono di riservare la capacità delle EC2 istanze (ad esempio, istanze GPU come g5 o p5) in una zona di disponibilità specifica per una determinata durata, garantendo la disponibilità anche in caso di forte domanda. ODCRs non avete alcun impegno a lungo termine, ma pagate la tariffa On-Demand per la capacità riservata, utilizzata o inattiva. In EKS, ODCRs sono supportati da tipi di nodi come Karpenter e gruppi di nodi gestiti. Per stabilire le priorità ODCRs in Karpenter, configura l'utilizzo del campo. NodeClass capacityReservationSelectorTerms Vedi la documentazione di Karpenter. NodePools

  • I Capacity Blocks sono un meccanismo di prenotazione specializzato per istanze GPU (ad esempio p5, p4d) o Trainium (trn1, trn2), progettato per carichi di lavoro ML a breve termine come la formazione di modelli, la messa a punto o la sperimentazione. Riservi la capacità per un periodo definito (in genere da 24 ore a 182 giorni) a partire da una data futura, pagando solo per il tempo prenotato. Sono prepagate, richiedono una pianificazione preliminare in base alle esigenze di capacità e non supportano la scalabilità automatica, ma sono collocate in modo isolato per reti a bassa latenza. EC2 UltraClusters Fanno pagare solo per il periodo prenotato. Per saperne di più, consulta Trova e acquista blocchi di capacità oppure inizia a configurare gruppi di nodi gestiti con Capacity Blocks seguendo le istruzioni contenute in Creare un gruppo di nodi gestiti con blocchi di capacità per ML.

Riserva la capacità tramite la Console di gestione AWS e configura i nodi per utilizzare blocchi di capacità ML. Pianifica le prenotazioni in base alla pianificazione dei carichi di lavoro ed esegui i test in un cluster di staging. Per ulteriori informazioni, consulta la documentazione relativa ai blocchi di capacità.

Prendi in considerazione On-Demand, Amazon EC2 Spot o On-Demand Capacity Reservations (ODCRs) per le istanze G Amazon EC2

Per EC2 le istanze Amazon G, considera le diverse opzioni di acquisto tra On-Demand, Amazon EC2 Spot Instances e On-Demand Capacity Reservations. ODCRsconsentono di riservare la capacità delle EC2 istanze in una zona di disponibilità specifica per un determinato periodo, garantendo la disponibilità anche in caso di forte domanda. A differenza delle istanze ML Capacity, disponibili solo per le istanze P e Trainium, ODCRs possono essere utilizzati per una gamma più ampia di tipi di istanze, comprese le istanze G, il che li rende adatti a carichi di lavoro che richiedono funzionalità GPU diverse, come l'inferenza o la grafica. Quando si utilizzano le istanze Amazon EC2 Spot, la possibilità di diversificare tra diversi tipi, dimensioni e zone di disponibilità è fondamentale per poter rimanere su Spot più a lungo.

ODCRs non avete alcun impegno a lungo termine, ma pagate la tariffa On-Demand per la capacità riservata, utilizzata o inattiva. ODCRs possono essere creati per un uso immediato o programmati per date future, garantendo flessibilità nella pianificazione della capacità. In Amazon EKS, ODCRs sono supportati da tipi di nodi come Karpenter e gruppi di nodi gestiti. Per stabilire le priorità ODCRs in Karpenter, configura l'utilizzo del campo. NodeClass capacityReservationSelectorTerms Vedi la documentazione di Karpenter. NodePools Per ulteriori informazioni sulla creazione ODCRs, inclusi i comandi CLI, consulta la Guida introduttiva alla prenotazione della capacità su richiesta.

Prendi in considerazione altri tipi e dimensioni di istanze accelerate

La selezione dell'istanza accelerata e della dimensione appropriate è essenziale per ottimizzare prestazioni e costi nei carichi di lavoro ML su Amazon EKS. Ad esempio, diverse famiglie di istanze GPU hanno prestazioni e funzionalità diverse, come la memoria GPU. Per aiutarti a scegliere l'opzione con il miglior rapporto prezzo/prestazioni, consulta le istanze GPU disponibili nella pagina Tipi di istanze in Accelerated Computing. EC2 Valuta diversi tipi e dimensioni di istanze per trovare la soluzione migliore per i tuoi requisiti di carico di lavoro specifici. Prendi in considerazione fattori come il numero GPUs, la memoria e le prestazioni della rete. Selezionando attentamente il tipo e la dimensione dell'istanza GPU corretti, è possibile ottenere un migliore utilizzo delle risorse e un'efficienza dei costi nei cluster EKS.

Se si utilizza un'istanza GPU in un nodo EKS, per impostazione predefinita il nvidia-device-plugin-daemonset pod verrà inserito nel kube-system namespace. Per capire rapidamente se state utilizzando appieno le GPU della vostra istanza, potete usare nvidia-smi come mostrato di seguito:

kubectl exec nvidia-device-plugin-daemonset-xxxxx \ -n kube-system -- nvidia-smi \ --query-gpu=index,power.draw,power.limit,temperature.gpu,utilization.gpu,utilization.memory,memory.free,memory.used \ --format=csv -l 5
  • Se utilization.memory è vicino al 100%, è probabile che i tuoi codici siano vincolati alla memoria. Ciò significa che la GPU (memoria) è completamente utilizzata, ma potrebbe suggerire che sia necessario studiare un'ulteriore ottimizzazione delle prestazioni.

  • Se utilization.gpu è vicino al 100%, ciò non significa necessariamente che la GPU sia completamente utilizzata. Una metrica migliore da considerare è il rapporto tra a. power.draw power.limit Se questo rapporto è pari o superiore al 100%, i tuoi codici stanno utilizzando appieno la capacità di calcolo della GPU.

  • Il -l 5 flag indica di emettere le metriche ogni 5 secondi. Nel caso di un singolo tipo di istanza GPU, il flag di interrogazione dell'indice non è necessario.

Per ulteriori informazioni, consulta le istanze GPU nella documentazione di AWS.

Ottimizza l'allocazione delle risorse GPU con Time-Slicing, MIG e l'allocazione frazionata della GPU

I limiti statici delle risorse in Kubernetes (ad esempio, CPU, memoria, numero di GPU) possono portare a un approvvigionamento eccessivo o al sottoutilizzo, in particolare per carichi di lavoro dinamici come l'inferenza. AI/ML La scelta della GPU giusta è importante. Per carichi di lavoro a basso volume o con picchi di traffico, il time-slicing consente a più carichi di lavoro di condividere una singola GPU condividendone le risorse di elaborazione, migliorando potenzialmente l'efficienza e riducendo gli sprechi. La condivisione della GPU può essere ottenuta tramite diverse opzioni:

  • Sfrutta i selettori dei nodi e l'affinità dei nodi per influenzare la pianificazione: assicurati che i nodi forniti e i pod siano pianificati in modo appropriato GPUs per il carico di lavoro (ad esempio,) karpenter.k8s.aws/instance-gpu-name: "a100"

  • Time-Slicing: pianifica i carichi di lavoro per condividere le risorse di calcolo di una GPU nel tempo, permettendo l'esecuzione simultanea senza partizionamento fisico. È ideale per carichi di lavoro con esigenze di elaborazione variabili, ma può mancare l'isolamento della memoria.

  • GPU multiistanza (MIG): MIG consente di partizionare una singola GPU NVIDIA in più istanze isolate ed è supportata con NVIDIA Ampere (ad esempio, GPU A100), NVIDIA Hopper (ad esempio, GPU H100) e NVIDIA Blackwell (ad esempio Blackwell). GPUs GPUs Ogni istanza MIG riceve risorse di calcolo e memoria dedicate, che consentono la condivisione delle risorse in ambienti multi-tenant o carichi di lavoro che richiedono garanzie di risorse, il che consente di ottimizzare l'utilizzo delle risorse GPU, inclusi scenari come la gestione di più modelli con batch di diverse dimensioni tramite time-slicing.

  • Allocazione frazionata della GPU: utilizza la pianificazione basata su software per allocare parti del calcolo o della memoria di una GPU ai carichi di lavoro, offrendo flessibilità per carichi di lavoro dinamici. NVIDIA KAI Scheduler, parte della piattaforma Run:AI, consente tutto ciò consentendo ai pod di richiedere risorse GPU frazionarie.

Per abilitare queste funzionalità in EKS, puoi implementare il NVIDIA Device Plugin, che utilizza come risorse programmabili e supporta time-slicing e MIG. GPUs Per saperne di più, consulta Time-Slicing GPUs in Kubernetes e condivisione della GPU su Amazon EKS con istanze time-slicing e accelerate NVIDIA. EC2

Esempio

Ad esempio, per abilitare il time-slicing con il plugin per dispositivi NVIDIA:

apiVersion: v1 kind: ConfigMap metadata: name: nvidia-device-plugin-config namespace: kube-system data: config.yaml: | version: v1 sharing: timeSlicing: resources: - name: nvidia.com/gpu replicas: 4 # Allow 4 pods to share each GPU

Esempio

Ad esempio, per utilizzare KAI Scheduler per l'allocazione frazionata della GPU, implementalo insieme a NVIDIA GPU Operator e specifica le risorse GPU frazionarie nelle specifiche del pod:

apiVersion: v1 kind: Pod metadata: name: fractional-gpu-pod-example annotations: gpu-fraction: "0.5" # Annotation for 50% GPU labels: runai/queue: "default" # Required queue assignment spec: containers: - name: ml-workload image: nvcr.io/nvidia/pytorch:25.04-py3 resources: limits: nvidia.com/gpu: 1 nodeSelector: nvidia.com/gpu: "true" schedulerName: kai-scheduler

Resilienza dei nodi e gestione dei lavori di formazione

Implementa Node Health Checks con il ripristino automatico

Per i lavori di formazione distribuiti su Amazon EKS che richiedono comunicazioni frequenti tra nodi, come l'addestramento su modelli multi-GPU su più nodi, problemi hardware come guasti della GPU o dell'EFA possono causare interruzioni dei lavori di formazione. Queste interruzioni possono portare alla perdita di progressi nella formazione e all'aumento dei costi, in particolare per carichi di lavoro di lunga durata che si basano su hardware stabile. AI/ML

Per aumentare la resilienza contro i guasti hardware, come i guasti della GPU nei cluster EKS che eseguono carichi di lavoro GPU, consigliamo di utilizzare EKS Node Monitoring Agent con Auto Repair o Amazon. SageMaker HyperPod Mentre EKS Node Monitoring Agent with Auto Repair offre funzionalità come il monitoraggio dello stato dei nodi e la riparazione automatica utilizzando meccanismi Kubernetes standard, SageMaker HyperPod offre resilienza mirata e funzionalità aggiuntive progettate specificamente per la formazione ML su larga scala, come controlli approfonditi dello stato e ripresa automatica dei lavori.

  • L'EKS Node Monitoring Agent con Node Auto Repair monitora continuamente lo stato dei nodi leggendo i log Ready e applicando NodeConditions, incluse condizioni standard come quelle specifiche dell'hardware accelerato per identificare problemi come guasti della GPU o della rete. Quando un nodo è considerato non integro, Node Auto Repair lo isola e lo sostituisce con un nuovo nodo. La riprogrammazione dei pod e il riavvio dei lavori si basano sui meccanismi standard di Kubernetes e sulla politica di riavvio del processo.

  • L'agente per i controlli SageMaker HyperPodapprofonditi e il monitoraggio dello stato di salute monitora continuamente lo stato di salute delle istanze basate su GPU e Trainium. È personalizzato per i AI/ML carichi di lavoro e utilizza etichette (ad esempio) per gestire lo stato dei nodi. node-health-status Quando un nodo è considerato non integro, HyperPod attiva la sostituzione automatica dell'hardware difettoso, ad esempio. GPUs Per impostazione predefinita, rileva i guasti relativi alla rete per l'EFA attraverso i suoi controlli sanitari di base e supporta la ripresa automatica dei lavori di formazione interrotti, consentendo di continuare i lavori dall'ultimo checkpoint e riducendo al minimo le interruzioni per le attività di machine learning su larga scala.

Sia per EKS Node Monitoring Agent con Auto Repair che per SageMaker HyperPod i cluster che utilizzano EFA, per monitorare metriche specifiche di EFA come errori di Remote Direct Memory Access (RDMA) e cadute di pacchetti, assicurati che il driver AWS EFA sia installato. Inoltre, consigliamo di implementare l'CloudWatch Observability Add-on o di utilizzare strumenti come DCGM Exporter con Prometheus e Grafana per monitorare EFA, GPU e, per esempio, metriche specifiche relative alle sue funzionalità. SageMaker HyperPod

Disabilita Karpenter Consolidation per carichi di lavoro sensibili alle interruzioni

Per i carichi di lavoro sensibili alle interruzioni, come l'elaborazione, le attività di AI/ML previsione su larga scala o la formazione, consigliamo di ottimizzare le politiche di consolidamento di Karpenter per evitare interruzioni durante l'esecuzione del lavoro. La funzionalità di consolidamento di Karpenter ottimizza automaticamente i costi del cluster chiudendo i nodi sottoutilizzati o sostituendoli con alternative più economiche. Tuttavia, anche quando un carico di lavoro utilizza completamente una GPU, Karpenter può consolidare i nodi se identifica un tipo di istanza di dimensioni adeguate a un prezzo inferiore che soddisfi i requisiti del pod, con conseguenti interruzioni del lavoro.

La politica di WhenEmptyOrUnderutilized consolidamento può chiudere i nodi prematuramente, con conseguenti tempi di esecuzione più lunghi. Ad esempio, le interruzioni possono ritardare la ripresa dei job a causa della riprogrammazione dei pod e del ricaricamento dei dati, il che potrebbe essere costoso per i lavori di inferenza in batch di lunga durata. Per ovviare a questo problema, puoi impostare WhenEmpty e configurare una consolidateAfter durata, ad esempio 1 ora, consolidationPolicy per conservare i nodi durante i picchi di carico di lavoro. Per esempio:

disruption: consolidationPolicy: WhenEmpty consolidateAfter: 60m

Questo approccio migliora la latenza di avvio dei pod per carichi di lavoro di inferenza in batch complessi e altri lavori sensibili alle interruzioni, come l'elaborazione di dati di inferenza online in tempo reale o l'addestramento dei modelli, in cui il costo dell'interruzione supera i risparmi sui costi di elaborazione. NodePool Karpenter Disruption Budgets è un'altra funzionalità per la gestione delle interruzioni di Karpenter. Con i budget, puoi assicurarti che non più di un certo numero di nodi vengano interrotti nei nodi prescelti in un determinato momento. NodePool Puoi anche utilizzare i budget per le interruzioni per evitare che tutti i nodi vengano interrotti in un determinato momento (ad esempio nelle ore di punta). Per ulteriori informazioni, consulta la documentazione di Karpenter Consolidation.

Usa ttlSecondsAfter Finished per la pulizia automatica dei lavori Kubernetes

Consigliamo di impostare ttlSecondsAfterFinished i job Kubernetes in Amazon EKS per eliminare automaticamente gli oggetti di lavoro completati. Gli oggetti di lavoro persistenti consumano risorse del cluster, come la memoria del server API, e complicano il monitoraggio con dashboard ingombranti (ad esempio Grafana, Amazon). CloudWatch Ad esempio, l'impostazione di un TTL di 1 ora garantisce che i lavori vengano rimossi subito dopo il completamento, mantenendo il cluster in ordine. Per ulteriori dettagli, consulta Pulizia automatica per lavori finiti.

Configurazione della priorità dei lavori a bassa priorità per lavori/carichi di lavoro con priorità più alta

Per i carichi di AI/ML lavoro a priorità mista su Amazon EKS, puoi configurare la priorità dei lavori a bassa priorità per garantire che le attività con priorità più alta (ad esempio, inferenza in tempo reale) ricevano risorse tempestivamente. Senza prelazione, i carichi di lavoro a bassa priorità come i processi batch (ad es. inferenza in batch, elaborazione dei dati), i servizi non batch (ad esempio, attività in background, cron job) o i lavori che richiedono un uso intensivo di CPU/memoria (ad esempio i servizi Web) possono ritardare i pod critici occupando i nodi. La priorità consente a Kubernetes di eliminare i pod a bassa priorità quando i pod ad alta priorità richiedono risorse, garantendo un'allocazione efficiente delle risorse sui nodi con GPUs o memoria. CPUs Ti consigliamo di utilizzare Kubernetes per assegnare priorità e controllare il comportamento di sfratto. PriorityClass PodDisruptionBudget

apiVersion: scheduling.k8s.io/v1 kind: PriorityClass metadata: name: low-priority value: 100 --- spec: priorityClassName: low-priority

Per ulteriori informazioni, consulta la documentazione sulla priorità e la priorità di Kubernetes.

Scalabilità e prestazioni delle applicazioni

Personalizza la capacità di calcolo per i carichi di lavoro ML con Karpenter o Static Nodes

Per garantire una capacità di calcolo efficiente in termini di costi e reattiva per i flussi di lavoro di machine learning (ML) su Amazon EKS, consigliamo di personalizzare la strategia di provisioning dei nodi in base alle caratteristiche del carico di lavoro e agli impegni di costo. Di seguito sono riportati due approcci da considerare: just-in-time scalabilità con Karpenter e gruppi di nodi statici per la capacità riservata.

  • Just-in-time scaler sul piano di dati come Karpenter: per flussi di lavoro ML dinamici con richieste di calcolo variabili (ad esempio, inferenza basata su GPU seguita da grafici basati su CPU), consigliamo di utilizzare scalatori sul piano dati come Karpenter. just-in-time

  • Utilizza gruppi di nodi statici per carichi di lavoro prevedibili: per carichi di lavoro ML prevedibili e stazionari o quando si utilizzano istanze riservate, i gruppi di nodi gestiti da EKS possono contribuire a garantire che la capacità riservata sia completamente fornita e utilizzata, massimizzando i risparmi. Questo approccio è ideale per tipi di istanze specifici eseguiti tramite o. RIs ODCRs

Esempio

Questo è un esempio di Karpenter diversificato NodePoolche consente il lancio di istanze g Amazon in cui la generazione di EC2 istanze è superiore a tre.

apiVersion: karpenter.sh/v1 kind: NodePool metadata: name: gpu-inference spec: template: spec: nodeClassRef: group: karpenter.k8s.aws kind: EC2NodeClass name: default requirements: - key: karpenter.sh/capacity-type operator: In values: ["on-demand"] - key: karpenter.k8s.aws/instance-category operator: In values: ["g"] - key: karpenter.k8s.aws/instance-generation operator: Gt values: ["3"] - key: kubernetes.io/arch operator: In values: ["amd64"] taints: - key: nvidia.com/gpu effect: NoSchedule limits: cpu: "1000" memory: "4000Gi" nvidia.com/gpu: "10" *# Limit the total number of GPUs to 10 for the NodePool* disruption: consolidationPolicy: WhenEmpty consolidateAfter: 60m expireAfter: 720h

Esempio

Esempio di utilizzo di gruppi di nodi statici per un carico di lavoro di formazione:

apiVersion: eksctl.io/v1alpha5 kind: ClusterConfig metadata: name: ml-cluster region: us-west-2 managedNodeGroups: - name: gpu-node-group instanceType: p4d.24xlarge minSize: 2 maxSize: 2 desiredCapacity: 2 taints: - key: nvidia.com/gpu effect: NoSchedule

Utilizza fattori e tolleranze per impedire che carichi di lavoro non accelerati vengano pianificati su istanze accelerate

La pianificazione di carichi di lavoro non accelerati su risorse GPU non è efficiente dal punto di vista del calcolo, pertanto consigliamo di utilizzare taints and toleration per garantire che i pod di carichi di lavoro non accelerati non siano pianificati su nodi inappropriati. Per ulteriori informazioni, consulta la documentazione di Kubernetes.

Scalabilità basata sulle prestazioni del modello

Per i carichi di lavoro di inferenza, consigliamo di utilizzare Kubernetes Event-Driven Autoscaling (KEDA) per scalare in base a metriche prestazionali del modello come le richieste di inferenza o il throughput dei token, con periodi di cooldown appropriati. Le politiche di scalabilità statica possono sovrafornire o sottodimensionare le risorse, con un impatto sui costi e sulla latenza. Scopri di più nella documentazione KEDA.

Allocazione dinamica delle risorse per la gestione avanzata delle GPU

L'allocazione dinamica delle risorse (DRA) rappresenta un progresso fondamentale nella gestione delle risorse GPU Kubernetes. DRA supera le tradizionali limitazioni dei plug-in dei dispositivi per consentire la condivisione sofisticata delle GPU, il riconoscimento della topologia e il coordinamento delle risorse tra i nodi. Disponibile nella versione 1.33 di Amazon EKS, DRA affronta le sfide critiche dei AI/ML carichi di lavoro fornendo quanto segue:

  • Allocazione di GPU a grana fine

  • Meccanismi di condivisione avanzati, come Multi-Process Service (MPS) e Multi-Instance GPU (MIG)

  • Support per architetture hardware di nuova generazione, tra cui NVIDIA 00 GB2 UltraClusters

L'allocazione tradizionale delle GPU viene considerata GPUs come risorse intere opache, con conseguente sottoutilizzo significativo (spesso del 30-40% nei cluster di produzione). Ciò si verifica perché i carichi di lavoro ricevono l'accesso esclusivo all'intero anche quando richiedono solo risorse frazionarie. GPUs DRA trasforma questo modello introducendo un'allocazione strutturata e dichiarativa che fornisce allo scheduler Kubernetes una visibilità completa sulle caratteristiche hardware e sui requisiti del carico di lavoro. Ciò consente decisioni di posizionamento intelligenti e una condivisione efficiente delle risorse.

Vantaggi dell'utilizzo di DRA anziché del plug-in per dispositivi NVIDIA

Il plug-in per dispositivi NVIDIA (a partire dalla versione0.12.0) supporta meccanismi di condivisione della GPU tra cui time-slicing, MPS e MIG. Tuttavia, esistono limitazioni architettoniche che DRA affronta.

Limitazioni dei plug-in per dispositivi NVIDIA

  • Configurazione statica: le configurazioni di condivisione della GPU (repliche time-slicing e impostazioni MPS) richiedono la preconfigurazione a livello di cluster. ConfigMaps Ciò rende difficile fornire strategie di condivisione diverse per carichi di lavoro diversi.

  • Selezione granulare limitata: sebbene il plug-in del dispositivo esponga le caratteristiche della GPU tramite le etichette dei nodi, i carichi di lavoro non possono richiedere dinamicamente configurazioni GPU specifiche (dimensioni della memoria e capacità di calcolo) come parte della decisione di pianificazione.

  • Nessun coordinamento delle risorse tra nodi: non è possibile gestire risorse GPU distribuite su più nodi o esprimere requisiti topologici complessi come i domini per sistemi come NVIDIA 00. NVLink GB2

  • Vincoli dello scheduler: lo scheduler Kubernetes tratta le risorse GPU come numeri interi opachi, limitando la sua capacità di prendere decisioni basate sulla topologia o di gestire dipendenze complesse dalle risorse.

  • Complessità di configurazione: l'impostazione di diverse strategie di condivisione richiede un'etichettatura multipla e attenta dei nodi, creando complessità operativa. ConfigMaps

Soluzioni con DRA

  • Selezione dinamica delle risorse: DRA consente ai carichi di lavoro di specificare requisiti dettagliati (memoria GPU, versioni dei driver e attributi specifici) al momento della richiesta. resourceclaims Ciò consente una corrispondenza più flessibile delle risorse.

  • Conoscenza della topologia: tramite parametri strutturati e selettori di dispositivi, DRA gestisce requisiti complessi come la comunicazione GPU tra nodi e le interconnessioni coerenti con la memoria.

  • Gestione delle risorse tra nodi: computeDomains abilita il coordinamento delle risorse GPU distribuite su più nodi, fondamentale per sistemi come 00 con canali IMEX. GB2

  • Configurazione specifica per il carico di lavoro: ciascuna ResourceClaim specifica strategie e configurazioni di condivisione diverse, consentendo un controllo granulare per carico di lavoro anziché impostazioni a livello di cluster.

  • Integrazione migliorata dello scheduler: DRA fornisce allo scheduler informazioni dettagliate sul dispositivo e consente decisioni di posizionamento più intelligenti in base alla topologia hardware e alle caratteristiche delle risorse.

Importante: DRA non sostituisce completamente il plug-in del dispositivo NVIDIA. Il driver NVIDIA DRA funziona insieme al plug-in del dispositivo per fornire funzionalità avanzate. Il plug-in del dispositivo continua a gestire l'individuazione e la gestione di base della GPU, mentre DRA aggiunge funzionalità avanzate di allocazione e pianificazione.

Istanze supportate da DRA e relative funzionalità

Il supporto DRA varia in base alla famiglia di EC2 istanze Amazon e all'architettura GPU, come illustrato nella tabella seguente.

Famiglia di istanze Tipo di GPU Separazione temporale Supporto MIG Supporto MPS Supporto IMEX Casi d'uso

G5

NVIDIA A10G

No

No

Carichi di lavoro di inferenza e grafica

G6

NVIDIA L4

No

No

Inferenza AI ed elaborazione video

G6e

NVIDIA L40S

No

No

Formazione, inferenza e grafica

P4D/P4DE

NVIDIA A100

No

Formazione su larga scala e HPC

P5

NVIDIA H100

No

Formazione sul modello Foundation

P6

NVIDIA B200

No

Modelli con miliardi o trilioni di parametri, addestramento distribuito e inferenza

P6e

NVIDIA 00 GB2

Modelli con miliardi o trilioni di parametri, addestramento distribuito e inferenza

Di seguito sono riportate le descrizioni di ciascuna funzionalità della tabella:

  • Time-slicing: consente a più carichi di lavoro di condividere le risorse di calcolo della GPU nel tempo.

  • GPU multiistanza (MIG): partizionamento a livello hardware che crea istanze GPU isolate.

  • Servizio multiprocesso (MPS): consente l'esecuzione simultanea di più processi CUDA su una singola GPU.

  • Internode Memory Exchange (IMEX): comunicazione coerente in termini di memoria tra i nodi per 00. GB2 UltraClusters

Risorse aggiuntive

Per ulteriori informazioni sui driver Kubernetes DRA e NVIDIA DRA, consulta le seguenti risorse su: GitHub

Configura l'allocazione dinamica delle risorse per la gestione avanzata delle GPU

L'argomento seguente mostra come configurare l'allocazione dinamica delle risorse (DRA) per la gestione avanzata delle GPU.

Prerequisiti

Prima di implementare DRA su Amazon EKS, assicurati che il tuo ambiente soddisfi i seguenti requisiti.

Configurazione del cluster
Componenti richiesti
  • Versione del plug-in per dispositivi NVIDIA 0.17.1 o successiva

  • Versione del driver NVIDIA DRA o successiva 25.3.0

Fase 1: Creare un cluster con un gruppo di nodi abilitato al DRA utilizzando eksctl

  1. Crea un file di configurazione del cluster denominato: dra-eks-cluster.yaml

    --- apiVersion: eksctl.io/v1alpha5 kind: ClusterConfig metadata: name: dra-eks-cluster region: us-west-2 version: '1.33' managedNodeGroups: - name: gpu-dra-nodes amiFamily: AmazonLinux2023 instanceType: g6.12xlarge desiredCapacity: 2 minSize: 1 maxSize: 3 labels: node-type: "gpu-dra" nvidia.com/gpu.present: "true" taints: - key: nvidia.com/gpu value: "true" effect: NoSchedule
  2. Crea il cluster:

    eksctl create cluster -f dra-eks-cluster.yaml

Fase 2: Implementazione del plug-in per dispositivi NVIDIA

Implementa il plug-in per dispositivi NVIDIA per abilitare l'individuazione di base della GPU:

  1. Aggiungi il repository Helm del plug-in per dispositivi NVIDIA:

    helm repo add nvidia https://nvidia.github.io/k8s-device-plugin helm repo update
  2. Crea valori personalizzati per il plug-in del dispositivo:

    cat <<EOF > nvidia-device-plugin-values.yaml gfd: enabled: true nfd: enabled: true tolerations: - key: [nvidia.com/gpu](http://nvidia.com/gpu) operator: Exists effect: NoSchedule EOF
  3. Installa il plug-in del dispositivo NVIDIA:

    helm install nvidia-device-plugin nvidia/nvidia-device-plugin \ --namespace nvidia-device-plugin \ --create-namespace \ --version v0.17.1 \ --values nvidia-device-plugin-values.yaml

Fase 3: Implementazione del driver NVIDIA DRA Helm chart

  1. Crea un file di dra-driver-values.yaml valori per il driver DRA:

    --- nvidiaDriverRoot: / gpuResourcesEnabledOverride: true resources: gpus: enabled: true computeDomains: enabled: true # Enable for GB200 IMEX support controller: tolerations: - key: nvidia.com/gpu operator: Exists effect: NoSchedule kubeletPlugin: affinity: nodeAffinity: requiredDuringSchedulingIgnoredDuringExecution: nodeSelectorTerms: - matchExpressions: - key: "nvidia.com/gpu.present" operator: In values: ["true"] tolerations: - key: nvidia.com/gpu operator: Exists effect: NoSchedule
  2. Aggiungi il repository NVIDIA NGC Helm:

    helm repo add nvidia https://helm.ngc.nvidia.com/nvidia helm repo update
  3. Installa il driver NVIDIA DRA:

    helm install nvidia-dra-driver nvidia/nvidia-dra-driver-gpu \ --version="25.3.0-rc.2" \ --namespace nvidia-dra-driver \ --create-namespace \ --values dra-driver-values.yaml

Fase 4: Verificare l'installazione del DRA

  1. Verificate che le risorse dell'API DRA siano disponibili:

    kubectl api-resources | grep [resource.k8s.io/v1beta1](http://resource.k8s.io/v1beta1)

    Di seguito è riportato l'output previsto:

    deviceclasses [resource.k8s.io/v1beta1](http://resource.k8s.io/v1beta1) false DeviceClass resourceclaims [resource.k8s.io/v1beta1](http://resource.k8s.io/v1beta1) true ResourceClaim resourceclaimtemplates [resource.k8s.io/v1beta1](http://resource.k8s.io/v1beta1) true ResourceClaimTemplate resourceslices [resource.k8s.io/v1beta1](http://resource.k8s.io/v1beta1) false ResourceSlice
  2. Controlla le classi di dispositivi disponibili:

    kubectl get deviceclasses

    Di seguito è riportato un esempio di output previsto:

    NAME AGE compute-domain-daemon.nvidia.com 4h39m compute-domain-default-channel.nvidia.com 4h39m gpu.nvidia.com 4h39m mig.nvidia.com 4h39m

    Quando un'istanza GPU G6 appena creata si unisce al cluster Amazon EKS con DRA abilitato, si verificano le seguenti azioni:

    • Il driver NVIDIA DRA rileva automaticamente la GPU A10G e ne crea due su quel nodo. resourceslices

    • La gpu.nvidia.com slice registra il dispositivo GPU A10G fisico con le sue specifiche (memoria, capacità di elaborazione e altro).

    • Poiché A10G non supporta il partizionamento MIG, la compute-domain.nvidia.com slice crea un singolo dominio di calcolo che rappresenta l'intero contesto di calcolo della GPU.

    • Questi resourceslices vengono quindi pubblicati sul server API Kubernetes, rendendo le risorse della GPU disponibili per la pianificazione. resourceclaims

      Lo scheduler DRA può ora allocare in modo intelligente questa GPU ai Pod che richiedono risorse GPU, fornendo una gestione delle risorse più flessibile rispetto agli approcci tradizionali basati sui plug-in dei dispositiviresourceclaimtemplates. Ciò avviene automaticamente senza intervento manuale. Il nodo diventa disponibile semplicemente per i carichi di lavoro della GPU una volta che il driver DRA ha completato il processo di individuazione e registrazione delle risorse.

      Quando si esegue il comando seguente:

      kubectl get resourceslices

      Di seguito è riportato un esempio di output previsto:

      NAME NODE DRIVER POOL AGE ip-100-64-129-47.ec2.internal-compute-domain.nvidia.com-rwsts ip-100-64-129-47.ec2.internal compute-domain.nvidia.com ip-100-64-129-47.ec2.internal 35m ip-100-64-129-47.ec2.internal-gpu.nvidia.com-6kndg ip-100-64-129-47.ec2.internal gpu.nvidia.com ip-100-64-129-47.ec2.internal 35m

Continua su Pianifica un carico di lavoro GPU semplice utilizzando l'allocazione dinamica delle risorse.

Pianifica un carico di lavoro GPU semplice utilizzando l'allocazione dinamica delle risorse

Per pianificare un carico di lavoro GPU semplice utilizzando l'allocazione dinamica delle risorse (DRA), procedi nel seguente modo. Prima di procedere, assicurati di averlo seguito. Configura l'allocazione dinamica delle risorse per la gestione avanzata delle GPU

  1. Crea una base ResourceClaimTemplate per l'allocazione della GPU con un file denominato: basic-gpu-claim-template.yaml

    --- apiVersion: v1 kind: Namespace metadata: name: gpu-test1 --- apiVersion: resource.k8s.io/v1beta1 kind: ResourceClaimTemplate metadata: namespace: gpu-test1 name: single-gpu spec: spec: devices: requests: - name: gpu deviceClassName: gpu.nvidia.com
  2. Applica il modello:

    kubectl apply -f basic-gpu-claim-template.yaml
  3. Verifica lo stato:

    kubectl get resourceclaimtemplates -n gpu-test1

    Di seguito è riportato un output di esempio:

    NAME AGE single-gpu 9m16s
  4. Crea un Pod che usi il ResourceClaimTemplate con un file chiamatobasic-gpu-pod.yaml:

    --- apiVersion: v1 kind: Pod metadata: namespace: gpu-test1 name: gpu-pod labels: app: pod spec: containers: - name: ctr0 image: ubuntu:22.04 command: ["bash", "-c"] args: ["nvidia-smi -L; trap 'exit 0' TERM; sleep 9999 & wait"] resources: claims: - name: gpu0 resourceClaims: - name: gpu0 resourceClaimTemplateName: single-gpu nodeSelector: NodeGroupType: gpu-dra nvidia.com/gpu.present: "true" tolerations: - key: "nvidia.com/gpu" operator: "Exists" effect: "NoSchedule"
  5. Applica e monitora il Pod:

    kubectl apply -f basic-gpu-pod.yaml
  6. Controlla lo stato del Pod:

    kubectl get pod -n gpu-test1

    Di seguito è riportato un esempio di output previsto:

    NAME READY STATUS RESTARTS AGE gpu-pod 1/1 Running 0 13m
  7. Controlla lo ResourceClaim stato:

    kubectl get resourceclaims -n gpu-test1

    Di seguito è riportato un esempio di output previsto:

    NAME STATE AGE gpu-pod-gpu0-l76cg allocated,reserved 9m6s
  8. Visualizza i log dei Pod per visualizzare le informazioni sulla GPU:

    kubectl logs gpu-pod -n gpu-test1

    Di seguito è riportato un esempio di output previsto:

    GPU 0: NVIDIA L4 (UUID: GPU-da7c24d7-c7e3-ed3b-418c-bcecc32af7c5)

Continua a leggere Tecniche di ottimizzazione della GPU con allocazione dinamica delle risorse per tecniche di ottimizzazione della GPU più avanzate utilizzando DRA.

Tecniche di ottimizzazione della GPU con allocazione dinamica delle risorse

I carichi di lavoro GPU moderni richiedono una gestione sofisticata delle risorse per ottenere un utilizzo ottimale e un'efficienza dei costi. DRA consente diverse tecniche di ottimizzazione avanzate che rispondono a diversi casi d'uso e funzionalità hardware:

Queste tecniche possono migliorare significativamente l'utilizzo delle risorse. Le organizzazioni segnalano che l'utilizzo della GPU aumenta dal 30-40% con l'allocazione tradizionale all'80-90% con strategie di condivisione ottimizzate. La scelta della tecnica dipende dalle caratteristiche del carico di lavoro, dai requisiti di isolamento e dalle capacità hardware.

Ottimizza i carichi di lavoro della GPU con time-slicing

Il time-slicing consente a più carichi di lavoro di condividere le risorse di calcolo della GPU pianificandone l'esecuzione in sequenza sulla stessa GPU fisica. È ideale per carichi di lavoro di inferenza con utilizzo sporadico della GPU.

Effettuare le seguenti operazioni.

  1. Definite un file ResourceClaimTemplate per il time-slicing con un file denominato: timeslicing-claim-template.yaml

    --- apiVersion: v1 kind: Namespace metadata: name: timeslicing-gpu --- apiVersion: resource.k8s.io/v1beta1 kind: ResourceClaimTemplate metadata: name: timeslicing-gpu-template namespace: timeslicing-gpu spec: spec: devices: requests: - name: shared-gpu deviceClassName: gpu.nvidia.com config: - requests: ["shared-gpu"] opaque: driver: gpu.nvidia.com parameters: apiVersion: resource.nvidia.com/v1beta1 kind: GpuConfig sharing: strategy: TimeSlicing
  2. Definite un Pod usando il time-slicing con un file chiamato: timeslicing-pod.yaml

    --- # Pod 1 - Inference workload apiVersion: v1 kind: Pod metadata: name: inference-pod-1 namespace: timeslicing-gpu labels: app: gpu-inference spec: restartPolicy: Never containers: - name: inference-container image: nvcr.io/nvidia/pytorch:25.04-py3 command: ["python", "-c"] args: - | import torch import time import os print(f"=== POD 1 STARTING ===") print(f"GPU available: {torch.cuda.is_available()}") print(f"GPU count: {torch.cuda.device_count()}") if torch.cuda.is_available(): device = torch.cuda.current_device() print(f"Current GPU: {torch.cuda.get_device_name(device)}") print(f"GPU Memory: {torch.cuda.get_device_properties(device).total_memory / 1024**3:.1f} GB") # Simulate inference workload for i in range(20): x = torch.randn(1000, 1000).cuda() y = torch.mm(x, x.t()) print(f"Pod 1 - Iteration {i+1} completed at {time.strftime('%H:%M:%S')}") time.sleep(60) else: print("No GPU available!") time.sleep(5) resources: claims: - name: shared-gpu-claim resourceClaims: - name: shared-gpu-claim resourceClaimTemplateName: timeslicing-gpu-template nodeSelector: NodeGroupType: "gpu-dra" nvidia.com/gpu.present: "true" tolerations: - key: nvidia.com/gpu operator: Exists effect: NoSchedule --- # Pod 2 - Training workload apiVersion: v1 kind: Pod metadata: name: training-pod-2 namespace: timeslicing-gpu labels: app: gpu-training spec: restartPolicy: Never containers: - name: training-container image: nvcr.io/nvidia/pytorch:25.04-py3 command: ["python", "-c"] args: - | import torch import time import os print(f"=== POD 2 STARTING ===") print(f"GPU available: {torch.cuda.is_available()}") print(f"GPU count: {torch.cuda.device_count()}") if torch.cuda.is_available(): device = torch.cuda.current_device() print(f"Current GPU: {torch.cuda.get_device_name(device)}") print(f"GPU Memory: {torch.cuda.get_device_properties(device).total_memory / 1024**3:.1f} GB") # Simulate training workload with heavier compute for i in range(15): x = torch.randn(2000, 2000).cuda() y = torch.mm(x, x.t()) loss = torch.sum(y) print(f"Pod 2 - Training step {i+1}, Loss: {loss.item():.2f} at {time.strftime('%H:%M:%S')}") time.sleep(5) else: print("No GPU available!") time.sleep(60) resources: claims: - name: shared-gpu-claim-2 resourceClaims: - name: shared-gpu-claim-2 resourceClaimTemplateName: timeslicing-gpu-template nodeSelector: NodeGroupType: "gpu-dra" nvidia.com/gpu.present: "true" tolerations: - key: nvidia.com/gpu operator: Exists effect: NoSchedule
  3. Applica il modello e il Pod:

    kubectl apply -f timeslicing-claim-template.yaml kubectl apply -f timeslicing-pod.yaml
  4. Monitora le richieste di risorse:

    kubectl get resourceclaims -n timeslicing-gpu -w

    Di seguito è riportato un output di esempio:

    NAME STATE AGE inference-pod-1-shared-gpu-claim-9p97x allocated,reserved 21s training-pod-2-shared-gpu-claim-2-qghnb pending 21s inference-pod-1-shared-gpu-claim-9p97x pending 105s training-pod-2-shared-gpu-claim-2-qghnb pending 105s inference-pod-1-shared-gpu-claim-9p97x pending 105s training-pod-2-shared-gpu-claim-2-qghnb allocated,reserved 105s inference-pod-1-shared-gpu-claim-9p97x pending 105s

Primo Pod (inference-pod-1)

  • Stato: allocated,reserved

  • Significato: DRA ha trovato una GPU disponibile e l'ha riservata per questo Pod

  • Stato del pod: inizia a funzionare immediatamente

Secondo Pod (training-pod-2)

  • Stato: pending

  • Significato: in attesa che DRA configuri il time-slicing sulla stessa GPU

  • Stato del pod: In attesa di essere programmato

  • Lo stato passerà dal pending allocated,reserved al running

Ottimizza i carichi di lavoro della GPU con MPS

Multi-Process Service (MPS) consente l'esecuzione simultanea di più contesti CUDA su una singola GPU con un isolamento migliore rispetto al time-slicing.

Effettuare le seguenti operazioni.

  1. Definite un file ResourceClaimTemplate per MPS con il nomemps-claim-template.yaml:

    --- apiVersion: v1 kind: Namespace metadata: name: mps-gpu --- apiVersion: resource.k8s.io/v1beta1 kind: ResourceClaimTemplate metadata: name: mps-gpu-template namespace: mps-gpu spec: spec: devices: requests: - name: shared-gpu deviceClassName: gpu.nvidia.com config: - requests: ["shared-gpu"] opaque: driver: gpu.nvidia.com parameters: apiVersion: resource.nvidia.com/v1beta1 kind: GpuConfig sharing: strategy: MPS
  2. Definite un Pod utilizzando MPS con un file denominato: mps-pod.yaml

    --- # Single Pod with Multiple Containers sharing GPU via MPS apiVersion: v1 kind: Pod metadata: name: mps-multi-container-pod namespace: mps-gpu labels: app: mps-demo spec: restartPolicy: Never containers: # Container 1 - Inference workload - name: inference-container image: nvcr.io/nvidia/pytorch:25.04-py3 command: ["python", "-c"] args: - | import torch import torch.nn as nn import time import os print(f"=== INFERENCE CONTAINER STARTING ===") print(f"Process ID: {os.getpid()}") print(f"GPU available: {torch.cuda.is_available()}") print(f"GPU count: {torch.cuda.device_count()}") if torch.cuda.is_available(): device = torch.cuda.current_device() print(f"Current GPU: {torch.cuda.get_device_name(device)}") print(f"GPU Memory: {torch.cuda.get_device_properties(device).total_memory / 1024**3:.1f} GB") # Create inference model model = nn.Sequential( nn.Linear(1000, 500), nn.ReLU(), nn.Linear(500, 100) ).cuda() # Run inference for i in range(1, 999999): with torch.no_grad(): x = torch.randn(128, 1000).cuda() output = model(x) result = torch.sum(output) print(f"Inference Container PID {os.getpid()}: Batch {i}, Result: {result.item():.2f} at {time.strftime('%H:%M:%S')}") time.sleep(2) else: print("No GPU available!") time.sleep(60) resources: claims: - name: shared-gpu-claim request: shared-gpu # Container 2 - Training workload - name: training-container image: nvcr.io/nvidia/pytorch:25.04-py3 command: ["python", "-c"] args: - | import torch import torch.nn as nn import time import os print(f"=== TRAINING CONTAINER STARTING ===") print(f"Process ID: {os.getpid()}") print(f"GPU available: {torch.cuda.is_available()}") print(f"GPU count: {torch.cuda.device_count()}") if torch.cuda.is_available(): device = torch.cuda.current_device() print(f"Current GPU: {torch.cuda.get_device_name(device)}") print(f"GPU Memory: {torch.cuda.get_device_properties(device).total_memory / 1024**3:.1f} GB") # Create training model model = nn.Sequential( nn.Linear(2000, 1000), nn.ReLU(), nn.Linear(1000, 500), nn.ReLU(), nn.Linear(500, 10) ).cuda() criterion = nn.MSELoss() optimizer = torch.optim.Adam(model.parameters(), lr=0.001) # Run training for epoch in range(1, 999999): x = torch.randn(64, 2000).cuda() target = torch.randn(64, 10).cuda() optimizer.zero_grad() output = model(x) loss = criterion(output, target) loss.backward() optimizer.step() print(f"Training Container PID {os.getpid()}: Epoch {epoch}, Loss: {loss.item():.4f} at {time.strftime('%H:%M:%S')}") time.sleep(3) else: print("No GPU available!") time.sleep(60) resources: claims: - name: shared-gpu-claim request: shared-gpu resourceClaims: - name: shared-gpu-claim resourceClaimTemplateName: mps-gpu-template nodeSelector: NodeGroupType: "gpu-dra" nvidia.com/gpu.present: "true" tolerations: - key: nvidia.com/gpu operator: Exists effect: NoSchedule
  3. Applica il modello e crea più MPS Pod:

    kubectl apply -f mps-claim-template.yaml kubectl apply -f mps-pod.yaml
  4. Monitora le richieste di risorse:

    kubectl get resourceclaims -n mps-gpu -w

    Di seguito è riportato un output di esempio:

    NAME STATE AGE mps-multi-container-pod-shared-gpu-claim-2p9kx allocated,reserved 86s

Questa configurazione dimostra una vera condivisione della GPU utilizzando NVIDIA Multi-Process Service (MPS) tramite l'allocazione dinamica delle risorse (DRA). A differenza del time-slicing, in cui i carichi di lavoro utilizzano a turno la GPU in sequenza, MPS consente l'esecuzione simultanea di entrambi i container sulla stessa GPU fisica. L'intuizione principale è che la condivisione di DRA MPS richiede più contenitori all'interno di un singolo Pod, non più Pod separati. Una volta distribuito, il driver DRA ne ResourceClaim assegna uno al Pod e configura automaticamente MPS per consentire l'esecuzione simultanea dei contenitori di inferenza e di addestramento.

Ogni contenitore dispone del proprio spazio di memoria GPU e delle proprie risorse di calcolo isolate, con il daemon MPS che coordina l'accesso all'hardware sottostante. È possibile verificare che funzioni effettuando le seguenti operazioni:

  • Verificanvidia-smi, che mostrerà entrambi i contenitori come processi M+C (MPS + Compute) che condividono lo stesso dispositivo GPU.

  • Monitoraggio dei log di entrambi i contenitori, che mostreranno timestamp interlacciati che dimostrano l'esecuzione simultanea.

Questo approccio massimizza l'utilizzo della GPU consentendo ai carichi di lavoro complementari di condividere in modo efficiente il costoso hardware della GPU, anziché lasciarlo sottoutilizzato da un singolo processo.

Contenitore 1: inference-container
root@mps-multi-container-pod:/workspace# nvidia-smi Wed Jul 16 21:09:30 2025 +-----------------------------------------------------------------------------------------+ | NVIDIA-SMI 570.158.01 Driver Version: 570.158.01 CUDA Version: 12.9 | |-----------------------------------------+------------------------+----------------------+ | GPU Name Persistence-M | Bus-Id Disp.A | Volatile Uncorr. ECC | | Fan Temp Perf Pwr:Usage/Cap | Memory-Usage | GPU-Util Compute M. | | | | MIG M. | |=========================================+========================+======================| | 0 NVIDIA L4 On | 00000000:35:00.0 Off | 0 | | N/A 48C P0 28W / 72W | 597MiB / 23034MiB | 0% E. Process | | | | N/A | +-----------------------------------------+------------------------+----------------------+ +-----------------------------------------------------------------------------------------+ | Processes: | | GPU GI CI PID Type Process name GPU Memory | | ID ID Usage | |=========================================================================================| | 0 N/A N/A 1 M+C python 246MiB | +-----------------------------------------------------------------------------------------+
Contenitore 2: training-container
root@mps-multi-container-pod:/workspace# nvidia-smi Wed Jul 16 21:16:00 2025 +-----------------------------------------------------------------------------------------+ | NVIDIA-SMI 570.158.01 Driver Version: 570.158.01 CUDA Version: 12.9 | |-----------------------------------------+------------------------+----------------------+ | GPU Name Persistence-M | Bus-Id Disp.A | Volatile Uncorr. ECC | | Fan Temp Perf Pwr:Usage/Cap | Memory-Usage | GPU-Util Compute M. | | | | MIG M. | |=========================================+========================+======================| | 0 NVIDIA L4 On | 00000000:35:00.0 Off | 0 | | N/A 51C P0 28W / 72W | 597MiB / 23034MiB | 0% E. Process | | | | N/A | +-----------------------------------------+------------------------+----------------------+ +-----------------------------------------------------------------------------------------+ | Processes: | | GPU GI CI PID Type Process name GPU Memory | | ID ID Usage | |=========================================================================================| | 0 N/A N/A 1 M+C python 314MiB | +-----------------------------------------------------------------------------------------+

Ottimizza i carichi di lavoro della GPU con GPU multiistanza

La GPU multiistanza (MIG) fornisce il partizionamento a livello hardware, creando istanze GPU isolate con risorse di calcolo e memoria dedicate.

L'utilizzo del partizionamento MIG dinamico con vari profili richiede l'operatore GPU NVIDIA. L'operatore GPU NVIDIA utilizza MIG Manager per creare profili MIG e riavviare le istanze GPU come P4D, P4De, P5, P6 e altre per applicare le modifiche alla configurazione. L'operatore GPU include funzionalità complete di gestione MIG tramite il componente MIG Manager, che controlla le modifiche alle etichette dei nodi e applica automaticamente la configurazione MIG appropriata. Quando viene richiesta una modifica del profilo MIG, l'operatore spegne automaticamente tutti i client GPU, applica la nuova geometria della partizione e riavvia i servizi interessati. Questo processo richiede il riavvio del nodo per le istanze GPU per garantire transizioni di stato della GPU pulite. Ecco perché l'abilitazione WITH–0—REBOOT=true nella configurazione MIG Manager è essenziale per implementazioni MIG di successo.

Sono necessari sia NVIDIA DRA Driver che NVIDIA GPU Operator per lavorare con MIG in Amazon EKS. Inoltre, non sono necessari NVIDIA Device Plugin e DCGM Exporter, poiché fanno parte di NVIDIA GPU Operator. Poiché EKS NVIDIA AMIs viene fornito con i driver NVIDIA preinstallati, abbiamo disabilitato l'implementazione dei driver da parte dell'operatore GPU per evitare conflitti e sfruttare i driver ottimizzati già presenti sulle istanze. Il driver NVIDIA DRA gestisce l'allocazione dinamica delle risorse per le istanze MIG, mentre l'operatore GPU gestisce l'intero ciclo di vita della GPU. Ciò include la configurazione MIG, la funzionalità dei plug-in dei dispositivi, il monitoraggio tramite DCGM e l'individuazione delle funzionalità dei nodi. Questo approccio integrato offre una soluzione completa per la gestione delle GPU aziendali, con isolamento a livello hardware e funzionalità di allocazione dinamica delle risorse.

Fase 1: Implementare NVIDIA GPU Operator
  1. Aggiungi il repository NVIDIA GPU Operator:

    helm repo add nvidia https://nvidia.github.io/gpu-operator helm repo update
  2. Crea un file: gpu-operator-values.yaml

    driver: enabled: false mig: strategy: mixed migManager: enabled: true env: - name: WITH_REBOOT value: "true" config: create: true name: custom-mig-parted-configs default: "all-disabled" data: config.yaml: |- version: v1 mig-configs: all-disabled: - devices: all mig-enabled: false # P4D profiles (A100 40GB) p4d-half-balanced: - devices: [0, 1, 2, 3] mig-enabled: true mig-devices: "1g.5gb": 2 "2g.10gb": 1 "3g.20gb": 1 - devices: [4, 5, 6, 7] mig-enabled: false # P4DE profiles (A100 80GB) p4de-half-balanced: - devices: [0, 1, 2, 3] mig-enabled: true mig-devices: "1g.10gb": 2 "2g.20gb": 1 "3g.40gb": 1 - devices: [4, 5, 6, 7] mig-enabled: false devicePlugin: enabled: true config: name: "" create: false default: "" toolkit: enabled: true nfd: enabled: true gfd: enabled: true dcgmExporter: enabled: true serviceMonitor: enabled: true interval: 15s honorLabels: false additionalLabels: release: kube-prometheus-stack nodeStatusExporter: enabled: false operator: defaultRuntime: containerd runtimeClass: nvidia resources: limits: cpu: 500m memory: 350Mi requests: cpu: 200m memory: 100Mi daemonsets: tolerations: - key: "nvidia.com/gpu" operator: "Exists" effect: "NoSchedule" nodeSelector: accelerator: nvidia priorityClassName: system-node-critical
  3. Installa GPU Operator utilizzando il gpu-operator-values.yaml file:

    helm install gpu-operator nvidia/gpu-operator \ --namespace gpu-operator \ --create-namespace \ --version v25.3.1 \ --values gpu-operator-values.yaml

    Questo grafico Helm utilizza i seguenti componenti e più profili MIG:

    • Device Plugin (pianificazione delle risorse GPU)

    • DCGM Exporter (metriche e monitoraggio della GPU)

    • Node Feature Discovery (NFD - etichettatura hardware)

    • GPU Feature Discovery (GFD - etichettatura specifica per GPU)

    • MIG Manager (partizionamento GPU multiistanza)

    • Container Toolkit (runtime del contenitore GPU)

    • Operator Controller (gestione del ciclo di vita)

  4. Verifica i pod di distribuzione:

    kubectl get pods -n gpu-operator

    Di seguito è riportato un output di esempio:

    NAME READY STATUS RESTARTS AGE gpu-feature-discovery-27rdq 1/1 Running 0 3h31m gpu-operator-555774698d-48brn 1/1 Running 0 4h8m nvidia-container-toolkit-daemonset-sxmh9 1/1 Running 1 (3h32m ago) 4h1m nvidia-cuda-validator-qb77g 0/1 Completed 0 3h31m nvidia-dcgm-exporter-cvzd7 1/1 Running 0 3h31m nvidia-device-plugin-daemonset-5ljm5 1/1 Running 0 3h31m nvidia-gpu-operator-node-feature-discovery-gc-67f66fc557-q5wkt 1/1 Running 0 4h8m nvidia-gpu-operator-node-feature-discovery-master-5d8ffddcsl6s6 1/1 Running 0 4h8m nvidia-gpu-operator-node-feature-discovery-worker-6t4w7 1/1 Running 1 (3h32m ago) 4h1m nvidia-gpu-operator-node-feature-discovery-worker-9w7g8 1/1 Running 0 4h8m nvidia-gpu-operator-node-feature-discovery-worker-k5fgs 1/1 Running 0 4h8m nvidia-mig-manager-zvf54 1/1 Running 1 (3h32m ago) 3h35m
  5. Crea un cluster Amazon EKS con un gruppo di nodi gestiti P4de per testare gli esempi MIG:

    apiVersion: eksctl.io/v1alpha5 kind: ClusterConfig metadata: name: dra-eks-cluster region: us-east-1 version: '1.33' managedNodeGroups: # P4DE MIG Node Group with Capacity Block Reservation - name: p4de-mig-nodes amiFamily: AmazonLinux2023 instanceType: p4de.24xlarge # Capacity settings desiredCapacity: 0 minSize: 0 maxSize: 1 # Use specific subnet in us-east-1b for capacity reservation subnets: - us-east-1b # AL2023 NodeConfig for RAID0 local storage only nodeadmConfig: apiVersion: node.eks.aws/v1alpha1 kind: NodeConfig spec: instance: localStorage: strategy: RAID0 # Node labels for MIG configuration labels: nvidia.com/gpu.present: "true" nvidia.com/gpu.product: "A100-SXM4-80GB" nvidia.com/mig.config: "p4de-half-balanced" node-type: "p4de" vpc.amazonaws.com/efa.present: "true" accelerator: "nvidia" # Node taints taints: - key: nvidia.com/gpu value: "true" effect: NoSchedule # EFA support efaEnabled: true # Placement group for high-performance networking placementGroup: groupName: p4de-placement-group strategy: cluster # Capacity Block Reservation (CBR) # Ensure CBR ID matches the subnet AZ with the Nodegroup subnet spot: false capacityReservation: capacityReservationTarget: capacityReservationId: "cr-abcdefghij" # Replace with your capacity reservation ID

    NVIDIA GPU Operator utilizza l'etichetta aggiunta ai nodi nvidia.com/mig.config: "p4de-half-balanced" e partiziona la GPU con il profilo specificato.

  6. Accedi all'istanza. p4de

  7. Esegui il comando seguente:

    nvidia-smi -L

    Dovresti vedere il seguente output di esempio:

    [root@ip-100-64-173-145 bin]# nvidia-smi -L GPU 0: NVIDIA A100-SXM4-80GB (UUID: GPU-ab52e33c-be48-38f2-119e-b62b9935925a) MIG 3g.40gb Device 0: (UUID: MIG-da972af8-a20a-5f51-849f-bc0439f7970e) MIG 2g.20gb Device 1: (UUID: MIG-7f9768b7-11a6-5de9-a8aa-e9c424400da4) MIG 1g.10gb Device 2: (UUID: MIG-498adad6-6cf7-53af-9d1a-10cfd1fa53b2) MIG 1g.10gb Device 3: (UUID: MIG-3f55ef65-1991-571a-ac50-0dbf50d80c5a) GPU 1: NVIDIA A100-SXM4-80GB (UUID: GPU-0eabeccc-7498-c282-0ac7-d3c09f6af0c8) MIG 3g.40gb Device 0: (UUID: MIG-80543849-ea3b-595b-b162-847568fe6e0e) MIG 2g.20gb Device 1: (UUID: MIG-3af1958f-fac4-59f1-8477-9f8d08c55029) MIG 1g.10gb Device 2: (UUID: MIG-401088d2-716f-527b-a970-b1fc7a4ac6b2) MIG 1g.10gb Device 3: (UUID: MIG-8c56c75e-5141-501c-8f43-8cf22f422569) GPU 2: NVIDIA A100-SXM4-80GB (UUID: GPU-1c7a1289-243f-7872-a35c-1d2d8af22dd0) MIG 3g.40gb Device 0: (UUID: MIG-e9b44486-09fc-591a-b904-0d378caf2276) MIG 2g.20gb Device 1: (UUID: MIG-ded93941-9f64-56a3-a9b1-a129c6edf6e4) MIG 1g.10gb Device 2: (UUID: MIG-6c317d83-a078-5c25-9fa3-c8308b379aa1) MIG 1g.10gb Device 3: (UUID: MIG-2b070d39-d4e9-5b11-bda6-e903372e3d08) GPU 3: NVIDIA A100-SXM4-80GB (UUID: GPU-9a6250e2-5c59-10b7-2da8-b61d8a937233) MIG 3g.40gb Device 0: (UUID: MIG-20e3cd87-7a57-5f1b-82e7-97b14ab1a5aa) MIG 2g.20gb Device 1: (UUID: MIG-04430354-1575-5b42-95f4-bda6901f1ace) MIG 1g.10gb Device 2: (UUID: MIG-d62ec8b6-e097-5e99-a60c-abf8eb906f91) MIG 1g.10gb Device 3: (UUID: MIG-fce20069-2baa-5dd4-988a-cead08348ada) GPU 4: NVIDIA A100-SXM4-80GB (UUID: GPU-5d09daf0-c2eb-75fd-3919-7ad8fafa5f86) GPU 5: NVIDIA A100-SXM4-80GB (UUID: GPU-99194e04-ab2a-b519-4793-81cb2e8e9179) GPU 6: NVIDIA A100-SXM4-80GB (UUID: GPU-c1a1910f-465a-e16f-5af1-c6aafe499cd6) GPU 7: NVIDIA A100-SXM4-80GB (UUID: GPU-c2cfafbc-fd6e-2679-e955-2a9e09377f78)

NVIDIA GPU Operator ha applicato con successo il profilo p4de-half-balanced MIG all'istanza P4DE, creando partizioni GPU a livello hardware come configurato. Ecco come funziona il partizionamento:

L'operatore GPU ha applicato questa configurazione dal profilo MIG incorporato:

p4de-half-balanced: - devices: [0, 1, 2, 3] # First 4 GPUs: MIG enabled mig-enabled: true mig-devices: "1g.10gb": 2 # 2x small instances (10GB each) "2g.20gb": 1 # 1x medium instance (20GB) "3g.40gb": 1 # 1x large instance (40GB) - devices: [4, 5, 6, 7] # Last 4 GPUs: Full GPUs mig-enabled: false

In base al tuo nvidia-smi -L output, ecco cosa ha creato l'operatore GPU:

  • Compatibile con MIG GPUs (0-3): hardware partizionato

    • GPU 0: NVIDIA A100- 80 GB SXM4

      • MIG 3g.40gb Device 0 — Carichi di lavoro di grandi dimensioni (40 GB di memoria, 42) SMs

      • MIG 2g.20gb Device 1 — Carichi di lavoro medi (20 GB di memoria, 28) SMs

      • MIG 1g.10gb Device 2 — Piccoli carichi di lavoro (10 GB di memoria, 14) SMs

      • MIG 1g.10gb Device 3 — Piccoli carichi di lavoro (10 GB di memoria, 14) SMs

    • GPU 1: NVIDIA A100- -80 GB SXM4

      • MIG 3g.40gb Device 0 — Layout di partizione identico

      • MIG 2g.20gb Dispositivo 1

      • MIG 1g.10gb Dispositivo 2

      • MIG 1g.10gb Dispositivo 3

    • GPU 2 e GPU 3: stesso schema di GPU 0 e GPU 1

  • Completo GPUs (4-7): nessun partizionamento MIG

    • GPU 4: NVIDIA A100- -80 GB — GPU completa da 80 GB SXM4

    • GPU 5: NVIDIA A100- 80 GB — GPU completa da 80 GB SXM4

    • GPU 6: NVIDIA A100- 80 GB — GPU completa da 80 GB SXM4

    • GPU 7: NVIDIA A100- 80 GB — GPU completa da 80 GB SXM4

Una volta che l'operatore GPU NVIDIA crea le partizioni MIG, il driver NVIDIA DRA rileva automaticamente queste istanze isolate dall'hardware e le rende disponibili per l'allocazione dinamica delle risorse in Kubernetes. Il driver DRA rileva ogni istanza MIG con il suo profilo specifico (1g.10gb, 2g.20gb, 3g.40gb) e le espone come risorse pianificabili attraverso la classe del dispositivo. mig.nvidia.com

Il driver DRA monitora continuamente la topologia MIG e mantiene un inventario delle istanze disponibili in tutte. GPUs Quando un Pod richiede un profilo MIG specifico tramite aResourceClaimTemplate, il driver DRA seleziona in modo intelligente l'istanza MIG appropriata da qualsiasi GPU disponibile, abilitando una vera multi-tenancy a livello hardware. Questa allocazione dinamica consente l'esecuzione simultanea di più carichi di lavoro isolati sulla stessa GPU fisica, mantenendo rigidi limiti di risorse e garanzie prestazionali.

Fase 2: Verificare l'allocazione delle risorse MIG

Vediamo ora alcuni esempi per dimostrare come DRA alloca dinamicamente le istanze MIG a diversi carichi di lavoro. Implementate resourceclaimtemplates e testate i pod per vedere come il driver DRA colloca i carichi di lavoro tra le partizioni MIG disponibili, consentendo a più container di condividere le risorse GPU con isolamento a livello di hardware.

  1. Crea resourceclaimtemplates per contenere il MIG: mig-claim-template.yaml

    apiVersion: v1 kind: Namespace metadata: name: mig-gpu --- # Template for 3g.40gb MIG instance (Large training) apiVersion: resource.k8s.io/v1beta1 kind: ResourceClaimTemplate metadata: name: mig-large-template namespace: mig-gpu spec: spec: devices: requests: - name: mig-large deviceClassName: mig.nvidia.com selectors: - cel: expression: | device.attributes['gpu.nvidia.com'].profile == '3g.40gb' --- # Template for 2g.20gb MIG instance (Medium training) apiVersion: resource.k8s.io/v1beta1 kind: ResourceClaimTemplate metadata: name: mig-medium-template namespace: mig-gpu spec: spec: devices: requests: - name: mig-medium deviceClassName: mig.nvidia.com selectors: - cel: expression: | device.attributes['gpu.nvidia.com'].profile == '2g.20gb' --- # Template for 1g.10gb MIG instance (Small inference) apiVersion: resource.k8s.io/v1beta1 kind: ResourceClaimTemplate metadata: name: mig-small-template namespace: mig-gpu spec: spec: devices: requests: - name: mig-small deviceClassName: mig.nvidia.com selectors: - cel: expression: | device.attributes['gpu.nvidia.com'].profile == '1g.10gb'
  2. Applica i tre modelli:

    kubectl apply -f mig-claim-template.yaml
  3. Esegui il comando seguente:

    kubectl get resourceclaimtemplates -n mig-gpu

    Di seguito è riportato un output di esempio:

    NAME AGE mig-large-template 71m mig-medium-template 71m mig-small-template 71m
  4. Crea mig-pod.yaml per pianificare più lavori per sfruttare questo resourceclaimtemplates vantaggio:

    --- # ConfigMap containing Python scripts for MIG pods apiVersion: v1 kind: ConfigMap metadata: name: mig-scripts-configmap namespace: mig-gpu data: large-training-script.py: | import torch import torch.nn as nn import torch.optim as optim import time import os print(f"=== LARGE TRAINING POD (3g.40gb) ===") print(f"Process ID: {os.getpid()}") print(f"GPU available: {torch.cuda.is_available()}") print(f"GPU count: {torch.cuda.device_count()}") if torch.cuda.is_available(): device = torch.cuda.current_device() print(f"Using GPU: {torch.cuda.get_device_name(device)}") print(f"GPU Memory: {torch.cuda.get_device_properties(device).total_memory / 1e9:.1f} GB") # Large model for 3g.40gb instance model = nn.Sequential( nn.Linear(2048, 1024), nn.ReLU(), nn.Linear(1024, 512), nn.ReLU(), nn.Linear(512, 256), nn.ReLU(), nn.Linear(256, 10) ).cuda() optimizer = optim.Adam(model.parameters()) criterion = nn.CrossEntropyLoss() print(f"Model parameters: {sum(p.numel() for p in model.parameters())}") # Training loop for epoch in range(100): # Large batch for 3g.40gb x = torch.randn(256, 2048).cuda() y = torch.randint(0, 10, (256,)).cuda() optimizer.zero_grad() output = model(x) loss = criterion(output, y) loss.backward() optimizer.step() if epoch % 10 == 0: print(f"Large Training - Epoch {epoch}, Loss: {loss.item():.4f}, GPU Memory: {torch.cuda.memory_allocated()/1e9:.2f}GB") time.sleep(3) print("Large training completed on 3g.40gb MIG instance") medium-training-script.py: | import torch import torch.nn as nn import torch.optim as optim import time import os print(f"=== MEDIUM TRAINING POD (2g.20gb) ===") print(f"Process ID: {os.getpid()}") print(f"GPU available: {torch.cuda.is_available()}") print(f"GPU count: {torch.cuda.device_count()}") if torch.cuda.is_available(): device = torch.cuda.current_device() print(f"Using GPU: {torch.cuda.get_device_name(device)}") print(f"GPU Memory: {torch.cuda.get_device_properties(device).total_memory / 1e9:.1f} GB") # Medium model for 2g.20gb instance model = nn.Sequential( nn.Linear(1024, 512), nn.ReLU(), nn.Linear(512, 256), nn.ReLU(), nn.Linear(256, 10) ).cuda() optimizer = optim.Adam(model.parameters()) criterion = nn.CrossEntropyLoss() print(f"Model parameters: {sum(p.numel() for p in model.parameters())}") # Training loop for epoch in range(100): # Medium batch for 2g.20gb x = torch.randn(128, 1024).cuda() y = torch.randint(0, 10, (128,)).cuda() optimizer.zero_grad() output = model(x) loss = criterion(output, y) loss.backward() optimizer.step() if epoch % 10 == 0: print(f"Medium Training - Epoch {epoch}, Loss: {loss.item():.4f}, GPU Memory: {torch.cuda.memory_allocated()/1e9:.2f}GB") time.sleep(4) print("Medium training completed on 2g.20gb MIG instance") small-inference-script.py: | import torch import torch.nn as nn import time import os print(f"=== SMALL INFERENCE POD (1g.10gb) ===") print(f"Process ID: {os.getpid()}") print(f"GPU available: {torch.cuda.is_available()}") print(f"GPU count: {torch.cuda.device_count()}") if torch.cuda.is_available(): device = torch.cuda.current_device() print(f"Using GPU: {torch.cuda.get_device_name(device)}") print(f"GPU Memory: {torch.cuda.get_device_properties(device).total_memory / 1e9:.1f} GB") # Small model for 1g.10gb instance model = nn.Sequential( nn.Linear(512, 256), nn.ReLU(), nn.Linear(256, 10) ).cuda() print(f"Model parameters: {sum(p.numel() for p in model.parameters())}") # Inference loop for i in range(200): with torch.no_grad(): # Small batch for 1g.10gb x = torch.randn(32, 512).cuda() output = model(x) prediction = torch.argmax(output, dim=1) if i % 20 == 0: print(f"Small Inference - Batch {i}, Predictions: {prediction[:5].tolist()}, GPU Memory: {torch.cuda.memory_allocated()/1e9:.2f}GB") time.sleep(2) print("Small inference completed on 1g.10gb MIG instance") --- # Pod 1: Large training workload (3g.40gb) apiVersion: v1 kind: Pod metadata: name: mig-large-training-pod namespace: mig-gpu labels: app: mig-large-training workload-type: training spec: restartPolicy: Never containers: - name: large-training-container image: nvcr.io/nvidia/pytorch:25.04-py3 command: ["python", "/scripts/large-training-script.py"] volumeMounts: - name: script-volume mountPath: /scripts readOnly: true resources: claims: - name: mig-large-claim resourceClaims: - name: mig-large-claim resourceClaimTemplateName: mig-large-template nodeSelector: node.kubernetes.io/instance-type: p4de.24xlarge nvidia.com/gpu.present: "true" tolerations: - key: nvidia.com/gpu operator: Exists effect: NoSchedule volumes: - name: script-volume configMap: name: mig-scripts-configmap defaultMode: 0755 --- # Pod 2: Medium training workload (2g.20gb) - can run on SAME GPU as Pod 1 apiVersion: v1 kind: Pod metadata: name: mig-medium-training-pod namespace: mig-gpu labels: app: mig-medium-training workload-type: training spec: restartPolicy: Never containers: - name: medium-training-container image: nvcr.io/nvidia/pytorch:25.04-py3 command: ["python", "/scripts/medium-training-script.py"] volumeMounts: - name: script-volume mountPath: /scripts readOnly: true resources: claims: - name: mig-medium-claim resourceClaims: - name: mig-medium-claim resourceClaimTemplateName: mig-medium-template nodeSelector: node.kubernetes.io/instance-type: p4de.24xlarge nvidia.com/gpu.present: "true" tolerations: - key: nvidia.com/gpu operator: Exists effect: NoSchedule volumes: - name: script-volume configMap: name: mig-scripts-configmap defaultMode: 0755 --- # Pod 3: Small inference workload (1g.10gb) - can run on SAME GPU as Pod 1 & 2 apiVersion: v1 kind: Pod metadata: name: mig-small-inference-pod namespace: mig-gpu labels: app: mig-small-inference workload-type: inference spec: restartPolicy: Never containers: - name: small-inference-container image: nvcr.io/nvidia/pytorch:25.04-py3 command: ["python", "/scripts/small-inference-script.py"] volumeMounts: - name: script-volume mountPath: /scripts readOnly: true resources: claims: - name: mig-small-claim resourceClaims: - name: mig-small-claim resourceClaimTemplateName: mig-small-template nodeSelector: node.kubernetes.io/instance-type: p4de.24xlarge nvidia.com/gpu.present: "true" tolerations: - key: nvidia.com/gpu operator: Exists effect: NoSchedule volumes: - name: script-volume configMap: name: mig-scripts-configmap defaultMode: 0755
  5. Applica questa specifica, che dovrebbe implementare tre Pod:

    kubctl apply -f mig-pod.yaml

    Questi Pod devono essere programmati dal driver DRA.

  6. Controllate i registri del Pod del driver DRA e vedrete un risultato simile a questo:

    I0717 21:50:22.925811 1 driver.go:87] NodePrepareResource is called: number of claims: 1 I0717 21:50:22.932499 1 driver.go:129] Returning newly prepared devices for claim '933e9c72-6fd6-49c5-933c-a896407dc6d1': [&Device{RequestNames:[mig-large],PoolName:ip-100-64-173-145.ec2.internal,DeviceName:gpu-0-mig-9-4-4,CDIDeviceIDs:[k8s.gpu.nvidia.com/device=**gpu-0-mig-9-4-4**],}] I0717 21:50:23.186472 1 driver.go:87] NodePrepareResource is called: number of claims: 1 I0717 21:50:23.191226 1 driver.go:129] Returning newly prepared devices for claim '61e5ddd2-8c2e-4c19-93ae-d317fecb44a4': [&Device{RequestNames:[mig-medium],PoolName:ip-100-64-173-145.ec2.internal,DeviceName:gpu-2-mig-14-0-2,CDIDeviceIDs:[k8s.gpu.nvidia.com/device=**gpu-2-mig-14-0-2**],}] I0717 21:50:23.450024 1 driver.go:87] NodePrepareResource is called: number of claims: 1 I0717 21:50:23.455991 1 driver.go:129] Returning newly prepared devices for claim '1eda9b2c-2ea6-401e-96d0-90e9b3c111b5': [&Device{RequestNames:[mig-small],PoolName:ip-100-64-173-145.ec2.internal,DeviceName:gpu-1-mig-19-2-1,CDIDeviceIDs:[k8s.gpu.nvidia.com/device=**gpu-1-mig-19-2-1**],}]
  7. Verifica il resourceclaims per vedere lo stato del Pod:

    kubectl get resourceclaims -n mig-gpu -w

    Di seguito è riportato un output di esempio:

    NAME STATE AGE mig-large-training-pod-mig-large-claim-6dpn8 pending 0s mig-large-training-pod-mig-large-claim-6dpn8 pending 0s mig-large-training-pod-mig-large-claim-6dpn8 allocated,reserved 0s mig-medium-training-pod-mig-medium-claim-bk596 pending 0s mig-medium-training-pod-mig-medium-claim-bk596 pending 0s mig-medium-training-pod-mig-medium-claim-bk596 allocated,reserved 0s mig-small-inference-pod-mig-small-claim-d2t58 pending 0s mig-small-inference-pod-mig-small-claim-d2t58 pending 0s mig-small-inference-pod-mig-small-claim-d2t58 allocated,reserved 0s

    Come puoi vedere, tutti i Pod sono stati spostati dalla posizione in sospeso al allocated,reserved driver DRA.

  8. Esegui nvidia-smi dal nodo. Noterai che sono in esecuzione tre processori Python:

    root@ip-100-64-173-145 bin]# nvidia-smi +-----------------------------------------------------------------------------------------+ | NVIDIA-SMI 570.158.01 Driver Version: 570.158.01 CUDA Version: 12.8 | |-----------------------------------------+------------------------+----------------------+ | GPU Name Persistence-M | Bus-Id Disp.A | Volatile Uncorr. ECC | | Fan Temp Perf Pwr:Usage/Cap | Memory-Usage | GPU-Util Compute M. | | | | MIG M. | |=========================================+========================+======================| | 0 NVIDIA A100-SXM4-80GB On | 00000000:10:1C.0 Off | On | | N/A 63C P0 127W / 400W | 569MiB / 81920MiB | N/A Default | | | | Enabled | +-----------------------------------------+------------------------+----------------------+ | 1 NVIDIA A100-SXM4-80GB On | 00000000:10:1D.0 Off | On | | N/A 56C P0 121W / 400W | 374MiB / 81920MiB | N/A Default | | | | Enabled | +-----------------------------------------+------------------------+----------------------+ | 2 NVIDIA A100-SXM4-80GB On | 00000000:20:1C.0 Off | On | | N/A 63C P0 128W / 400W | 467MiB / 81920MiB | N/A Default | | | | Enabled | +-----------------------------------------+------------------------+----------------------+ | 3 NVIDIA A100-SXM4-80GB On | 00000000:20:1D.0 Off | On | | N/A 57C P0 118W / 400W | 249MiB / 81920MiB | N/A Default | | | | Enabled | +-----------------------------------------+------------------------+----------------------+ | 4 NVIDIA A100-SXM4-80GB On | 00000000:90:1C.0 Off | 0 | | N/A 51C P0 77W / 400W | 0MiB / 81920MiB | 0% Default | | | | Disabled | +-----------------------------------------+------------------------+----------------------+ | 5 NVIDIA A100-SXM4-80GB On | 00000000:90:1D.0 Off | 0 | | N/A 46C P0 69W / 400W | 0MiB / 81920MiB | 0% Default | | | | Disabled | +-----------------------------------------+------------------------+----------------------+ | 6 NVIDIA A100-SXM4-80GB On | 00000000:A0:1C.0 Off | 0 | | N/A 52C P0 74W / 400W | 0MiB / 81920MiB | 0% Default | | | | Disabled | +-----------------------------------------+------------------------+----------------------+ | 7 NVIDIA A100-SXM4-80GB On | 00000000:A0:1D.0 Off | 0 | | N/A 47C P0 72W / 400W | 0MiB / 81920MiB | 0% Default | | | | Disabled | +-----------------------------------------+------------------------+----------------------+ +-----------------------------------------------------------------------------------------+ | MIG devices: | +------------------+----------------------------------+-----------+-----------------------+ | GPU GI CI MIG | Memory-Usage | Vol| Shared | | ID ID Dev | BAR1-Usage | SM Unc| CE ENC DEC OFA JPG | | | | ECC| | |==================+==================================+===========+=======================| | 0 2 0 0 | 428MiB / 40192MiB | 42 0 | 3 0 2 0 0 | | | 2MiB / 32767MiB | | | +------------------+----------------------------------+-----------+-----------------------+ | 0 3 0 1 | 71MiB / 19968MiB | 28 0 | 2 0 1 0 0 | | | 0MiB / 16383MiB | | | +------------------+----------------------------------+-----------+-----------------------+ | 0 9 0 2 | 36MiB / 9728MiB | 14 0 | 1 0 0 0 0 | | | 0MiB / 8191MiB | | | +------------------+----------------------------------+-----------+-----------------------+ | 0 10 0 3 | 36MiB / 9728MiB | 14 0 | 1 0 0 0 0 | | | 0MiB / 8191MiB | | | +------------------+----------------------------------+-----------+-----------------------+ | 1 1 0 0 | 107MiB / 40192MiB | 42 0 | 3 0 2 0 0 | | | 0MiB / 32767MiB | | | +------------------+----------------------------------+-----------+-----------------------+ | 1 5 0 1 | 71MiB / 19968MiB | 28 0 | 2 0 1 0 0 | | | 0MiB / 16383MiB | | | +------------------+----------------------------------+-----------+-----------------------+ | 1 13 0 2 | 161MiB / 9728MiB | 14 0 | 1 0 0 0 0 | | | 2MiB / 8191MiB | | | +------------------+----------------------------------+-----------+-----------------------+ | 1 14 0 3 | 36MiB / 9728MiB | 14 0 | 1 0 0 0 0 | | | 0MiB / 8191MiB | | | +------------------+----------------------------------+-----------+-----------------------+ | 2 1 0 0 | 107MiB / 40192MiB | 42 0 | 3 0 2 0 0 | | | 0MiB / 32767MiB | | | +------------------+----------------------------------+-----------+-----------------------+ | 2 5 0 1 | 289MiB / 19968MiB | 28 0 | 2 0 1 0 0 | | | 2MiB / 16383MiB | | | +------------------+----------------------------------+-----------+-----------------------+ | 2 13 0 2 | 36MiB / 9728MiB | 14 0 | 1 0 0 0 0 | | | 0MiB / 8191MiB | | | +------------------+----------------------------------+-----------+-----------------------+ | 2 14 0 3 | 36MiB / 9728MiB | 14 0 | 1 0 0 0 0 | | | 0MiB / 8191MiB | | | +------------------+----------------------------------+-----------+-----------------------+ | 3 1 0 0 | 107MiB / 40192MiB | 42 0 | 3 0 2 0 0 | | | 0MiB / 32767MiB | | | +------------------+----------------------------------+-----------+-----------------------+ | 3 5 0 1 | 71MiB / 19968MiB | 28 0 | 2 0 1 0 0 | | | 0MiB / 16383MiB | | | +------------------+----------------------------------+-----------+-----------------------+ | 3 13 0 2 | 36MiB / 9728MiB | 14 0 | 1 0 0 0 0 | | | 0MiB / 8191MiB | | | +------------------+----------------------------------+-----------+-----------------------+ | 3 14 0 3 | 36MiB / 9728MiB | 14 0 | 1 0 0 0 0 | | | 0MiB / 8191MiB | | | +------------------+----------------------------------+-----------+-----------------------+ +-----------------------------------------------------------------------------------------+ | Processes: | | GPU GI CI PID Type Process name GPU Memory | | ID ID Usage | |=========================================================================================| **| 0 2 0 64080 C python 312MiB | | 1 13 0 64085 C python 118MiB | | 2 5 0 64073 C python 210MiB |** +-----------------------------------------------------------------------------------------+

Ottimizza i carichi di lavoro della GPU con IMEX utilizzando 100 istanze P6e GB2

IMEX (Internode Memory Exchange) consente una comunicazione coerente in termini di memoria tra i nodi per la formazione distribuita su NVIDIA 00. GB2 UltraClusters

Effettuare le seguenti operazioni.

  1. Definite un programma ComputeDomain per l'addestramento multinodo con un file denominatoimex-compute-domain.yaml:

    apiVersion: resource.nvidia.com/v1beta1 kind: ComputeDomain metadata: name: distributed-training-domain namespace: default spec: numNodes: 2 channel: resourceClaimTemplate: name: imex-channel-template
  2. Definisci un Pod utilizzando i canali IMEX con un file denominato: imex-pod.yaml

    apiVersion: v1 kind: Pod metadata: name: imex-distributed-training namespace: default labels: app: imex-training spec: affinity: nodeAffinity: requiredDuringSchedulingIgnoredDuringExecution: nodeSelectorTerms: - matchExpressions: - key: nvidia.com/gpu.clique operator: Exists containers: - name: distributed-training image: nvcr.io/nvidia/pytorch:25.04-py3 command: ["bash", "-c"] args: - | echo "=== IMEX Channel Verification ===" ls -la /dev/nvidia-caps-imex-channels/ echo "" echo "=== GPU Information ===" nvidia-smi echo "" echo "=== NCCL Test (if available) ===" python -c " import torch import torch.distributed as dist import os print(f'CUDA available: {torch.cuda.is_available()}') print(f'CUDA device count: {torch.cuda.device_count()}') if torch.cuda.is_available(): for i in range(torch.cuda.device_count()): print(f'GPU {i}: {torch.cuda.get_device_name(i)}') # Check for IMEX environment variables imex_vars = [k for k in os.environ.keys() if 'IMEX' in k or 'NVLINK' in k] if imex_vars: print('IMEX Environment Variables:') for var in imex_vars: print(f' {var}={os.environ[var]}') print('IMEX channel verification completed') " # Keep container running for inspection sleep 3600 resources: claims: - name: imex-channel-0 - name: imex-channel-1 resourceClaims: - name: imex-channel-0 resourceClaimTemplateName: imex-channel-template - name: imex-channel-1 resourceClaimTemplateName: imex-channel-template tolerations: - key: nvidia.com/gpu operator: Exists effect: NoSchedule
    Nota

    Ciò richiede istanze P6e 00. GB2

  3. Implementa IMEX applicando i modelli and: ComputeDomain

    kubectl apply -f imex-claim-template.yaml kubectl apply -f imex-compute-domain.yaml kubectl apply -f imex-pod.yaml
  4. Controlla lo stato. ComputeDomain

    kubectl get computedomain distributed-training-domain
  5. Monitora la distribuzione del demone IMEX.

    kubectl get pods -n nvidia-dra-driver -l [resource.nvidia.com/computeDomain](http://resource.nvidia.com/computeDomain)
  6. Controlla i canali IMEX nel Pod:

    kubectl exec imex-distributed-training -- ls -la /dev/nvidia-caps-imex-channels/
  7. Visualizza i log del Pod:

    kubectl logs imex-distributed-training

    Di seguito è riportato un esempio di output previsto:

    === IMEX Channel Verification === total 0 drwxr-xr-x. 2 root root 80 Jul 8 10:45 . drwxr-xr-x. 6 root root 380 Jul 8 10:45 .. crw-rw-rw-. 1 root root 241, 0 Jul 8 10:45 channel0 crw-rw-rw-. 1 root root 241, 1 Jul 8 10:45 channel1

Per ulteriori informazioni, vedere l'esempio di NVIDIA su GitHub.