Dynamic Resource Allocation : partager les GPU sur Kubernetes
Pendant des années, demander un GPU sur Kubernetes se résumait à une ligne : nvidia.com/gpu: 1. Un entier, un GPU entier, aucune nuance. Ce modèle hérité des device plugins ne répond plus aux besoins des équipes qui font tourner de l'inférence de LLM et de l'entraînement sur des cartes à plusieurs dizaines de milliers d'euros. Depuis Kubernetes 1.35, la réponse officielle est stable et s'appelle Dynamic Resource Allocation (DRA).
Équipe Blent
Publié le · 12 min de lecture

Dans cet article
Pendant des années, demander un GPU sur Kubernetes se résumait à une ligne : nvidia.com/gpu: 1. Un entier, un GPU entier, aucune nuance. Ce modèle hérité des device plugins ne répond plus aux besoins des équipes qui font tourner de l'inférence de LLM et de l'entraînement sur des cartes à plusieurs dizaines de milliers d'euros. Depuis Kubernetes 1.35, la réponse officielle est stable et s'appelle Dynamic Resource Allocation (DRA).
Introduite en alpha dans Kubernetes 1.30, l'API DRA est passée en disponibilité générale avec la version 1.35. Elle apporte aux accélérateurs ce que les volumes persistants ont apporté au stockage : des objets dédiés pour décrire un besoin, le laisser être alloué par le scheduler, puis le partager entre plusieurs pods. NVIDIA, Intel, AMD et d'autres fournisseurs développent leurs drivers DRA, et le projet de NVIDIA est désormais hébergé dans les dépôts kubernetes-sigs.
Dans cet article, nous allons d'abord revenir sur les limites du modèle des device plugins, puis détailler les objets de l'API DRA, explorer les mécanismes de partage de GPU (MIG et time-slicing) avec le driver de NVIDIA, et enfin examiner des cas d'usage concrets pour l'inférence de LLM et l'entraînement.
Les limites du modèle « nvidia.com/gpu: 1 »
Avec un device plugin, le GPU est exposé comme une ressource étendue : le plugin annonce à chaque kubelet un nombre de GPU, et les pods en demandent une quantité dans leurs limits. Le mécanisme est simple, et c'est ce qui l'a rendu populaire dans les clusters Kubernetes dédiés au machine learning. Il montre pourtant vite ses faiblesses.
- Une ressource opaque : le scheduler ne voit qu'un compteur. Il ne sait pas distinguer une A100 de 40 Go d'une H100 de 80 Go, ni deux GPU reliés par NVLink de deux GPU éloignés. Les équipes contournent ce manque avec des labels de nœuds et des
nodeSelector. - Une allocation exclusive : un GPU demandé est réservé en entier, même si le modèle servi n'en utilise que 10 % de la mémoire. Pour de petits modèles, le gaspillage est considérable.
- Un partage configuré hors de l'API : le time-slicing ou le découpage MIG se règlent au niveau du nœud, via des ConfigMaps du GPU Operator ou des labels, et produisent des ressources aux noms différents (
nvidia.com/mig-1g.5gb). Le développeur doit connaître la configuration physique du nœud. - Pas de paramètres par demande : impossible de dire « je veux un GPU avec au moins 40 Go de mémoire » ou « je veux ce mode de partage », car la demande ne porte qu'une quantité.
- Pas de partage entre pods : deux pods ne peuvent pas référencer la même ressource allouée, ce qui complique les architectures où plusieurs conteneurs doivent travailler sur un même GPU.
Ces limites expliquent pourquoi le taux d'utilisation des GPU reste faible dans beaucoup d'entreprises, un point central des démarches de FinOps appliquées aux LLM. DRA part du constat inverse : décrire précisément le matériel et le besoin pour que le scheduler prenne la bonne décision.
Comprendre l'API Dynamic Resource Allocation
DRA reprend un modèle que les équipes connaissent déjà avec le stockage : une classe, une demande, une allocation. L'API vit dans le groupe resource.k8s.io/v1 et repose sur quatre objets principaux.
DeviceClass, ResourceSlice et ResourceClaim
- ResourceSlice : publié par le driver, il décrit les appareils disponibles sur un nœud avec leurs attributs (modèle, architecture, UUID) et leurs capacités (mémoire, par exemple). C'est l'inventaire que le scheduler consulte.
- DeviceClass : définit une catégorie d'appareils et le driver qui la gère. Elle est créée par l'administrateur ou installée avec le driver, de façon comparable à une StorageClass.
- ResourceClaim : la demande d'appareils, avec une classe, des filtres écrits en CEL (Common Expression Language) et éventuellement une configuration propre au driver. Une fois allouée, elle peut être partagée par plusieurs conteneurs ou plusieurs pods.
- ResourceClaimTemplate : un modèle à partir duquel Kubernetes crée un ResourceClaim par pod, supprimé avec lui. Il convient aux Jobs parallèles où chaque pod doit disposer de son propre appareil.
Le cycle est le suivant : le driver publie les ResourceSlices, un utilisateur crée un claim (ou un template), le scheduler cherche un nœud qui satisfait la demande et alloue des appareils, puis le kubelet appelle le driver pour préparer l'appareil et l'injecter dans le conteneur via CDI (Container Device Interface). Contrairement à un device plugin, la décision d'allocation est prise par le scheduler à partir de l'inventaire complet, et non par le kubelet après coup.
Exemple de configuration
Voici un template qui demande un GPU de la classe gpu.nvidia.com disposant d'au moins 40 Gio de mémoire, puis un pod qui le consomme.
# Demande : un GPU NVIDIA avec au moins 40 Gio de mémoire
apiVersion: resource.k8s.io/v1
kind: ResourceClaimTemplate
metadata:
name: gpu-40go
spec:
spec:
devices:
requests:
- name: gpu
exactly:
deviceClassName: gpu.nvidia.com
selectors:
- cel:
expression: |-
device.capacity['gpu.nvidia.com'].memory.compareTo(quantity('40Gi')) >= 0
# Pod qui référence le template
apiVersion: v1
kind: Pod
metadata:
name: inference
spec:
containers:
- name: serveur
image: registry.example.com/serveur-llm:1.0
resources:
claims:
- name: gpu-claim
request: gpu
resourceClaims:
- name: gpu-claim
resourceClaimTemplateName: gpu-40go
Le pod ne contient plus aucune quantité de GPU dans ses limits : il déclare un claim dans resourceClaims, puis chaque conteneur indique dans resources.claims celui qu'il utilise. Le filtre CEL remplace avantageusement les nodeSelector fondés sur des labels, puisqu'il porte directement sur les attributs et les capacités publiés par le driver. Nous supposons ici que la capacité mémoire est exposée sous la clé memory, comme dans la documentation officielle ; les noms exacts des attributs sont définis par chaque driver.
DRA a aussi des limites à connaître. Le scheduler ne gère pas la préemption pour les ressources DRA : un pod prioritaire qui attend un appareil reste en attente tant qu'il n'est pas libéré. D'autres fonctionnalités avancées évoluent encore, comme les partitionable devices, passés en beta dans Kubernetes 1.36, qui permettent de décrire des appareils découpables dynamiquement.
Partager et fractionner les GPU avec le driver NVIDIA
Le driver DRA Driver for NVIDIA GPUs est le projet qui implémente cette API pour les cartes NVIDIA. Il nécessite Kubernetes 1.32 ou plus récent, et DRA est activé par défaut depuis la version 1.34. Il s'installe avec un chart Helm publié sur registry.k8s.io, en précisant la racine du driver NVIDIA (nvidiaDriverRoot) si le GPU Operator gère l'installation du pilote sur les nœuds.
Le driver enregistre plusieurs DeviceClasses, dont gpu.nvidia.com pour un GPU entier et mig.nvidia.com pour des partitions MIG. Il gère aussi les ComputeDomains, qui isolent et connectent des pods sur des GPU reliés par NVLink multi-nœud. Cette dernière fonctionnalité est la plus mature du projet ; l'allocation de GPU et les modes de partage évoluent vite, et leur statut dépend de la version du driver. Vérifiez donc la documentation de la version installée avant de les utiliser en production.
MIG : un partage avec isolation matérielle
Le MIG (Multi-Instance GPU) découpe physiquement un GPU Ampere ou plus récent en plusieurs instances, chacune avec sa mémoire et ses unités de calcul dédiées. L'isolation est forte : une instance ne peut pas perturber les autres. Avec DRA, un développeur choisit directement le profil voulu dans son claim, sans connaître la configuration du nœud.
# Demande d'une partition MIG de profil 1g.5gb
apiVersion: resource.k8s.io/v1
kind: ResourceClaimTemplate
metadata:
name: mig-petit
spec:
spec:
devices:
requests:
- name: mig
exactly:
deviceClassName: mig.nvidia.com
selectors:
- cel:
expression: "device.attributes['gpu.nvidia.com'].profile == '1g.5gb'"
Les noms de profils varient selon le modèle de GPU. En mode MIG statique, activé par défaut, l'administrateur crée les partitions sur le nœud avant le démarrage du driver, qui se contente de les découvrir. Un mode MIG dynamique, où le driver crée et détruit les partitions à la demande, existe derrière un feature gate alpha et repose sur les partitionable devices. Il détruit les partitions qu'il n'a pas créées : il ne faut donc pas l'activer sur des nœuds déjà configurés.
Time-slicing : un partage souple
Le time-slicing fait alterner les contextes CUDA de plusieurs conteneurs sur un même GPU. Il fonctionne sur toutes les cartes, mais sans isolation mémoire ni garantie de débit. Avec le driver NVIDIA, il se configure dans le claim lui-même, au moyen d'une configuration opaque transmise au driver.
# Un GPU partagé en time-slicing entre les conteneurs d'un pod
apiVersion: resource.k8s.io/v1
kind: ResourceClaimTemplate
metadata:
name: gpu-partage
spec:
spec:
devices:
requests:
- name: gpu
exactly:
deviceClassName: gpu.nvidia.com
config:
- requests: ["gpu"]
opaque:
driver: gpu.nvidia.com
parameters:
apiVersion: resource.nvidia.com/v1beta1
kind: GpuConfig
sharing:
strategy: TimeSlicing
timeSlicingConfig:
interval: Long
Les conteneurs qui référencent la même demande (request: gpu) dans le même claim partagent un seul GPU. Dans la documentation actuelle du driver, cette option est protégée par le feature gate TimeSlicingSettings, de niveau alpha et désactivé par défaut. Le time-slicing et MPS sont par ailleurs mutuellement exclusifs sur un même GPU physique, et le time-slicing n'a aucun effet sur une partition MIG.
| Approche | Isolation mémoire | Granularité | Cas d'usage typique |
|---|---|---|---|
| GPU entier | Totale | 1 GPU | Entraînement, gros LLM |
| MIG | Matérielle | Profils fixes par GPU | Inférence multi-équipes |
| Time-slicing | Aucune | Partage du temps de calcul | Développement, petits modèles |
| MPS | Partielle, par processus | Partage simultané du calcul | Plusieurs petits services d'inférence |
Le choix dépend du compromis entre isolation et densité. MIG convient quand plusieurs équipes ou plusieurs clients partagent un GPU et que la latence doit rester prévisible. Le time-slicing, plus simple, est adapté aux environnements de développement et aux modèles légers. Le détail du comportement de MPS avec DRA est décrit dans le guide dédié du driver.
Cas d'usage : inférence de LLM et entraînement
DRA prend tout son sens quand on l'applique à des charges de travail réelles. Deux situations reviennent chez les équipes plateforme et MLOps.
Inférence de LLM
Un serveur d'inférence comme vLLM, souvent exposé avec KServe, a des besoins très variables selon la taille du modèle. Un modèle de 70 milliards de paramètres demande plusieurs GPU de grande mémoire, tandis qu'un petit modèle quantifié tient dans quelques gigaoctets. Avec DRA, chaque déploiement exprime son besoin réel.
- Gros modèles : un claim avec un filtre CEL sur la mémoire, ou plusieurs appareils réunis par une contrainte d'attribut commun, pour garantir qu'ils se trouvent sur le même nœud ou la même machine.
- Petits modèles : une partition MIG par réplique, afin de faire tourner plusieurs modèles sur un seul GPU de centre de données tout en isolant les équipes.
- Environnements de test : un GPU en time-slicing partagé entre plusieurs conteneurs, pour limiter le coût des espaces de développement.
La consommation de mémoire GPU, le taux d'utilisation et la latence se suivent avec Prometheus, ce qui permet d'ajuster les profils choisis. Pour la mise à l'échelle des répliques, les mécanismes d'auto-scaling restent complémentaires : DRA décide quel appareil est attribué, pas combien de pods sont lancés.
Entraînement
Pour l'entraînement distribué, le besoin est inverse : il faut des GPU entiers, bien connectés entre eux. Les ComputeDomains du driver NVIDIA répondent à ce cas sur les systèmes à NVLink multi-nœud, en créant un domaine isolé pour les pods d'un même job. Les contraintes de DRA, comme matchAttribute, permettent aussi d'exiger que les appareils d'une même demande partagent un attribut, par exemple le même GPU parent pour des partitions MIG.
Un ResourceClaimTemplate attaché à un Job garantit que chaque pod reçoit son propre appareil, et libère l'allocation à la fin du pod. Les équipes plateforme peuvent alors publier un catalogue de DeviceClasses (GPU entier, partition MIG, GPU partagé) et laisser les data scientists choisir, dans une logique de platform engineering. Les manifestes se versionnent et se packagent avec Helm, comme le reste de la configuration, ce qui s'intègre bien dans les plateformes décrites dans notre article sur Kubernetes et le machine learning.
Pour migrer depuis les device plugins, une démarche progressive fonctionne bien : installer le driver DRA sur un pool de nœuds dédié, y déployer un premier service d'inférence avec un claim, mesurer l'utilisation, puis étendre. Il faut éviter d'exposer les mêmes GPU à la fois par le device plugin et par le driver DRA, afin de ne pas allouer deux fois la même carte.
À découvrir : notre formation DevOps Engineer
Conclusion
Dynamic Resource Allocation change la façon de consommer le matériel spécialisé sur Kubernetes. En remplaçant un compteur opaque par des objets décrivant les appareils et les besoins, il permet au scheduler de choisir le bon GPU, de le partager et de le configurer demande par demande, avec ResourceClaim, DeviceClass et filtres CEL.
Avec le driver de NVIDIA, MIG offre une isolation matérielle pour l'inférence multi-équipes, et le time-slicing une densité maximale pour les environnements de développement, chacun avec ses contraintes et son niveau de maturité. Certaines fonctionnalités restent en alpha ou en beta : un test sur un cluster de préproduction s'impose avant de généraliser.
Pour les équipes plateforme et MLOps, maîtriser DRA devient une compétence différenciante : à mesure que les GPU concentrent les coûts de l'IA, savoir les allouer finement sera aussi important que savoir déployer les modèles.


