Add end-to-end GPU labs (Kueue + cluster autoscaler) to the Ray on AKS example - #5938
Li Chen (AaronLiChen) wants to merge 4 commits into
Conversation
Adds a plain Kubernetes batch Job under kueue-and-ray-on-aks that demonstrates the ProvisioningRequest -> AKS cluster autoscaler path: Kueue gates admission on a ProvisioningRequest AdmissionCheck, the autoscaler atomically grows an autoscaling CPU pool, and only then is the Job unsuspended. - 2-kueue-queues/manifests/40-autoscale-queue.yaml: self-contained cas-kueue-demo namespace, ResourceFlavor, ProvisioningRequestConfig, AdmissionCheck, cas-cluster-queue and cas-local-queue (all v1beta2) - 3-workloads/cas-batch-job/: job manifest and README - Uses a terminating busybox container so the Job reaches Complete 3/3 - Verified end to end on an AKS cluster in centraluseuap
The cas-batch-job example demonstrates the ProvisioningRequest gate on CPU, where atomic scale-up is a nicety. These labs apply it to GPUs, where it is what keeps an expensive cluster from deadlocking: a distributed job given 6 of 8 GPUs runs at 0%, not 75%, while holding capacity nobody else can use. Three labs covering the path a customer actually walks -- pool provisioning with scale-to-zero, hardware verification, distributed training, queue contention, and the DCGM/Kueue/CAS metrics to check afterwards: 1. GPU verification driver, CUDA compute, NCCL interconnect 2. Multi-node training PyTorch DDP with gang scheduling 3. Queue contention admission ordering when demand exceeds quota Lab 1's NCCL check is the one that earns its keep: a node can pass nvidia-smi with NVLink degraded to PCIe, which silently turns multi-GPU training into a crawl. Bus bandwidth is how you catch that in a minute rather than three days into a run. Sample outputs are from real runs on ND96amsr_A100_v4 and a live Kueue v0.18.2 install, not illustrative. The README records what was validated and the two paths that were not.
Ran Lab 2 on two ND96amsr_A100_v4 nodes. The Job completes 2/2 and the DNS
wait loop works as intended, but per-GPU throughput drops ~110x versus the
same 8 ranks on one node.
That is not a defect in the lab, it is the most useful thing the lab teaches.
NCCL logs "NET/IB : No device found" and falls back to TCP over eth0, so the
gradient all-reduce crosses the pod network instead of NVLink. Documented the
check ("Using network Socket") and why it matters, since this is a common and
expensive cause of poor multi-node scaling that people misattribute to their
model.
Replaced the estimated cold-start table with orders of magnitude -- the real
figures vary too much by region and registry to state precisely -- and added
troubleshooting entries for the two failures hit while testing: a partially
occupied GPU node leaving one rank Pending, and AllocationFailed when the
region had no A100 capacity.
There was a problem hiding this comment.
🟡 Changes recommended
There are documentation inconsistencies (e.g., incorrect configuration count and unclear “choose one” guidance, plus a manifest-vs-output mismatch) that should be corrected before merging.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Pull request overview
Adds an end-to-end “gpu-lab” walkthrough to the existing Kueue + Ray on AKS example, extending the autoscaler-driven ProvisioningRequest pattern from CPU to GPU workloads.
Changes:
- Adds CPU and GPU autoscale queue configurations (ProvisioningRequest AdmissionCheck + ClusterQueue/LocalQueue wiring).
- Adds two plain Kubernetes Job demos in Module 3: a CPU
cas-batch-joband a three-partgpu-lab(verify hardware, multi-node training, queue contention). - Updates the module READMEs to document the new queues and workloads.
File summaries
| File | Description |
|---|---|
| examples/kueue-and-ray-on-aks/README.md | Updates the top-level module summary to include autoscale queues and the new workloads. |
| examples/kueue-and-ray-on-aks/2-kueue-queues/README.md | Documents the new autoscale (CPU/GPU) queue options and how they relate to the existing demos. |
| examples/kueue-and-ray-on-aks/2-kueue-queues/manifests/40-autoscale-queue.yaml | Introduces a CPU autoscale queue using ProvisioningRequest to drive cluster autoscaler scale-up before admission. |
| examples/kueue-and-ray-on-aks/2-kueue-queues/manifests/50-gpu-autoscale-queue.yaml | Adds the GPU variant (GPU-managedResources + GPU pool taint toleration) for atomic GPU provisioning. |
| examples/kueue-and-ray-on-aks/3-workloads/README.md | Adds documentation and comparison table entries for the new cas-batch-job and gpu-lab workloads. |
| examples/kueue-and-ray-on-aks/3-workloads/cas-batch-job/README.md | Adds an end-to-end CPU-only ProvisioningRequest→CAS demo description and runbook. |
| examples/kueue-and-ray-on-aks/3-workloads/cas-batch-job/manifests/job.yaml | Adds the suspended Job manifest that triggers CAS scale-up via Kueue admission. |
| examples/kueue-and-ray-on-aks/3-workloads/gpu-lab/README.md | Adds the GPU lab guide (provisioning, verification, distributed training, contention, and metrics). |
| examples/kueue-and-ray-on-aks/3-workloads/gpu-lab/manifests/10-gpu-verify.yaml | Lab 1 Job manifest to validate GPU visibility, CUDA compute, and NCCL all-reduce performance. |
| examples/kueue-and-ray-on-aks/3-workloads/gpu-lab/manifests/20-multinode-training.yaml | Lab 2 indexed Job + headless Service manifest to demonstrate multi-node torchrun DDP with gang semantics. |
| examples/kueue-and-ray-on-aks/3-workloads/gpu-lab/manifests/30-queue-contention.yaml | Lab 3 manifests to demonstrate quota-based queuing/serialization under GPU contention. |
Review details
Suppressed comments (1)
examples/kueue-and-ray-on-aks/2-kueue-queues/README.md:89
- The "Choose one" note only mentions 20-/30- manifests, but the Apply section now also includes the 40- and 50- autoscale demos. Update this note so it matches the updated guidance about applying just one configuration (or clarify which combinations are supported).
> **⚠️ Choose one.** `20-single-queue.yaml` and `30-team-queues.yaml` are
> independent configurations. To switch between them, delete the active one
> first:
> ```bash
> kubectl delete -f manifests/20-single-queue.yaml # then apply 30-team-queues.yaml
- Files reviewed: 11/11 changed files
- Comments generated: 2
- Review effort level: Lite
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| Three queue configurations are provided as **independent demos** — apply one, | ||
| not several: |
| Expected tail. Measured on two `ND96amsr_A100_v4` nodes, 4 GPUs each: | ||
|
|
Wilson asked for ways to capture logs and events for Workload conditions, AdmissionCheck status, ProvisioningRequest events, Kueue controller logs, and the cluster autoscaler. Adds a section ordered along the admission path, so the first stage that looks wrong is the one to fix. Notes that on AKS the cluster autoscaler runs in the managed control plane, so its logs come from the cluster-autoscaler diagnostic setting category rather than kubectl logs, and that the setting must be enabled before reproducing a scale-up problem.
There was a problem hiding this comment.
🔵 Needs a closer look
There are a few concrete correctness/docs issues in the new lab materials (notably a hard-coded NCCL GPU count and queue option count/mutual-exclusion wording) that should be fixed before merging.
Review details
Suppressed comments (3)
Previously missed (2) — in code that hasn't changed since the last review.
examples/kueue-and-ray-on-aks/2-kueue-queues/README.md:89
- The "Choose one" warning only mentions
20-single-queue.yamland30-team-queues.yaml, but this PR adds two more mutually exclusive queue configs (40-autoscale-queue.yamland50-gpu-autoscale-queue.yaml). The warning should list all options so readers don't accidentally apply multiple demos.
> **⚠️ Choose one.** `20-single-queue.yaml` and `30-team-queues.yaml` are
> independent configurations. To switch between them, delete the active one
> first:
> ```bash
> kubectl delete -f manifests/20-single-queue.yaml # then apply 30-team-queues.yaml
examples/kueue-and-ray-on-aks/3-workloads/gpu-lab/manifests/10-gpu-verify.yaml:89
all_reduce_perfis hard-coded to-g 8, but the README explicitly suggests adapting these labs to other GPU SKUs (for example, 2-GPU H100 nodes). If someone updates the Job'snvidia.com/gpulimit but forgets to also change-g, the NCCL test will run with the wrong GPU count and can fail or hang.
examples/kueue-and-ray-on-aks/2-kueue-queues/README.md:11
- The intro says "Three queue configurations" but the table lists four (Single, Team, Autoscale, GPU autoscale). This mismatch is confusing for readers trying to choose the right manifest.
Three queue configurations are provided as **independent demos** — apply one,
not several:
- Files reviewed: 11/11 changed files
- Comments generated: 0 new
- Review effort level: Lite
Summary
Adds a
gpu-labmodule toexamples/kueue-and-ray-on-aks/— a three-part lab sequence that takes a customer from GPU pool provisioning through hardware verification, multi-node distributed training, and queue contention, plus the metrics to check afterwards.This extends #5867 (CPU-only ProvisioningRequest example) to GPUs, where atomic provisioning stops being a convenience and becomes what keeps an expensive cluster from deadlocking.
New/changed:
2-kueue-queues/manifests/50-gpu-autoscale-queue.yaml— GPU ResourceFlavor (with thenvidia.com/gpu=present:NoScheduletoleration AKS puts on GPU pools), ProvisioningRequestConfig withmanagedResources: [nvidia.com/gpu], AdmissionCheck, ClusterQueue, LocalQueue3-workloads/gpu-lab/— README + three lab manifests (hardware verify, multi-node torchrun DDP, queue contention)Validated on real hardware
Run against A100 80GB pools; measured output is in the README rather than illustrative numbers.
all_reduce_perfall pass —Avg bus bandwidth : 224.991 GB/s(NVLink-class)50 steps in 0.7s,17568.3 samples/s19.8 samples/sper GPU — ~110× slower. Cause:NCCL INFO NET/IB : No device found→Using network Socket, i.e. TCP over eth0 instead of RDMA. The README makes this the central teaching point: reading the NCCL transport line is how you tell a working multi-node job from a working-but-pointless one.insufficient unused quota for nvidia.com/gpu in flavor gpu-a100, 2 more neededCaveats, documented in the README
AllocationFailed(pinned availability set out of capacity). Kept in the README as a real illustration of why GPU capacity is worth gating.Test plan