Planification des tâches pour les clusters d'IA : SLURM, Kubernetes, Ray et savoir quand s'en passer.
Un planificateur détermine quelle tâche s'exécute sur quels GPU, quand et à quel coût. Sans lui, un cluster partagé se transforme en une situation où les utilisateurs s'envoient des messages sur Slack pour savoir si quelqu'un utilise le nœud 3. Avec un planificateur inadapté, vous passerez plus de temps à rédiger des fichiers YAML qu'à modéliser des données.
Cet article compare les planificateurs de tâches réellement utilisés — SLURM, Kubernetes (avec Kueue ou Volcano), Ray et KubeRay, ainsi que les solutions commerciales Run:ai et Determined — et reconnaît que la meilleure solution est parfois de ne rien utiliser, juste SSH. Le lecteur a lu K01, K02 et K03 et est actuellement en train de décider ce qu'il faut ajouter à un cluster de 1 à 32 nœuds.
Que fait réellement un planificateur ?
Trois tâches, plus ou moins par ordre de difficulté : placer le travail sur les ressources libres, mettre en file d’attente le travail qui ne peut pas encore être effectué et coordonner les tâches multi-nœuds — l’entraînement distribué nécessite que tous les N rangs démarrent en même temps (planification des gangs), car un PyTorch dist.barrier() bloque indéfiniment si un rang ne se présente jamais (voir K06).
Tout cluster dépassant le cadre d'une seule équipe nécessite également des quotas multi-locataires avec emprunt, une décroissance équitable entre les équipes, la préemption et une comptabilisation GPU appropriée (afin que la tâche voie CUDA_VISIBLE_DEVICES (Configuration correcte). Chaque planificateur ci-dessous effectue les trois premières opérations. Leur différence réside dans la deuxième liste et le type de tâches qu'ils supposent exécuter.
La grande distinction : traitements par lots contre services de longue durée
C'est là le cadrage le plus important. Travaux par lots Entraînement pendant 8 heures et sortie, ou exécution d'un balayage d'hyperparamètres de 200 tâches courtes, ou traitement d'un ensemble de données pendant la nuit — début, fin, résultat, rien ne répond HTTP. Services de longue durée sont un point de terminaison vLLM assurant l'inférence 24h/24 et 7j/7, une base de données de mémoire de scène, une pile de surveillance — censée rester opérationnelle en permanence et redémarrer en cas de panne.
SLURM a été conçu pour le traitement par lots. Kubernetes a été conçu pour les services de longue durée. Tout le reste consiste à étendre l'un des deux pour assurer l'autre moitié du travail.
| Charge de travail | SLURM | Kubernetes |
|---|---|---|
| Course d'entraînement de 8 heures | Originaire | Il faut Kueue ou Volcan |
| balayage des hyperparamètres de 200 tâches | Originaire | Gênant |
| Point final d'inférence vLLM 24h/24 et 7j/7 | Gênant | Originaire |
| Mixte : formation + inférence + outils | Douloureux | Native (avec Kueue) |
| Formation distribuée multi-nœuds | Autochtone (gang) | Nécessite le plugin gang |
| Services de redémarrage automatique ayant planté | Non | Originaire |
Si votre cluster n'effectue qu'une seule de ces tâches, le choix est simple. S'il effectue les deux (ce qui est de plus en plus courant), la question est de savoir pour quel planificateur vous êtes prêt à accepter le surcoût opérationnel.
SLURM — le choix par défaut en HPC qui reste la référence pour l'entraînement pur
SLURM est utilisé par plus de 65 % des supercalculateurs du TOP500 et par la plupart des programmes d'entraînement de pointe publiés. Pour les charges de travail du type « soumettre une tâche d'entraînement, attendre les résultats, obtenir le point de contrôle », il s'avère être la solution idéale depuis vingt ans.
Le modèle mental est simple : un partition est un pool de nœuds nommé (gpu-5090, cpu-only, bigmem); à JOB est un script shell avec #SBATCH directives, soumises avec sbatch; GRES (Ressources génériques) suit les GPU — --gres=gpu:rtxpro6000:2 demande deux cartes spécifiques ; planification des gangs est intégré.
Un minimum slurm.conf pour un cluster à 4 nœuds avec 8 cartes RTX Pro 6000 Blackwell par nœud :
ClusterName=kentino-lab
SlurmctldHost=head01
SchedulerType=sched/backfill
SelectType=select/cons_tres
SelectTypeParameters=CR_Core_Memory
GresTypes=gpu
# GPU accounting — without these two lines, fair-share runs on CPU-seconds and is meaningless
AccountingStorageType=accounting_storage/slurmdbd
AccountingStorageTRES=gres/gpu
PriorityType=priority/multifactor
PriorityDecayHalfLife=7-0
PriorityWeightFairshare=10000
PriorityWeightAge=1000
# cgroups isolate the GPUs a job is allowed to see
ProctrackType=proctrack/cgroup
NodeName=node[01-04] CPUs=128 RealMemory=1024000 Sockets=2 \
CoresPerSocket=64 ThreadsPerCore=1 Gres=gpu:rtxpro6000:8 State=UNKNOWN
PartitionName=train Nodes=node[01-03] Default=YES MaxTime=48:00:00 State=UP
PartitionName=interactive Nodes=node04 MaxTime=04:00:00 \
PriorityTier=10 State=UP
L'appariement gres.conf sur chaque nœud :
AutoDetect=nvml
NodeName=node[01-04] Name=gpu Type=rtxpro6000 File=/dev/nvidia[0-7]
AutoDetect=nvml Il interroge directement la bibliothèque de gestion NVIDIA, récupère automatiquement les tranches MIG et vous évite de configurer manuellement les chemins d'accès aux fichiers de périphériques.
Un utilisateur soumet :
#!/bin/bash
#SBATCH --job-name=qwen-finetune
#SBATCH --partition=train
#SBATCH --nodes=2
#SBATCH --ntasks-per-node=8
#SBATCH --gres=gpu:rtxpro6000:8
#SBATCH --time=12:00:00
#SBATCH --output=logs/%j.out
srun python -m torch.distributed.run \
--nnodes=2 --nproc-per-node=8 \
--rdzv-backend=c10d \
--rdzv-endpoint=$(scontrol show hostnames | head -1):29500 \
train.py --config configs/qwen72b.yaml
PyTorch Elastic et SLURM se combinent parfaitement : SLURM gère l'allocation, srun lance un processus par tâche, torch.distributed.run De plus, le backend de rendez-vous gère l'attribution des rangs. En cas de suppression d'un nœud, --max-restarts on torchrun plus --requeue on #SBATCH Il reprend à partir du dernier point de contrôle. Tout le monde l'utilise de cette façon.
Ce que vous obtenez gratuitement : planification des groupes, dégradation équitable des ressources, remplissage des ressources, comptabilisation (chaque seconde d’utilisation du GPU est enregistrée). sacctSLURM offre une sémantique simple : pas de CRD, pas de contrôleur, pas de graphe d'objets YAML. En revanche, il ne prend pas en charge les services de longue durée avec redémarrage automatique, l'ingress HTTP, les mises à jour progressives, les conteneurs sidecar ni la gestion d'état déclarative. SLURM est un planificateur de tâches par lots. L'utiliser comme Kubernetes serait contre-productif.
SLURM est le bon choix lorsque : Vous exécutez des entraînements, des inférences par lots ou des simulations ; les utilisateurs écrivent des scripts shell ; un ingénieur SRE à temps partiel peut l’exploiter.
Là où SLURM atteint ses limites
Trois endroits. Services de type Web — pas de concept de « fonctionnement continu, redémarrage en cas de panne, exposition du port 8000 ». Vous pouvez intégrer vLLM dans une tâche SLURM de longue durée, mais la vérification de l'état, les mises à jour progressives et l'équilibrage de charge deviennent votre problème. Charges de travail hétérogènes — SLURM suppose un seul pool de ressources ; un cluster exécutant l’entraînement + l’inférence + Jupyter + la surveillance + la préparation des données nécessite des contrôles plus précis. Isolation stricte des locataires multiples — Les cgroups isolent le CPU et la mémoire ; l’isolation du GPU repose sur CUDA_VISIBLE_DEVICES et le code utilisateur doit le respecter. Pas d'espaces de noms, pas de politique réseau par tâche, pas de gestion de conteneurs. Convient aux environnements de test ; inadapté à un service hébergé multi-clients.
Lorsque vous rencontrez l'un de ces problèmes, la solution la plus judicieuse consiste généralement à ajouter une seconde couche (Kubernetes pour les services) plutôt que de surcharger SLURM. Les offres gérées SUNK et 2026 de CoreWeave utilisent SLURM. on Kubernetes permet d'utiliser le même cluster pour les deux tâches. À l'échelle de Kentino, deux petits clusters distincts sont souvent plus simples.
Kubernetes pour l'IA : la voie native du cloud
Kubernetes a été conçu pour assurer le fonctionnement des services web sans état. Pour les clusters d'IA, tout le reste est ajouté par-dessus. La pile technologique de 2026 : Opérateur GPU NVIDIA (pilotes, CUDA, NCCL, DCGM, plugin de périphérique, configuration MIG), Kueue (file d'attente, quota, admission), Volcan (planificateur prenant en charge le traitement par lots), Formateur Kubeflow (PyTorchJob, TFJob, MPIJob CRD), KubeRay (Ray sur K8s), et Karpenter ou Cluster Autoscaler pour le provisionnement des nœuds.
Pourquoi une configuration Kubernetes naïve est inadaptée à l'IA : le planificateur kube-scheduler par défaut fonctionne selon le principe du premier entré, premier sorti (FIFO), sans gestion des groupes de tâches. Il peut démarrer 7 des 8 pods d'une tâche d'entraînement distribuée et laisser le huitième en attente, tandis que les 7 pods en cours d'exécution monopolisent les GPU. Ce n'est pas un problème théorique : c'est l'erreur la plus fréquente commise par les équipes qui tentent d'exécuter un entraînement sur un cluster conçu pour l'inférence. Les solutions sont Kueue (admission au niveau de la tâche) et Volcano (placement groupé des pods). La meilleure pratique actuelle consiste à utiliser les deux : Kueue en amont et Volcano en aval.
Volcan
Volcano est un planificateur de tâches par lots CNCF qui s'installe en parallèle ou à la place de kube-scheduler. Il ajoute une véritable planification groupée avec minAvailable sémantique (admission de zéro ou N, jamais partielle), priorités et préemption des files d'attente, stratégies de partage équitable / binpack / prenant en compte la topologie et prise en charge de premier ordre pour PyTorchJob, TFJob, MPIJob, RayJob et SparkApplication.
Une file d'attente minimale et une tâche d'entraînement PyTorch planifiée en équipe :
apiVersion: scheduling.volcano.sh/v1beta1
kind: Queue
metadata:
name: training
spec:
weight: 4
capability:
nvidia.com/gpu: 32
---
apiVersion: batch.volcano.sh/v1alpha1
kind: Job
metadata:
name: qwen-finetune
spec:
schedulerName: volcano
minAvailable: 16 # gang: all 16 ranks or zero
queue: training
policies:
- event: PodEvicted
action: RestartJob
tasks:
- replicas: 16
name: worker
template:
spec:
containers:
- name: pytorch
image: kentino/pytorch:2.5-cuda13
resources:
limits:
nvidia.com/gpu: 1
command: ["torchrun", "--nnodes=16", "--nproc-per-node=1", "train.py"]
Quand tu fais pas Volcano est nécessaire : un cluster d'inférence pur exécutant des déploiements vLLM indépendants. Chaque pod possède ses propres GPU, aucune coordination inter-pods n'est requise, et le planificateur kube par défaut convient parfaitement. Volcano révèle tout son potentiel dès l'intégration de l'entraînement distribué au sein du cluster.
Kueue et emprunts par quotas
Kueue gère une couche que Volcano ne gère pas : qui est autorisé à consommer quelle part du cluster, et que se passe-t-il lorsqu’une équipe est inactive ?
apiVersion: kueue.x-k8s.io/v1beta1
kind: ResourceFlavor
metadata:
name: rtxpro6000
spec:
nodeLabels:
nvidia.com/gpu.product: "RTX-PRO-6000-Blackwell"
---
apiVersion: kueue.x-k8s.io/v1beta1
kind: ClusterQueue
metadata:
name: team-research
spec:
cohort: "shared-gpu"
resourceGroups:
- coveredResources: ["nvidia.com/gpu"]
flavors:
- name: rtxpro6000
resources:
- name: "nvidia.com/gpu"
nominalQuota: 16
borrowingLimit: 8
Chaque équipe du groupe bénéficie de 16 GPU garantis et peut en emprunter jusqu'à 8 à l'autre. Les tâches prioritaires utilisent la capacité empruntée avant la limite. La feuille de route 2026 de Kueue met l'accent sur MultiKueue (distribution multi-clusters) et la préemption coopérative : une tâche de sauvegarde peut recevoir l'instruction « sauvegarder l'état et céder la place dans 60 secondes » au lieu d'être interrompue brutalement.
Partage de MIG et de GPU
Le plugin NVIDIA alloue par défaut l'intégralité du GPU. C'est approprié pour l'entraînement et les inférences importantes ; en revanche, c'est un gaspillage de ressources pour les inférences plus simples, les notebooks ou le développement. Trois modes de partage sont disponibles : MIG (mémoire matérielle et isolation des pannes, inférence multi-locataire en production), MPS (coopératif, sans isolement), découpage temporel (pas d'isolation, uniquement pour les développeurs).
MIG existe sur H100/H200, A100 et B200 — mais pas sur les GPU de la gamme Kentino. Les cartes graphiques RTX 5090, RTX 4090, RTX Pro 6000 Blackwell, L40 et L4 ne prennent pas en charge MIG. Si votre conception nécessite des partitions GPU isolées matériellement, votre matériel sera limité aux composants SXM/datacenter non fabriqués par Kentino. Le partage de temps (time-slicing) alloue N GPU virtuels par GPU physique, et le planificateur ignore la sursouscription ; utilisez-le uniquement en développement.
Points forts de Kubernetes : services d’inférence de longue durée (déploiements vLLM dimensionnés par HPA en fonction de la profondeur de la file d’attente, mises à jour progressives, contrôles d’intégrité – SLURM n’offre rien de comparable), charges de travail hétérogènes sur un seul cluster, écosystème (Prometheus, Grafana, Argo). Coût : une pile Kubernetes + GPU Operator + Kueue + Volcano + Kubeflow fonctionnelle comprend au minimum 20 composants. Estimation réaliste : Kubernetes 5 à 10 fois la charge opérationnelle du SLURM pour la formation par lots pure.
Kubernetes est le bon choix lorsque : Le cluster exécute des services d'inférence en parallèle de l'entraînement, vous avez une équipe plateforme, tout est déclaratif, ou vous avez besoin d'une portabilité multi-cluster.
Ray et KubeRay — Calcul distribué natif Python
Ray est un tout autre outil. Il ne s'agit pas vraiment d'un ordonnanceur de cluster au sens de SLURM/K8s, mais d'un environnement d'exécution Python distribué qui intègre un ordonnanceur, un système de stockage d'objets et un autoscaler. Vous écrivez du Python avec @ray.remote Les décorateurs et Ray déterminent où exécuter chaque tâche.
Où Ray trouve sa place : réglage hyperparamètre (Ray Tune — des centaines d'essais en parallèle, éliminant rapidement les mauvais ; le cas d'utilisation pour lequel Ray a été conçu et qui reste le meilleur de sa catégorie) apprentissage par renforcement (RLlib — environnements et apprenants en tant qu'acteurs, ce qui correspond bien à Ray et mal aux tâches par lots SLURM), formation distribuée (Ray Train, encapsulant PyTorch DDP / FSDP / DeepSpeed), modèle servant (Ray Serve, pipelines multi-modèles personnalisés natifs Python), et prétraitement des données (Ray Data). Ce que Ray n'est pas : un ordonnanceur de tâches mutualisé pour un cluster partagé. Ray suppose qu'une seule application est propriétaire du cluster.
KubeRay est l'opérateur qui exécute Ray sur Kubernetes. Trois CRD : RayCluster (longévité), RayJob (one-shot), RayService (Ray Serve, mises à jour continues). Un RayCluster minimal :
apiVersion: ray.io/v1
kind: RayCluster
metadata:
name: rl-cluster
spec:
rayVersion: '2.55.0'
headGroupSpec:
rayStartParams: { dashboard-host: '0.0.0.0' }
template:
spec:
containers:
- name: ray-head
image: rayproject/ray:2.55.0-py311-cu125
resources:
limits: { cpu: 8, memory: 32Gi }
workerGroupSpecs:
- groupName: gpu-workers
replicas: 4
minReplicas: 0
maxReplicas: 8
rayStartParams: {}
template:
spec:
containers:
- name: ray-worker
image: rayproject/ray:2.55.0-py311-cu125
resources:
limits:
cpu: 16
memory: 128Gi
nvidia.com/gpu: 4
KubeRay ajuste automatiquement la taille du groupe de nœuds de calcul entre minReplicas et maxReplicas En se basant sur la vue des tâches en attente de Ray, et en l'associant à Kueue (la planification groupée RayJob + Kueue est documentée et fonctionnelle), on obtient une architecture Ray mutualisée sur un cluster Kubernetes mutualisé — la configuration de production que la plupart des entreprises utilisant Ray déploient réellement.
Quand une architecture Ray autonome (sans Kubernetes) convient : dans un petit laboratoire de recherche où une seule personne gère le cluster, où le dynamisme prime sur la mutualisation et où la charge de travail est principalement axée sur l’apprentissage par renforcement ou la recherche d’hyperparamètres. L’architecture hybride sur laquelle convergent la plupart des équipes : SLURM ou K8s comme couche de base qui gère les nœuds et la mutualisation ; Ray est lancé dans une tâche SLURM ou un espace de noms K8s pour la durée de la charge de travail d'un utilisateur. N'installez pas Ray comme planificateur de tâches principal.
Run:ai et Determined — le niveau commercial
Deux solutions payantes sont régulièrement évoquées et méritent d'être abordées. Elles ciblent toutes deux le même problème : l'utilisation de Kubernetes, Volcano, Kueue et GPU Operator implique une quantité importante de YAML, et certaines organisations préfèrent acheter plutôt que de développer elles-mêmes.
NVIDIA Run:ai (acquis en 2024, rebaptisé en 2025) est un planificateur GPU natif de K8s. Fractionnement GPU répartit un seul GPU entre les charges de travail au niveau de la mémoire — 0.5 GPU Requêtes, redimensionnement dynamique, optimisation du placement de ressources : Run:ai justifie son prix dans les environnements d'entreprise où plus de 10 équipes de ML se partagent les GPU. En dessous de ce niveau, Kueue + GPU Operator offre gratuitement des fonctionnalités similaires.
Déterminé.IA (HPE, 2021) est une plateforme de formation gérée qui intègre le suivi des expériences, la recherche d'hyperparamètres, la gestion des points de contrôle et la formation distribuée dans un seul produit. Elle est idéale pour les équipes de recherche qui souhaitent un tableau de bord performant sans avoir à intégrer elles-mêmes cinq outils.
Le problème du cahier interactif
Un problème récurrent dans presque tous les clusters GPU partagés : les chercheurs souhaitent accéder aux GPU via JupyterHub pour le développement, ce qui doit coexister avec des tâches d’entraînement de longue durée occupant des nœuds entiers. La solution simpliste – un GPU dédié par notebook – gaspille 80 % du cluster. La solution rigoureuse : rendre les chercheurs autonomes. sbatch Tout — les transfère sur des ordinateurs portables avec des maquettes.
Trois modèles viables : SLURM salloc + Jupyter sur l'allocation (salloc --gres=gpu:1 --time=4:00:00, démarrer un serveur Jupyter sur le nœud alloué, tunneliser ; limite de temps stricte, GPU correctement pris en compte — solution la plus propre pour un cluster SLURM), Kubernetes + JupyterHub + Kueue (les modules d'ordinateurs portables passent par une file d'attente à faible priorité, les tâches d'entraînement préemptent les ordinateurs portables inactifs), ou Exécuter:ai avec fractionnement GPU (Les ordinateurs portables bénéficient chacun de 0.25/0.5 GPU). Quel que soit votre choix, fixer une limite de temps pour les sessions interactives — Un ordinateur portable sans délai d'expiration correspond à un GPU définitivement retiré du cluster.
La réalité honnête des petites équipes
La plupart des ressources en ligne sur les planificateurs supposent un cluster de 256 GPU et une équipe dédiée. En réalité, le client Kentino dispose plutôt de 1 à 4 nœuds, de 4 à 32 GPU au total, et de 2 à 6 utilisateurs qui se connaissent et communiquent via Slack. Dans cette configuration, Vous n'avez pas besoin de planificateur.
# user 1
ssh node01
tmux new -s my-training
CUDA_VISIBLE_DEVICES=0,1,2,3 python train.py
# Ctrl-B D to detach
# user 2 — coordinates on Slack first
ssh node01
nvidia-smi # which GPUs are free?
tmux new -s other-training
CUDA_VISIBLE_DEVICES=4,5,6,7 python train.py
Voilà l'intégralité du système d'ordonnancement des tâches. SLURM devient rentable dès 16 GPU ou 8 utilisateurs, selon la première éventualité. En dessous de ce seuil, les coûts d'exploitation dépassent les avantages. Installez un planificateur lorsque la coordination humaine fait défaut, pas avant.
Quand chaque outil trouve sa place — le résumé
| Scénario | Recommandation |
|---|---|
| 1 à 2 nœuds, 4 à 16 GPU, 2 à 6 utilisateurs communiquant entre eux | SSH + tmux + nvidia-smi. Pas de planificateur. |
| Laboratoire de recherche, 4 à 32 nœuds, tâches de formation par lots | SLURM. Ennuyeux, éprouvé, adapté. |
| Plateforme d'inférence au service du trafic client | Kubernetes + opérateur GPU. Pas de planification par lots. |
| Cluster mixte : entraînement + inférence + outils | Kubernetes + Kueue + Volcano + Kubeflow. |
| Python distribué lourd : renforcement par apprentissage, recherche d'hyperparamètres | Ray (ou KubeRay) au-dessus de SLURM ou K8s. |
| Plus de 10 équipes d'apprentissage automatique se disputent les GPU et le budget pour l'outillage. | Exécuter:ai sur Kubernetes. |
| Expériences d'entraînement gérées + suivi | IA déterminée. |
| Plus de 200 GPU, multi-équipes, multi-charges de travail | Fédéré : SLURM pour le traitement par lots, K8s pour les services. |
Que faire ensuite ? — l’arbre de décision
Suivez ces étapes dans l'ordre. Le premier « oui » met fin à la conversation.
- Moins de 16 GPU et moins de 8 utilisateurs qui communiquent entre eux ? Planificateur : oui → non. Documentez les conventions dans un fichier Markdown. Reprenez-les en cas de rupture de coordination.
- Cluster exécutant des services d'inférence de longue durée ainsi que des services d'entraînement ? Oui → Kubernetes est la base. Ajoutez GPU Operator, puis Kueue, puis Volcano si l'entraînement distribué est prévu. Non → SLURM est la base.
- Disposez-vous d'une équipe plateforme ou d'un budget pour en créer une (0.5 à 1.0 ETP pendant six mois, 0.25 ETP en régime permanent) ? Non → restez sur SLURM quelle que soit la répartition de la charge de travail. Le surcoût lié à Kubernetes est bien réel.
- >10 équipes en compétition pour du temps GPU avec des quotas stricts ? Oui → évaluer Run:ai. Non → Kueue + cohortes vous permettent d'atteindre 80 % du résultat.
-
Charge de travail importante basée sur le renforcement par apprentissage, la recherche d'hyperparamètres ou Python distribué ? Oui → Ray (KubeRay sur K8s, ou
salloc+ Ray sur SLURM). Non → omettre Ray. - Matériel compatible MIG (cartes de centre de données) ? Non à la gamme Kentino → prévoir une allocation GPU complète ou un partage de temps uniquement en développement. Ne pas concevoir autour de MIG.
La plupart des conversations Kentino s'arrêtent à l'étape 1, 2 ou 3. Le reste n'est que décoration.
Articles associés : formation distribuée en K02, groupes d'inférence dans K03, stockage en cluster dans K04, gestion des défaillances dans K06, le plafond de bande passante PCIe dans K07.
Ceci fait partie du Kentino Wiki, une série de référence sur l'intelligence artificielle, la robotique et les systèmes qui les connectent. Commentaires et corrections bienvenus. info@kentino.com.