Jobplanlægning for AI-klynger: SLURM, Kubernetes, Ray og vide, hvornår du ikke har brug for nogen af dem
En scheduler bestemmer, hvilket job der kører på hvilke GPU'er, hvornår og på hvis bekostning. Uden en sådan scheduler degenererer en delt klynge til, at folk pinger hinanden på Slack for at spørge, om nogen bruger node 3. Med den forkerte scheduler bruger du flere ingeniørtimer på YAML end på modelarbejde.
Denne artikel sammenligner de schedulere, folk rent faktisk vælger — SLURM, Kubernetes (med Kueue eller Volcano), Ray og KubeRay, og de kommercielle muligheder Run:ai og Determined — og er ærlig omkring tilfældet, hvor det rigtige svar er "intet, bare SSH". Publikum har læst K01, K02og K03 og beslutter nu, hvad der skal lægges oven på en klynge med 1-32 noder.
Hvad en planlægger rent faktisk gør
Tre job, nogenlunde i sværhedsgrad: placere arbejde på ledige ressourcer, sætte arbejde i kø, der endnu ikke passer, og koordinere job med flere noder — distribueret træning kræver, at alle N rækker starter på én gang (bandeplanlægning), fordi en PyTorch dist.barrier() blokkerer på ubestemt tid, hvis én rang aldrig dukker op (se K06).
Alt ud over en klynge med et enkelt team kræver også kvoter for flere lejere med låntagning, fair share-henfald på tværs af teams, præemption og korrekt GPU-regnskabsføring (så jobbet ser CUDA_VISIBLE_DEVICES (indstillet korrekt). Hver planlægger nedenfor udfører de første tre. Hvor de afviger er på den anden liste, og på hvilken slags arbejde de antager, du kører.
Den store opdeling: batchjob vs. langvarige tjenester
Dette er den vigtigste enkeltindramning. Batchjob træne i 8 timer og afslutte, eller køre en hyperparam-sweep på 200 korte job, eller behandle et datasæt natten over - start, slut, resultat, intet svarer på HTTP. Langvarige tjenester er et vLLM-slutpunkt, der betjener inferens døgnet rundt, en scene-hukommelsesdatabase, en overvågningsstak - der skal forblive oppe for evigt og genstarte ved nedbrud.
SLURM blev bygget til batch. Kubernetes blev bygget til langvarige tjenester. Alt andet er en af de to, der strækkes for at klare den anden halvdel.
| arbejdsbyrde | SLURM | Kubernetes |
|---|---|---|
| 8-timers træningsløb | Native | Kræver Kueue eller vulkan |
| 200-job hyperparameter scanning | Native | Akavet |
| 24/7 vLLM-inferensslutpunkt | Akavet | Native |
| Blandet: træning + inferens + værktøjer | Smertefuld | Indfødt (med Kueue) |
| Distribueret træning med flere noder | Indfødt (bande) | Kræver bande-plugin |
| Automatisk genstart af nedbrudte tjenester | Ingen | Native |
Hvis din klynge kun gør én af disse ting, er valget nemt. Hvis den gør begge dele – hvilket bliver mere og mere normalt – er spørgsmålet, hvilken scheduler du er villig til at betale driftsafgiften på.
SLURM — HPC-standarden, der stadig vinder ved ren træning
SLURM kører mere end 65% af TOP500-supercomputerne og de fleste publicerede frontier-træningskørsler. For arbejdsbelastninger, der ligner "indsend et træningsjob, vent på resultater, få kontrolpunktet", har det været det rigtige svar i tyve år.
Den mentale model er enkel: a partition er en navngiven pulje af noder (gpu-5090, cpu-only, bigmem); -en arbejde er et shell-script med #SBATCH direktiver, indsendt med sbatch; STENtøj (Generic RESources) sporer GPU'er — --gres=gpu:rtxpro6000:2 beder om to specifikke kort; bandeplanlægning er indbygget.
En minimal slurm.conf for en 4-node klynge med 8× RTX Pro 6000 Blackwell pr. node:
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
Matchningen gres.conf på hver node:
AutoDetect=nvml
NodeName=node[01-04] Name=gpu Type=rtxpro6000 File=/dev/nvidia[0-7]
AutoDetect=nvml forespørger direkte i NVIDIA Management Library, henter automatisk MIG-skiver og gemmer dig manuelle filstier for enheden.
En bruger indsender:
#!/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 og SLURM sammensætter rent: SLURM ejer allokeringen, srun starter én proces pr. opgave, torch.distributed.run plus rendezvous-backend'en håndterer rangtildeling. Ved et node-drop, --max-restarts on torchrun plus --requeue on #SBATCH kommer sig efter det sidste checkpoint. Alle løber den på denne måde.
Hvad du får gratis: gruppeplanlægning, fair-share-henfald, backfill, regnskab (hvert GPU-sekund logget til sacct), og simpel semantik — ingen CRD'er, ingen controller, ingen YAML-objektgraf. Hvad du ikke får: langvarige tjenester med automatisk genstart, HTTP-ingress, rullende opdateringer, sidecar-containere, deklarativ tilstand. SLURM er en batch-scheduler. At behandle det som Kubernetes vil skade.
SLURM har ret, når: du kører træning, batch-inferens eller simulering; brugere skriver shell-scripts; én deltids SRE kan betjene det.
Hvor SLURM rammer sit loft
Tre steder. Web-lignende tjenester — intet koncept om "kør for evigt, genstart ved død, eksponér port 8000." Du kan presse vLLM ind i et langvarigt SLURM-job, men sundhedstjek, løbende opdateringer og load balancing bliver dit problem. Heterogene arbejdsbyrder — SLURM antager én ressourcepulje; en klynge, der kører træning + inferens + Jupyter + overvågning + dataforberedelse, ønsker mere detaljerede kontroller. Streng isolation af flere lejere — cgroups isolerer CPU og hukommelse; GPU-isolering er afhængig af CUDA_VISIBLE_DEVICES og brugerens kode, der respekterer den. Ingen navnerum, ingen netværkspolitik pr. job, ingen container-grade tenancy. Fint for laboratorier; ikke fint for en hosted multi-customer service.
Når du rammer en af disse, er det rigtige træk normalt at tilføje et andet lag - Kubernetes til tjenesterne - i stedet for at plage SLURM. CoreWeaves SUNK og 2026 managed-tilbud kører SLURM. on Kubernetes, så den samme klynge gør begge dele. På Kentino-skala er to separate små klynger ofte enklere.
Kubernetes til AI: den cloud-native vej
Kubernetes blev bygget til at holde statsløse webtjenester kørende. For AI-klynger bliver "alt andet" boltet på. 2026-stakken: NVIDIA GPU-operatør (drivere, CUDA, NCCL, DCGM, enheds-plugin, MIG-konfiguration), Kueue (kø, kvote, adgang) Volcano (batch-bevidst planlægger), Kubeflow-træner (PyTorchJob, TFJob, MPIJob CRD'er), KubeRay (Ray på K8'ere), og Tømrer eller Cluster Autoscaler til nodeklarering.
Hvorfor naiv Kubernetes er forkert til AI: Standard kube-scheduleren er FIFO uden gangsemantik. Den starter gerne 7 ud af 8 pods af et distribueret træningsjob og lader den ottende være i afventning, mens de kørende 7 holder GPU'er inaktive. Dette er ikke teoretisk - det er den mest almindelige fejl, teams begår, når de forsøger at køre træning på en klynge, de har bygget til inferens. Rettelserne er Kueue (adgang på jobniveau) og Volcano (gangplacering på pod-niveau). Nuværende bedste praksis er begge: Kueue øverst, Volcano nedenunder.
Volcano
Volcano er en CNCF batch scheduler, der installeres sammen med eller i stedet for kube-scheduler. Den tilføjer ægte gruppeplanlægning med minAvailable semantik (tillad nul eller N, aldrig delvis), køprioriteter og præemption, fair-share / binpack / topologi-bevidste strategier og førsteklasses understøttelse af PyTorchJob, TFJob, MPIJob, RayJob og SparkApplication.
En minimal kø plus et gruppeplanlagt PyTorch-træningsjob:
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"]
Når du gør det ikke Behov for Volcano: en ren inferensklynge, der kører uafhængige vLLM-implementeringer. Hver pod ejer sine egne GPU'er, ingen koordinering på tværs af pods, standard kube-scheduler er fin. Volcano fortjener sin plads i det øjeblik, du blander distribueret træning ind i klyngen.
Kueue og kvoteoplåning
Kueue håndterer et lag, som Volcano ikke gør: hvem må forbruge hvor meget af klyngen, og hvad sker der, når ét hold er inaktivt.
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
Begge hold i kohorten får 16 GPU'er garanteret og kan låne op til 8 fra den andens inaktive pool. Højprioriteret arbejde foregriber lånt kapacitet. Kueues 2026-køreplan fokuserer på MultiKueue (multi-cluster dispatch) og kooperativ foregribelse — et checkpointing-job kan få besked på "gem tilstand og udbytte på 60 sekunder" i stedet for at blive afbrudt direkte.
MIG- og GPU-deling
NVIDIA-enhedsplugin'et bruger som standard allokering af hele GPU'en. Til træning og store inferenser er det korrekt; til små inferenser, bærbare computere eller udviklingsarbejde er det spild af data. Tre delingstilstande: MIG (hardwarehukommelse og fejlisolering, produktionsmulti-tenant inferens), MPS (kooperativ, ingen isolation) tidsopdeling (ingen isolation, kun udvikling).
MIG findes på H100/H200, A100 og B200 — ikke på nogen GPU i Kentino-serien. RTX 5090, RTX 4090, RTX Pro 6000 Blackwell, L40, L4 understøtter ikke MIG. Hvis dit design kræver hardwareisolerede GPU-partitioner, begrænser det din hardware til SXM/datacenterdele, som Kentino ikke bygger. Time-slicing annoncerer N virtuelle GPU'er pr. fysisk GPU, og scheduleren har ingen anelse om, at de er overtegnet – brug det kun til udviklere.
Hvad Kubernetes er god til: langvarige inferenstjenester (vLLM-implementeringer skaleret af HPA på kødybde, rullende opdateringer, sundhedstjek - SLURM har intet sammenligneligt), heterogene arbejdsbelastninger på én klynge, økosystem (Prometheus, Grafana, Argo). Hvad du betaler: en fungerende K8s + GPU-operatør + Kueue + Volcano + Kubeflow-stak er mindst 20 komponenter. Realistisk estimat: Kubernetes er 5–10 gange den operationelle belastning af SLURM til ren batchtræning.
Kubernetes er det rigtige valg, når: klyngen kører inferenstjenester sideløbende med træning, du har et platformteam, deklarativ "alting matters", eller du har brug for portabilitet mellem flere klynger.
Ray og KubeRay — Python-native distribueret beregning
Ray er noget helt andet. Det er ikke rigtig en klyngeplanlægger i SLURM/K8s forstand — det er en distribueret Python-runtime, der leveres med en planlægger, et objektlager og en autoskalering. Du skriver Python med @ray.remote dekoratører, og Ray finder ud af, hvor hver opgave skal udføres.
Hvor Ray passer ind: justering af hyperparameter (Ray Tune — hundredvis af parallelle forsøg, hvor dårlige forsøg fjernes tidligt; det anvendelsesscenarie, Ray blev bygget til, og stadig det bedste i sin klasse), forstærkning læring (RLlib — miljøer og elever som aktører, hvilket knytter sig tydeligt til Ray og dårligt til SLURM-batchjobs), distribueret træning (Ray Train, indpakning af PyTorch DDP / FSDP / DeepSpeed), model servering (Ray Serve, Python-native, brugerdefinerede multimodel-pipelines), og data forbehandling (Ray Data). Hvad Ray ikke er: en batchplanlægger med flere lejere til en delt klynge. Ray antager, at én applikation ejer klyngen.
KubeRay er den operatør, der kører Ray på Kubernetes. Tre CRD'er: RayCluster (langlivet), RayJob (engangsslag), RayService (Ray Serve, løbende opdateringer). En minimal RayCluster:
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 autoskalerer arbejdergruppen mellem minReplicas og maxReplicas baseret på Rays visning af ventende opgaver. Kombineret med Kueue (RayJob + Kueue-gangplanlægning er dokumenteret og fungerer) får du Ray med flere lejere på en K8s-klynge med flere lejere — den produktionsopsætning, som de fleste "Ray-værksteder" rent faktisk kører.
Når Ray standalone (uden K8'er) har ret: et lille forskningslaboratorium, hvor én person ejer klyngen, er dynamik vigtigere end multi-tenancy, og arbejdsbyrden er tung på RL eller hyperparametersøgning. Den hybrid, som de fleste teams samles om: SLURM eller K8s som basislaget, der ejer noder og multi-tenancy; Ray startede inde i et SLURM-job eller et K8s-navneområde i løbet af én brugers arbejdsbyrde. Installer ikke Ray som din topniveau-planlægger.
Run:ai og Determined — det kommercielle niveau
To betalte tilbud dukker op ofte nok til at løse dem. Begge er rettet mod det samme smertepunkt: K8s + Volcano + Kueue + GPU Operator er en masse YAML, og nogle organisationer vil hellere købe end bygge.
NVIDIA Run:ai (erhvervet 2024, omdøbt 2025) er en K8s-native GPU-scheduler. GPU-fraktionering opdeler en enkelt GPU mellem arbejdsbelastninger på hukommelsesniveau — 0.5 GPU anmodninger, dynamisk størrelsesændring, bin-packing. Run:ai tjener sin pris i virksomhedsmiljøer med 10+ ML-teams, der konkurrerer om delte GPU'er. Nedenfor dækker Kueue + GPU Operator det meste af det samme område gratis.
Determined.AI (HPE, 2021) er en administreret træningsplatform — eksperimentsporing, hyperparametersøgning, checkpointstyring, distribueret træning i ét produkt. Bedst egnet til forskerteams, der ønsker et poleret dashboard uden selv at integrere fem værktøjer.
Problemet med den interaktive notesbog
Et mønster, der rammer næsten alle delte GPU-klynger: Forskere ønsker JupyterHub-adgang til GPU'er til udvikling, og det skal sameksistere med langvarige træningsjob, der indeholder hele noder. Det naive svar - én dedikeret GPU pr. bærbar - spilder 80% af klyngen. Det strenge svar - får forskere til at sbatch alt — driver dem over på bærbare computere med legetøjsmodeller.
Tre brugbare mønstre: SLURM salloc + Jupyter på allokeringen (salloc --gres=gpu:1 --time=4:00:00, start en Jupyter-server på den allokerede node, tunnel ind; hård tidsgrænse, GPU korrekt medregnet — reneste svar for en SLURM-klynge), Kubernetes + JupyterHub + Kueue (notebook-pods gennem en kø med lav prioritet, træningsjob forudser inaktive notesbøger), eller Run:ai med GPU-fraktionering (bærbare computere får 0.25 / 0.5 GPU hver). Uanset hvad du vælger, Sæt en tidsgrænse for interaktive sessioner — en bærbar computer uden timeout er en GPU, der permanent er trukket tilbage fra klyngen.
Den ærlige lille holds virkelighed
Det meste online planlægningsindhold forudsætter en 256-GPU-klynge og et platformteam. Den realistiske Kentino-kunde er tættere på 1-4 noder, 4-32 GPU'er i alt, 2-6 brugere, der alle kender hinanden og Slack-koordinerer. For den opsætning, Du behøver ikke en planlægger.
# 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
Det er hele jobplanlægningssystemet. SLURM starter med at tjene sig selv hjem omkring 16 GPU'er eller 8 brugere, alt efter hvad der kommer først. Derunder overstiger driftsomkostningerne værdien. Installer en planlægger, når den menneskelige koordinering svigter, ikke før.
Når hvert værktøj passer — resuméet
| Scenario | Anbefaling |
|---|---|
| 1-2 noder, 4-16 GPU'er, 2-6 brugere, der taler med hinanden | SSH + tmux + nvidia-smi. Ingen scheduler. |
| Forskningslaboratorium, 4-32 noder, batchtræningsjob | SLURM. Kedeligt, velprøvet, passer. |
| Inferensplatform, der betjener kundetrafik | Kubernetes + GPU-operatør. Ingen batchplanlægger. |
| Blandet klynge: træning + inferens + værktøjer | Kubernetes + Kueue + Vulkan + Kubeflow. |
| Tungt distribueret Python: RL, hyperparam-søgning | Ray (eller KubeRay) oven på SLURM eller K8s. |
| 10+ ML-teams konkurrerer om GPU'er og budget til værktøjer | Kør:ai på Kubernetes. |
| Administrerede træningseksperimenter + sporing | Determined.AI. |
| 200+ GPU'er, multiteam, multiworkload | Federeret: SLURM for batch, K8'er for tjenester. |
Hvad skal man gøre nu – beslutningstræet
Gå dette i rækkefølge. Det første "ja" afslutter samtalen.
- Færre end 16 GPU'er og færre end 8 brugere, der taler med hinanden? Ja → ingen planlægningsfunktion. Dokumentér konventioner i en markdown-fil. Genbesøg når koordineringen bryder.
- Kører klynger langvarige inferenstjenester samt træning? Ja → Kubernetes er basen. Tilføj GPU Operator, derefter Kueue, og derefter Volcano, hvis distribueret træning er en del af omfanget. Nej → SLURM er basen.
- Har du et platformteam eller et budget til et (0.5-1.0 FTE i seks måneder, 0.25 FTE i steady-state)? Nej → bliv på SLURM uanset arbejdsbyrdesammensætning. K8s-skatten er reel.
- >10 hold, der konkurrerer om GPU-tid med strenge kvotekrav? Ja → evaluer Run:ai. Nej → Kueue + kohorter når 80% af vejen.
-
Stor arbejdsbyrde på RL, hyperparametersøgning eller distribueret Python? Ja → Ray (KubeRay på K8'ere, eller
salloc+ Ray på SLURM). Nej → udelad Ray. - MIG-kompatibel hardware (datacenterkort)? Nej på Kentino-opsætningen → planlæg for fuld GPU-allokering eller tidsopdeling kun i udviklingen. Design ikke omkring MIG.
De fleste Kentino-samtaler slutter ved trin 1, 2 eller 3. Resten er pynt.
Ledsagende artikler: distribueret træning i K02, inferensklynger i K03, klyngelagring i K04, fejlhåndtering i K06, PCIe-båndbreddeloftet i K07.
Dette er en del af Kentino Wiki, en referenceserie om AI-beregning, robotteknologi og de systemer, der forbinder dem. Kommentarer og rettelser er velkomne på info@kentino.com.