Cloud

Des GPU utilisés à 5 % : reprendre le contrôle du coût d'inférence sur Kubernetes

Les flottes GPU tournent en moyenne autour de 5 % d'utilisation réelle, alors que les factures d'inférence explosent. Cet article fait le point sur ce que change Dynamic Resource Allocation passée en GA, sur les ordonnanceurs (Kueue, Volcano), sur le partage de GPU (MIG, time-slicing, batching) et sur la stratégie d'achat, pour ramener la discussion à la seule métrique qui compte : le coût par token.

| | | 9 min read
Rack de serveurs GPU dans un datacenter, avec un tableau de bord de supervision affichant des courbes d'utilisation faibles.

Un cluster GPU coûte cher même quand il ne fait rien. C’est toute la difficulté du problème : contrairement au CPU, où le sur-provisionnement se dilue dans des marges raisonnables, une carte accélératrice inactive brûle plusieurs euros de l’heure sans produire un seul token. Les chiffres publiés cet été sont sévères : l’utilisation moyenne des flottes GPU mesurées tourne autour de 5 %, et le meilleur cluster de l’échantillon — 136 nœuds H200 dédiés à de l’inférence — plafonne à environ 49 %. Entre les deux, aucun écart matériel : ce sont les mêmes cartes, les mêmes drivers. L’écart vient de l’ordonnancement, du partage et de la discipline de mesure.

Le constat : la facture explose pendant que les cartes dorment

Trois mécanismes expliquent l’essentiel du gaspillage.

D’abord, la réservation statique. Une équipe obtient quatre nœuds « pour son modèle », les garde en permanence, et les utilise réellement quelques heures par jour. Le reste du temps, la capacité est indisponible pour les autres mais facturée à 100 %.

Ensuite, la granularité d’allocation. Historiquement, Kubernetes expose les accélérateurs comme une ressource étendue entière : nvidia.com/gpu: 1. Un service d’inférence qui a besoin de 12 Gio de VRAM sur une carte de 80 Gio en consomme une entière. Le compteur du cluster affiche « 100 % alloué » pendant que les SM sont à 8 %.

Enfin, la mesure trompeuse. La métrique DCGM_FI_DEV_GPU_UTIL indique le pourcentage de temps pendant lequel au moins un kernel est actif — pas le taux d’occupation des unités de calcul. Un serveur d’inférence qui traite une requête à la fois peut afficher 90 % d’« utilisation » avec une occupation SM réelle de 10 %. Beaucoup de plateformes croient être saturées alors qu’elles sont simplement mal alimentées.

La conséquence pratique : le seul indicateur qui résiste à l’analyse est le coût par token (ou par requête, ou par image générée). C’est lui qui agrège le prix horaire du nœud, le débit réel du serveur, l’efficacité du batching et le taux d’inactivité. Un cluster à 15 % d’utilisation avec un excellent débit par carte peut être moins cher qu’un cluster à 40 % mal configuré.

Ce que change DRA en disponibilité générale

Dynamic Resource Allocation (DRA) est l’API qui remplace le compteur opaque par une déclaration de besoin. Le cœur de l’API est passé en GA avec Kubernetes 1.34, et le modèle est désormais stable : un driver publie des ResourceSlice décrivant les devices disponibles et leurs attributs ; l’utilisateur crée une ResourceClaim (ou un ResourceClaimTemplate) qui sélectionne des devices par des expressions CEL.

apiVersion: resource.k8s.io/v1
kind: ResourceClaimTemplate
metadata:
  name: llm-serving-gpu
spec:
  spec:
    devices:
      requests:
      - name: gpu
        exactly:
          deviceClassName: gpu.nvidia.com
          selectors:
          - cel:
              expression: |
                device.attributes["gpu.nvidia.com"].productName.matches("H100|H200") &&
                device.capacity["gpu.nvidia.com"].memory.compareTo(quantity("40Gi")) >= 0
apiVersion: v1
kind: Pod
metadata:
  name: vllm-server
spec:
  containers:
  - name: server
    image: registry.example.net/vllm:latest
    resources:
      claims:
      - name: gpu
  resourceClaims:
  - name: gpu
    resourceClaimTemplateName: llm-serving-gpu

Ce que cela débloque concrètement :

  • La sélection par capacité, pas par étiquette. Plus besoin de maintenir une forêt de nodeSelector et de taints par génération de carte.
  • Le partage explicite. Une ResourceClaim peut être référencée par plusieurs pods, ce qui rend le partage d’un device un objet de premier ordre, versionné et auditable, plutôt qu’un effet de bord de configuration du device plugin.
  • Les topologies multi-nœuds, indispensables pour les modèles dont les poids ne tiennent pas sur une carte et qui exigent un domaine NVLink ou une interconnexion cohérente.

Le second événement est de gouvernance : NVIDIA a fait don de son driver DRA à la CNCF lors de la KubeCon Europe 2026. Un driver sous gouvernance communautaire, c’est la possibilité de faire converger les conventions d’attributs entre fournisseurs — et donc d’écrire des manifestes qui survivent à un changement de cloud ou de constructeur. La création d’un SIG AI Infra annoncée le 23 juillet 2026 va dans le même sens : normaliser ce qui, jusqu’ici, relevait de l’artisanat par plateforme.

Panorama des ordonnanceurs

Le scheduler par défaut de Kubernetes est un ordonnanceur de pods, pas de jobs. Il ne sait pas dire « ces huit workers doivent démarrer ensemble ou pas du tout », ce qui produit des demi-allocations mortelles pour les workloads distribués.

Kueue est aujourd’hui la brique de référence pour la mise en file d’attente. Le modèle repose sur des ClusterQueue (le pool de quota, partagé) et des LocalQueue (le point d’entrée par namespace/équipe). Les jobs sont admis, pas rejetés : ils attendent qu’un quota se libère.

apiVersion: kueue.x-k8s.io/v1beta1
kind: ClusterQueue
metadata:
  name: gpu-shared
spec:
  namespaceSelector: {}
  cohort: research
  resourceGroups:
  - coveredResources: ["cpu", "memory", "nvidia.com/gpu"]
    flavors:
    - name: h200
      resources:
      - name: "nvidia.com/gpu"
        nominalQuota: 32
        borrowingLimit: 16   # emprunt possible aux autres files de la cohorte

Le mécanisme de cohorte est ce qui fait basculer l’économie du cluster : l’équipe A garde son quota nominal, mais quand elle n’en fait rien, l’équipe B peut l’emprunter. On passe d’une réservation statique à une réservation logique. C’est, dans la plupart des plateformes que j’ai vues, le levier au meilleur rapport effort/gain.

Volcano couvre le même territoire avec un angle HPC : gang scheduling natif, politiques de queue, plugins de binpack et de topologie. Le choix se joue surtout sur l’écosystème existant — Volcano si vous venez de Spark/MPI, Kueue si vous vivez dans les Job et les CRD d’opérateurs (Ray, Kubeflow, JobSet).

Deux réglages complètent l’ensemble, indépendamment de l’outil : une stratégie de bin-packing (MostAllocated) plutôt que de dispersion, pour vider des nœuds entiers et permettre au cluster autoscaler de les supprimer ; et une hiérarchie de PriorityClass claire, avec préemption des batchs par le trafic en ligne.

Partager un GPU : quatre techniques, quatre compromis

MIG (Multi-Instance GPU) découpe une carte en instances matériellement isolées, chacune avec sa propre mémoire et ses propres SM. L’isolation est réelle, la latence prévisible. C’est la bonne réponse pour du multi-tenant sérieux ou pour héberger plusieurs petits modèles. En contrepartie, le profil est statique (reconfigurer exige de drainer la carte) et la granularité est grossière : un profil 1g.10gb reste un investissement fixe.

Le time-slicing multiplexe temporellement plusieurs contextes sur la même carte. Aucune isolation mémoire, aucune garantie de latence, mais un coût de mise en œuvre nul et un excellent rendement pour des charges intermittentes : notebooks de data scientists, environnements de dev, tests d’intégration. À proscrire pour du trafic en production sous SLO.

Le batching continu n’est pas une technique d’infrastructure mais c’est le plus gros gain disponible. Un serveur comme vLLM ou TensorRT-LLM agrège les requêtes entrantes dans un batch dynamique et récupère les slots libérés à chaque itération. Le passage d’un traitement séquentiel à un batching continu fait souvent gagner un ordre de grandeur sur le débit par carte, au prix de quelques dizaines de millisecondes de latence de mise en file. Avant d’acheter des cartes, vérifiez ce paramètre.

La mise en cache enfin : cache de préfixe (les prompts systèmes partagés ne sont calculés qu’une fois) et cache de réponses exactes en amont. Sur des charges conversationnelles à préambule long, c’est du calcul purement supprimé — le meilleur token est celui qu’on ne génère pas.

Stratégie d’achat : trois couches, pas une

Le modèle qui tient la route est une pyramide :

  1. Instances réservées / engagements pluriannuels pour le trafic de base, celui qui existe à 4 h du matin un dimanche. C’est là que se trouvent les remises les plus fortes, et le risque est nul si le dimensionnement est fait sur le percentile bas.
  2. Spot / preemptible pour l’élasticité et pour tout le batch : fine-tuning, évaluations, régénération d’embeddings. Le rabais est substantiel, l’éviction est la norme.
  3. On-demand comme tampon uniquement, pour absorber les pics et les indisponibilités de spot.

La gestion des évictions se traite par la conception, pas par l’espoir : terminationGracePeriodSeconds généreux, handler de terminaison de nœud, checkpointing régulier pour l’entraînement, et pour l’inférence, un readiness gate qui retire l’instance du load balancer avant l’arrêt. Attention au coût caché du démarrage : charger 140 Gio de poids depuis un object storage prend plusieurs minutes, pendant lesquelles vous payez une carte qui ne sert rien. Cache local des images et des poids sur disque NVMe, ou pré-chargement via un DaemonSet.

Observabilité et FinOps : mesurer ce qui se facture

Le socle technique repose sur DCGM Exporter, mais avec les bonnes métriques :

# Occupation réelle des unités de calcul, par pod
avg by (exported_pod) (DCGM_FI_PROF_SM_ACTIVE)

# Coût horaire par pod GPU (prix du nœud injecté en métrique statique)
sum by (exported_pod) (
  DCGM_FI_DEV_FB_USED / on(instance) group_left node_gpu_memory_total
  * on(instance) group_left node_hourly_cost
)

# Coût par million de tokens générés
(sum(rate(node_hourly_cost[5m])) / 3600)
  / (sum(rate(vllm_generation_tokens_total[5m])) / 1e6)

DCGM_FI_PROF_SM_ACTIVE et DCGM_FI_PROF_PIPE_TENSOR_ACTIVE disent la vérité là où GPU_UTIL raconte une histoire flatteuse. Corrélez-les systématiquement avec la mémoire allouée : une carte à 90 % de VRAM et 10 % de SM signale un modèle trop gros pour son trafic — candidat à la quantification ou au partage.

Côté gouvernance, le FinOps Framework 2026 a été restructuré pour couvrir explicitement l’IA, le SaaS et les datacenters privés. L’apport pratique tient en deux points : d’une part, l’extension du périmètre aux capacités on-premise, ce qui permet enfin de comparer un cluster GPU interne et une instance cloud dans le même modèle ; d’autre part, l’insistance sur les unit economics — coût par token, par requête, par utilisateur actif — plutôt que sur la seule dépense mensuelle. Techniquement, cela suppose un tagging discipliné (team, model, env, workload-type) appliqué au niveau du namespace et propagé aux labels de pods, puis une normalisation des données de facturation, typiquement via le format FOCUS.

Feuille de route pour une petite plateforme

Trois actions, dans cet ordre, couvrent l’essentiel du gain dès le premier mois.

Semaine 1 — mesurer. Déployez DCGM Exporter, tracez SM_ACTIVE et la VRAM par pod, exposez les métriques de votre serveur d’inférence et construisez un unique tableau de bord « coût par million de tokens, par modèle ». Presque toutes les décisions suivantes découlent de ce graphique.

Semaines 2-3 — mutualiser. Installez Kueue, créez une ClusterQueue par équipe dans une cohorte commune, et supprimez les nœuds nominativement réservés. Le quota reste, la réservation physique disparaît. Basculez le scheduler en MostAllocated et laissez l’autoscaler faire son travail.

Semaine 4 — densifier. Vérifiez que le batching continu est actif et correctement dimensionné, activez le cache de préfixe, puis appliquez MIG sur les nœuds hébergeant plusieurs petits modèles et le time-slicing sur les nœuds de développement.

Le point commun de ces trois étapes : aucune ne demande d’acheter quoi que ce soit. L’écart entre 5 % et 49 % d’utilisation n’est pas un problème de capacité, c’est un problème de plomberie. Avec DRA en GA et un driver NVIDIA sous gouvernance CNCF, cette plomberie devient pour la première fois portable — ce qui déplace la question du « comment obtenir un GPU » vers la seule qui intéresse la direction financière : combien coûte le token que vous venez de servir.

Comments (0)

Please log in to leave a comment.

Log In

No comments yet. Be the first to comment!