Kubernetes GPU pour ML/IA : optimiser performances et coûts

Rédigé par Matthieu ROBIN | May 13, 2026 12:00:00 PM

Autoscaling intelligent, batch scheduling et quotas : maximiser le ROI GPU

Temps de lecture : 8 minutes

Les clusters Kubernetes GPU génèrent des coûts substantiels tout en affichant fréquemment des taux d’utilisation sous-optimaux. Selon Sedai, les autoscalers traditionnels (HPA, VPA, Cluster Autoscaler) fonctionnent de manière réactive, attendant que les seuils d’utilisation soient franchis avant d’ajuster les ressources. Cette latence inhérente génère deux pathologies coûteuses : le sur-provisionnement préventif pour absorber les pics (gaspillage de 30 à 50 % du budget GPU selon Kedify), et le sous-provisionnement chronique qui dégrade les SLA et frustre les utilisateurs finaux.

L’optimisation GPU dans Kubernetes requiert une approche systémique combinant quatre leviers techniques : un autoscaling intelligent avec Karpenter pour provisionner dynamiquement les nœuds GPU optimaux, le batch scheduling via Volcano ou Kueue pour maximiser le bin-packing et éliminer la fragmentation, des quotas hiérarchiques empêchant la monopolisation des ressources par une équipe, et une observabilité granulaire via DCGM pour identifier les inefficiences. Cet article détaille ces quatre leviers, expose les pièges opérationnels observés en production, et propose un framework de déploiement progressif validé sur des clusters multi-tenant hébergeant des workloads de training et d’inférence.

 

Les 4 leviers d’optimisation GPU dans Kubernetes

L’optimisation efficace des ressources GPU dans Kubernetes repose sur quatre piliers techniques complémentaires, chacun traitant une source distincte de gaspillage.

 

1. Autoscaling dynamique : Karpenter vs Cluster Autoscaler

Cluster Autoscaler (CA), le système historique, opère via des Auto Scaling Groups (ASG) AWS ou leurs équivalents cloud. CA détecte les pods unschedulables, identifie le node group correspondant à leurs contraintes, puis demande à l’ASG d’ajouter des nœuds. Cette architecture indirecte génère trois limitations critiques : un temps de provisionnement de 3 à 5 minutes (détection des pods + scaling ASG + boot de l’instance), une granularité limitée aux node groups prédéfinis (impossible de demander dynamiquement une m5.8xlarge si seules des m5.4xlarge sont configurées), et une fragmentation des ressources (des pods demandant 6 vCPU placés sur des nœuds 8 vCPU laissent 2 vCPU inutilisables).

Karpenter, qui a atteint la v1.0.0 stable en août 2024, révolutionne l’autoscaling Kubernetes en interagissant directement avec l’API EC2. Lorsqu’un pod devient unschedulable, Karpenter évalue ses contraintes (CPU, RAM, GPU, zone, architecture), interroge l’API EC2 pour identifier les instance types satisfaisant ces contraintes, triés par coût (price-capacity-optimized), puis provisionne l’instance optimale en 45 à 60 secondes. Cette approche génère trois avantages mesurables : une vitesse 3× supérieure à CA (45 s vs 3 à 5 min selon des benchmarks Medium 2025), une granularité quasi infinie (Karpenter choisit parmi 800+ instance types EC2 sans configuration préalable), et une efficacité économique accrue (la consolidation automatique remplace les nœuds sous-utilisés par des instances moins chères).

Configuration Karpenter pour GPU :

apiVersion: karpenter.sh/v1
kind: NodePool
metadata:
  name: gpu-pool
spec:
  template:
    spec:
      requirements:
      - key: karpenter.k8s.aws/instance-gpu-count
        operator: Gt
        values: ["0"]  # Toute instance avec GPU
      - key: karpenter.k8s.aws/instance-gpu-name
        operator: In
        values: ["a100", "h100"]  # Seulement A100 ou H100
      - key: karpenter.sh/capacity-type
        operator: In
        values: ["spot", "on-demand"]  # Mix Spot/On-Demand
      nodeClassRef:
        name: gpu-class
  limits:
    cpu: "1000"
    memory: 4000Gi
    nvidia.com/gpu: "32"  # Max 32 GPU dans ce pool
  disruption:
    consolidationPolicy: WhenUnderutilized
    consolidateAfter: 30m  # Consolide après 30 min de sous-utilisation

Karpenter introduit la consolidation automatique : lorsqu’un nœud GPU tombe sous 50 % d’utilisation pendant 30 minutes (configurable), Karpenter draine ses pods vers d’autres nœuds et termine l’instance. Cette fonctionnalité, absente du Cluster Autoscaler standard, réduit les coûts de 20 à 35 % selon nOps, qui observe moins de 1 % de terminations Spot grâce aux bonnes pratiques intégrées (diversification des instances sur plusieurs AZ, reconsidération des workloads en temps réel).

 

2. Batch scheduling : Volcano et Kueue pour les workloads de training

Le scheduler Kubernetes standard opère au niveau du pod : chaque pod est évalué indépendamment et placé dès qu’un nœud satisfait ses contraintes. Cette approche échoue pour les workloads distribués où tous les pods doivent démarrer simultanément. Un PyTorchJob avec 16 pods (16 GPU) peut voir 12 pods schedulés et 4 restant en Pending indéfiniment, bloquant les 12 GPU déjà alloués sans progression du training.

Volcano, projet CNCF en adoption croissante en 2025, implémente le gang scheduling via les PodGroups : soit tous les pods du groupe sont schedulés simultanément, soit aucun. Volcano maintient les pods en état Pending jusqu’à ce que suffisamment de ressources soient disponibles pour l’ensemble, puis les ordonnance atomiquement. Cette garantie élimine les deadlocks et les GPU gaspillés en attente de pods complémentaires. Les benchmarks InfraCloud 2025 montrent que Volcano réduit la fragmentation GPU de 40 % par rapport au scheduler standard sur des workloads de training distribués.

Exemple de PodGroup Volcano :

apiVersion: batch.volcano.sh/v1alpha1
kind: Job
metadata:
  name: llama-training
spec:
  minAvailable: 16  # Gang scheduling : 16 pods ou rien
  schedulerName: volcano
  plugins:
    env: []
    svc: []
  policies:
  - event: PodEvicted
    action: RestartJob
  tasks:
  - replicas: 16
    name: worker
    template:
      spec:
        containers:
        - name: pytorch
          image: pytorch/pytorch:2.1.0-cuda12.1-cudnn8-runtime
          resources:
            limits:
              nvidia.com/gpu: 1
              cpu: "8"
              memory: 32Gi

Kueue, développé par le Kubernetes SIG Scheduling et ayant atteint la production-readiness en 2024, implémente une couche de mise en file d’attente avant le scheduler. Les workloads (Jobs, PyTorchJobs, RayJobs) sont soumis dans des LocalQueues liées à des ClusterQueues avec des quotas configurés. Kueue simule le scheduling et n’admet le workload que si les ressources sont disponibles et les quotas respectés. Les cohorts permettent à plusieurs ClusterQueues de partager les quotas inutilisés avec une logique de weighted fairness : si l’équipe A n’utilise que 50 % de son quota, l’équipe B peut emprunter temporairement le surplus.

Yunikorn, projet Apache originaire de l’écosystème Hadoop, propose un scheduler universel avec des hierarchical queues et des fairness policies (DRF - Dominant Resource Fairness). Contrairement à Volcano, qui requiert des custom resources (VolcanoJob), Yunikorn s’intègre comme scheduler de remplacement et gère nativement les workloads Kubernetes standards. Les benchmarks PEARC 2024 montrent que Yunikorn réduit les temps d’exécution des workflows de 4,6× et améliore l’utilisation du cluster de 3× par rapport au scheduler standard sur des workloads scientifiques multi-tenant.

Scheduler Gang scheduling Quotas hiérarchiques Fairness Use case optimal
Kubernetes standard Non ResourceQuota (flat) Priority FIFO Workloads single-pod, inférence
Volcano Oui (PodGroup) Via queues DRF, Proportion Training distribué, HPC
Kueue Via admission ClusterQueue + Cohorts Borrowing avec weights Multi-équipes, quotas stricts
Yunikorn Oui Oui (YARN-like) DRF natif Hybride K8s/Hadoop, workloads scientifiques

 

3. Resource quotas et policies : empêcher la monopolisation

Les ResourceQuotas Kubernetes limitent la consommation totale par namespace. Un quota GPU typique, tel que requests.nvidia.com/gpu: "16", empêche un namespace de consommer plus de 16 GPU simultanément. Les LimitRanges définissent un min/max par pod : bloquer les pods demandant 0 GPU (erreur de configuration) ou plus de 8 GPU (risque de monopolisation).

Ces mécanismes natifs présentent deux limitations. Premièrement, ils opèrent au niveau du namespace, pas au niveau de l’équipe ou du projet : une organisation avec 10 équipes nécessite 10 namespaces distincts, ce qui complexifie le RBAC et le networking. Deuxièmement, ils sont statiques : impossible de redistribuer dynamiquement le quota inutilisé d’une équipe vers une autre en cas de pic de charge.

Kueue résout ces problèmes via des ClusterQueues hiérarchiques et des cohorts. Une ClusterQueue représente un pool de ressources avec un quota nominal. Les cohorts permettent le borrowing : plusieurs ClusterQueues forment un cohort et partagent leurs quotas inutilisés selon les weights configurés. Exemple : équipe A, quota 20 GPU ; équipe B, quota 10 GPU ; cohort commun. Si l’équipe A utilise seulement 10 GPU, l’équipe B peut emprunter jusqu’à 10 GPU supplémentaires (total 20), qui seront automatiquement libérés si l’équipe A a besoin de retrouver sa capacité nominale.

Configuration Kueue avec cohorts :

apiVersion: kueue.x-k8s.io/v1beta1
kind: ResourceFlavor
metadata:
  name: a100-80gb
spec:
  nodeLabels:
    nvidia.com/gpu.product: A100-SXM4-80GB
---
apiVersion: kueue.x-k8s.io/v1beta1
kind: ClusterQueue
metadata:
  name: team-research
spec:
  cohort: shared-gpu  # Membre du cohort pour borrowing
  namespaceSelector: {}
  resourceGroups:
  - coveredResources: ["nvidia.com/gpu"]
    flavors:
    - name: a100-80gb
      resources:
      - name: nvidia.com/gpu
        nominalQuota: 20     # Quota garanti
        borrowingLimit: 10   # Peut emprunter jusqu’à 10 GPU

Les policies de préemption permettent aux workloads à haute priorité de récupérer des ressources en préemptant (terminant) des workloads à basse priorité. Volcano implémente une préemption configurable : les workloads de production (priorité haute) peuvent préempter les workloads de dev/test (priorité basse) lorsque le cluster atteint la saturation. La préemption respecte des « laws » (lois Yunikorn) : un workload ne peut préempter que si sa priorité est strictement supérieure, la préemption respecte les PodDisruptionBudgets, et les workloads préemptés sont automatiquement remis en file d’attente.

 

4. Observabilité et FinOps : mesurer pour optimiser

Optimiser sans mesurer revient à naviguer sans instruments. L’observabilité GPU dans Kubernetes combine trois couches : les métriques bas niveau (DCGM), les métriques Kubernetes (kube-state-metrics), et les métriques business (coût par job, coût par équipe).

DCGM Exporter, déployé automatiquement par GPU Operator, expose des métriques Prometheus sur le port 9400. Les métriques critiques pour l’optimisation incluent : DCGM_FI_DEV_GPU_UTIL (taux d’utilisation %, cible 60 à 80 %), DCGM_FI_DEV_FB_USED et DCGM_FI_DEV_FB_FREE (VRAM, pour identifier les GPU surdimensionnés), DCGM_FI_DEV_POWER_USAGE (consommation électrique, pour détecter les inefficiences énergétiques), et DCGM_FI_DEV_XID_ERRORS (erreurs matérielles, pour anticiper les pannes).

Les métriques Kubernetes (via kube-state-metrics) exposent : le nombre de pods GPU en Pending (indicateur de saturation du cluster), le ratio GPU requested vs allocatable par nœud (fragmentation), et les pods GPU par priorité (répartition de la charge). La corrélation entre DCGM et Kubernetes révèle les pathologies : des nœuds avec un GPU allocatable >0 mais une utilization GPU à 100 % signalent de la fragmentation (des pods demandant 2 GPU sur un nœud 4 GPU bloquent les 2 GPU restants, insuffisants pour les pods ultérieurs).

Les métriques FinOps transforment les données techniques en insights business. Karpenter expose karpenter_nodes_total_pod_requests et karpenter_nodes_allocatable, qui permettent de calculer le taux réel de bin-packing. En corrélant cela avec les données de pricing EC2, on peut calculer le coût par GPU-heure effectivement utilisée (et non simplement allouée). Kubecost, outil open source racheté par AWS en 2024, agrège ces métriques et génère des rapports : coût par namespace, par workload, par équipe, avec des recommandations d’optimisation basées sur les patterns observés.

 

Stratégies avancées d’optimisation

GPU sharing intelligent : quand et comment

Le partage GPU (MIG, time-slicing) améliore l’utilisation mais introduit des compromis. MIG fournit une isolation matérielle garantissant des performances prévisibles, idéale pour une production multi-tenant. Le time-slicing offre une granularité supérieure, mais sans isolation, ce qui le rend plutôt adapté au dev/test. Une stratégie hybride permet de combiner les avantages des deux : MIG pour les workloads de production en inférence (SLA stricts), time-slicing pour les notebooks interactifs et l’expérimentation, et GPU exclusifs pour le training intensif.

Les AWS EKS Best Practices 2024 recommandent, pour les workloads ML/IA : d’utiliser le time-slicing pour les workloads spiky avec une utilization <30 %, MIG pour l’inférence steady-state nécessitant de l’isolation, et des GPU exclusifs via Karpenter pour le training distribué. La configuration Karpenter permet de spécifier karpenter.k8s.aws/instance-gpu-name: "a100" pour forcer un modèle GPU spécifique, évitant ainsi un placement sur des T4 inadaptés.

 

Spot instances : réduire les coûts de training de 70 %

Les Spot instances AWS offrent jusqu’à 70 % de réduction par rapport à l’On-Demand, en échange d’un risque d’interruption avec un préavis de 2 minutes. Karpenter intègre nativement le support Spot via karpenter.sh/capacity-type: spot dans les requirements du NodePool. Karpenter utilise une stratégie price-capacity-optimized : prioriser les pools Spot offrant le meilleur compromis entre prix et probabilité d’interruption.

Les bonnes pratiques Spot pour le ML selon nOps incluent : diversifier les instances sur plusieurs AZ (multiplier les capacity pools Spot réduit la probabilité d’interruption simultanée), utiliser SpotToSpotConsolidation (feature gate Karpenter, remplaçant des Spot instances sous-utilisées par d’autres Spot moins chères sans disruption), et réaliser un checkpointing régulier pour les workloads de training (sauvegarder l’état toutes les N minutes pour reprendre après interruption). nOps rapporte un taux d’interruption Spot inférieur à 1 % sur des clusters Karpenter bien configurés, contre 5 à 15 % observés avec des ASG statiques.

Pour les workloads critiques ne tolérant aucune interruption (inférence de production, training final de modèles 70B+), un mix Spot/On-Demand via les priorités de NodePool reste pertinent : 80 % de capacité Spot pour les économies, 20 % d’On-Demand pour la garantie de disponibilité. Karpenter provisionne préférentiellement du Spot, puis bascule vers l’On-Demand si le Spot n’est pas disponible.

 

Autoscaling prédictif : anticiper au lieu de réagir

L’autoscaling réactif (HPA, Karpenter) attend que les métriques franchissent des seuils avant d’agir, ce qui génère une latence de 30 à 90 secondes (collecte des métriques + évaluation + scaling). L’autoscaling prédictif utilise des modèles de séries temporelles (ARIMA, Prophet) pour prévoir la charge future et préchauffer la capacité 5 à 10 minutes à l’avance.

Kedify, plateforme commerciale basée sur KEDA, introduit un predictive autoscaling piloté par les error budgets : les prévisions estiment la charge des 15 prochaines minutes, le système prewarm juste assez de capacité pour absorber le pic prévu sans dépasser le budget SLO, et les scalers réactifs prennent le relais si la prévision est erronée. Kedify recommande des horizons de prédiction courts (10 à 30 min, au-delà l’incertitude explose), de préférer les quantiles aux moyennes (prévision P95 plutôt que moyenne), et de fixer des caps stricts (budget maximum de prewarm pour éviter le gaspillage si la prévision est fausse).

L’autoscaling prédictif apporte selon Kedify une réduction de 15 à 25 % de la latence P95, mais nécessite un investissement : des données historiques suffisantes (minimum 2 semaines), des modèles réentraînés régulièrement (hebdomadairement), et un monitoring serré (comparaison forecast vs réel, ajustements en cas de drift). Le ROI se justifie surtout pour des applications exigeant une latence ultra-faible (<50 ms P99) ou gérant des pics prévisibles (publications batch, trafic diurne marqué).

 

Les 5 erreurs qui plafonnent l’utilisation GPU

1. Utiliser Cluster Autoscaler avec Karpenter simultanément

Symptôme : installer Karpenter sans désactiver Cluster Autoscaler, en espérant que les deux coexistent et se complètent.

Impact : CA et Karpenter observent tous deux les pods unschedulables et tentent chacun de provisionner des nœuds. Résultat : des race conditions avec des nœuds dupliqués, du thrashing (scaling up/down continu à cause des conflits entre les deux systèmes), et des coûts temporairement doublés. La documentation AWS Karpenter est explicite : « We recommend not using Kubernetes Cluster Autoscaler at the same time as Karpenter because both systems scale up nodes in response to unschedulable pods. » Les organisations ignorant cet avertissement rapportent 30 à 50 % de surcoûts durant la période de coexistence.

Solution : adopter une migration progressive : identifier les node pools gérés par CA, créer des NodePools Karpenter équivalents, tester en staging, basculer la production par vagues (20 % → 50 % → 100 %), puis désactiver CA une fois le cluster 100 % Karpenter. Durant la transition, utiliser des node taints pour séparer strictement les nœuds CA et Karpenter, afin d’éviter tout recouvrement. Documenter dans le runbook la procédure de rollback (réactiver CA si Karpenter pose problème). Timeline typique : 2 à 4 semaines pour une migration complète sur un cluster de plus de 50 nœuds.

2. Ignorer les disruption budgets et la consolidation Karpenter

Symptôme : activer la consolidation Karpenter (consolidationPolicy: WhenUnderutilized) sans configurer de PodDisruptionBudgets (PDB) sur les workloads critiques.

Impact : Karpenter consolide agressivement : dès qu’un nœud tombe sous 50 % d’utilisation pendant 30 minutes, il draine les pods et termine le nœud. Sans PDB, Karpenter peut drainer simultanément tous les replicas d’un service, causant du downtime. Sur les workloads stateful (bases de données, queues), la consolidation force un redémarrage pouvant prendre plusieurs minutes, ce qui dégrade les SLA. Des organisations rapportent des incidents de production (P1/P2) causés par la consolidation Karpenter sur des services sans PDB durant les premières semaines suivant le déploiement.

Solution : auditer tous les deployments/statefulsets critiques et créer des PDB appropriés : minAvailable: 1 pour les services ayant 2+ replicas (garantit qu’au moins 1 replica reste toujours en fonctionnement), maxUnavailable: 1 pour les services multi-replicas (limite les disruptions simultanées). Pour les workloads stateful, ajouter l’annotation karpenter.sh/do-not-disrupt: "true" sur les pods pour bloquer la consolidation. Tester la consolidation en staging avec du trafic synthétique, vérifier que les PDB empêchent bien les downtimes, et monitorer karpenter_disruption_pods_disrupted_total en corrélation avec les incidents de production.

3. Ne pas dimensionner les node pools avec des limits

Symptôme : créer des NodePools Karpenter ou des ClusterQueues Kueue sans spécifier de limits, en comptant sur les budgets cloud ou sur la bonne volonté des équipes.

Impact : un bug applicatif générant une boucle infinie de pods (CrashLoopBackOff recréant continuellement des pods) peut déclencher Karpenter, qui provisionnera indéfiniment des nœuds GPU. En 2 heures, un cluster peut passer de 10 à 200 nœuds H100, et la facture AWS exploser à 1’400 CHF/h (200 × 7 CHF/h). Sans limits, rien n’arrête l’escalade jusqu’à l’épuisement des billing limits AWS (si elles sont configurées) ou jusqu’à une intervention manuelle paniquée. Des incidents réels documentent des factures surprises de 50’000 à 100’000 CHF sur 48 h.

Solution : définir systématiquement des limits dans les NodePools, par exemple limits: nvidia.com/gpu: "32" (maximum 32 GPU dans ce pool), combiner cela avec les AWS Service Quotas (pour limiter le nombre d’instances GPU par région), créer des CloudWatch alarms sur le coût estimé (alerte si le coût projeté mensuel dépasse un seuil), et implémenter des admission controllers (OPA, Kyverno) pour valider que chaque pod spécifie des resource limits raisonnables. Pour Kueue, configurer nominalQuota + borrowingLimit afin d’empêcher une équipe de monopoliser le cluster entier, même en cas de bug.

4. Sous-estimer l’impact réseau sur l’autoscaling GPU

Symptôme : Karpenter provisionne rapidement des nœuds GPU (45 à 60 s), mais les pods restent en ContainerCreating pendant 5 à 10 minutes supplémentaires.

Impact : le bottleneck n’est pas Karpenter, mais la récupération d’images Docker volumineuses. Les images ML/IA (PyTorch, TensorFlow avec CUDA) pèsent de 5 à 15 Go. Sur un réseau standard, le pull d’image prend 3 à 8 minutes selon la bande passante et le cache registry. L’autoscaling rapide devient alors illusoire : les pods ne sont disponibles qu’après 6 à 11 minutes au total (45 s Karpenter + 5 à 10 min de pull d’image), contre 3 à 5 minutes avec CA. La promesse de rapidité de Karpenter ne se concrétise pas, et les utilisateurs sont frustrés par la latence perçue.

Solution : mettre en place une stratégie multi-volets : pré-cacher les images sur des AMI custom (en customisant l’AMI avec les images déjà téléchargées, ce qui réduit le cold start à moins d’une minute), utiliser des image pull policies optimisées (imagePullPolicy: IfNotPresent évite les repulls inutiles), déployer un registry cache interne (Harbor, Dragonfly) dans le même VPC que le cluster, et compresser les images (multi-stage builds, suppression des dépendances de dev). Pour les productions critiques, maintenir un warm pool de nœuds GPU pré-provisionnés (Karpenter --reserved-eni ou node pool statique minimal) permet d’éviter totalement le cold start.

5. Négliger le monitoring des coûts en temps réel

Symptôme : déployer Karpenter, Kueue et diverses optimisations, puis attendre la facture cloud mensuelle pour mesurer les économies.

Impact : des optimisations mal calibrées peuvent générer des surcoûts invisibles jusqu’au billing shock de fin de mois. Exemples fréquents : une consolidation Karpenter trop agressive provoque du thrashing (scale up/down continu) et augmente les coûts de 15 à 25 %, des quotas Kueue mal configurés laissent des GPU idle (alloués mais inutilisés) pendant des heures, ou des Spot instances avec interruptions fréquentes (>10 %) gaspillent du temps de training faute de checkpointing adapté. Sans visibilité en temps réel, impossible d’identifier et de corriger ces pathologies avant leur impact financier.

Solution : implémenter un pipeline FinOps complet : Kubecost ou un équivalent pour obtenir le coût en temps réel par namespace et workload, des dashboards Grafana avec des métriques telles que le coût par GPU-heure utilisée (et non allouée), le taux d’utilisation par node pool, le coût par équipe avec tendances sur 7 et 30 jours, ainsi que des alertes sur les anomalies (coût >20 % au-dessus de la moyenne hebdomadaire). Des revues hebdomadaires engineering + finance permettent ensuite d’analyser les dashboards et d’ajuster les configurations. ROI typique du monitoring : 1 à 2 jours de setup + 2 h/semaine de revue = 10 à 20 % du budget GPU économisés grâce aux optimisations identifiées.

 

Cas pratique : optimisation d’un cluster ML dans une startup fintech

Cas concret : une startup fintech suisse de 40 data scientists / ML engineers, opérant un cluster EKS avec des workloads de training (modèles de fraud detection) et d’inférence (scoring temps réel), disposait d’un budget GPU de 35’000 CHF/mois, régulièrement dépassé (overruns de 45’000 à 50’000 CHF). L’infrastructure initiale reposait sur Cluster Autoscaler avec 3 node groups fixes (p3.8xlarge pour le training, g4dn.xlarge pour l’inférence, m5.2xlarge pour le système), des ResourceQuotas par équipe et un monitoring basique via CloudWatch.

Problèmes identifiés après audit :

  • Fragmentation GPU : des node groups fixes (4 GPU par nœud p3.8xlarge) avec des jobs demandant 2 à 3 GPU laissaient 1 à 2 GPU idle, pour une utilization globale de 42 %.
  • Training jobs échouant 25 % du temps (absence de gang scheduling, pods partiels bloquant des GPU).
  • Overprovisioning défensif : 8 nœuds p3.8xlarge (32 GPU) maintenus 24/7 pour absorber des pics ponctuels de 2 h/jour.
  • Aucune visibilité sur le coût par équipe / projet, rendant impossible l’identification des gaspillages.

Optimisations déployées (timeline : 6 semaines)

Semaine 1-2 : migration de Cluster Autoscaler vers Karpenter

  • Installation de Karpenter v1.0.2 via Helm.
  • Création de NodePools : gpu-training (instances p3, p4d, priorité Spot), gpu-inference (g4dn, g5, On-Demand), system (m5).
  • Désactivation progressive de CA : 25 % du trafic vers Karpenter (1 semaine de monitoring), puis 100 %.
  • Résultat : temps de provisionnement des nœuds GPU passant de 4,5 min à 55 s (-80 %), bin-packing amélioré (Karpenter choisit p3.2xlarge pour les jobs à 2 GPU au lieu de p3.8xlarge).

Semaine 3-4 : implémentation de Volcano et du gang scheduling

  • Déploiement de Volcano v1.9.
  • Migration des PyTorchJobs vers VolcanoJob avec PodGroups.
  • Configuration de queues : production (priorité haute, quota 16 GPU), research (priorité moyenne, quota 24 GPU), dev (priorité basse, quota 8 GPU, préemptable).
  • Résultat : taux de succès des training jobs passant de 75 % à 96 %, réduction du temps moyen de job de 22 % grâce à la disparition des partial allocations et des retries.

Semaine 5 : déploiement de Kubecost et des dashboards FinOps

  • Installation de Kubecost v2.1.
  • Configuration d’allocation tags par équipe / projet.
  • Création de dashboards Grafana : coût en temps réel par équipe avec breakdown GPU / CPU / réseau, efficiency scores (GPU utilisée vs allouée), recommandations automatiques.
  • Découvertes : l’équipe A monopolisait 45 % du budget avec une utilization de 28 % (notebooks interactifs jamais fermés), tandis que l’équipe B affichait une efficiency de 82 % mais un quota insuffisant.

Semaine 6 : optimisations finales

  • Activation de la consolidation Karpenter avec PDB sur les services critiques.
  • Passage de 80 % des training jobs sur Spot (économies de 70 %).
  • Redistribution des quotas sur la base de l’efficience mesurée.

Résultats mesurés après 3 mois :

Métrique Avant Après Amélioration
Utilisation GPU moyenne 42 % 74 % +76 %
Coût mensuel GPU 47’000 CHF (avg overrun) 26’500 CHF -44 %
Taux de succès des training jobs 75 % 96 % +28 %
Temps moyen de provisionnement 4,5 min 55 s -80 %
Coût par training job 38 CHF 14 CHF -63 %

Économie annuelle projetée : (47’000 - 26’500) × 12 = 246’000 CHF, soit un ROI <3 mois sur l’investissement engineering (6 semaines × 1 FTE senior DevOps ≈ 25’000 CHF).

 

En résumé : 3 points clés

Karpenter transforme l’autoscaling GPU en un autoscaling beaucoup plus efficace

Cluster Autoscaler génère une latence de 3 à 5 minutes et de la fragmentation via des node groups statiques. Karpenter v1.0 (stable depuis août 2024) provisionne dynamiquement en 45 à 60 secondes l’instance optimale parmi 800+ types EC2, réduit les coûts de 20 à 35 % via la consolidation automatique, et supporte nativement Spot avec moins de 1 % d’interruptions grâce à la diversification multi-AZ et à l’approche price-capacity-optimized. Le cas pratique fintech montre des gains mesurables : temps de provisionnement -80 %, utilisation GPU +76 %, coût mensuel -44 %. Karpenter impose toutefois de la rigueur : définir des limits strictes, configurer les PDB avant la consolidation, et pré-cacher les images pour éviter le bottleneck réseau. Une migration progressive CA → Karpenter sur 2 à 4 semaines réduit les risques.

Le gang scheduling via Volcano/Kueue élimine la plupart des échecs du training distribué

Le scheduler Kubernetes standard place les pods indépendamment, ce qui génère des deadlocks sur les workloads distribués : un PyTorchJob à 16 pods peut voir 12 pods schedulés et 4 en Pending, bloquant 12 GPU sans progression. Volcano garantit l’atomicité : tous les pods sont schedulés ensemble ou aucun, ce qui élimine les partial allocations. Les benchmarks montrent un taux de succès des jobs en forte hausse et une baisse du temps moyen grâce à la disparition des retries. Kueue ajoute des quotas hiérarchiques avec borrowing, permettant aux équipes de partager dynamiquement les quotas inutilisés tout en évitant la monopolisation. Yunikorn, de son côté, apporte une alternative solide pour les environnements hybrides K8s/Hadoop et les workloads scientifiques.

Le FinOps en temps réel transforme l’optimisation d’un effort ponctuel en démarche continue

Attendre la facture mensuelle pour juger des optimisations conduit à des billing shocks récurrents. Les pathologies coûteuses — consolidation trop agressive, quotas mal calibrés, Spot mal géré — restent invisibles sans monitoring temps réel. Kubecost + Grafana exposent le coût par namespace, équipe et workload en temps réel, avec tendances et alertes. Le cas fintech a montré qu’une équipe monopolisait 45 % du budget avec seulement 28 % d’utilisation, ce qui a permis une redistribution data-driven des ressources. Les revues hebdomadaires engineering + finance permettent d’identifier en continu 10 à 20 % d’économies supplémentaires. L’investissement dans le monitoring est donc rapidement amorti.