GPU Slicing with Dynamic Resource Allocation (DRA)
The classic device-plugin model hands out whole GPUs: a Pod asks for nvidia.com/gpu: 1 and gets an entire card, even for a LoRA fine-tune that never touches more than a few GiB. On a 24 GB A30 that is up to 20× the memory the job needs, sitting idle.
Dynamic Resource Allocation (DRA) — a resource.k8s.io/v1 API that reached GA in Kubernetes 1.34 — lets a Pod request a slice of a GPU instead. With the NVIDIA DRA driver that slice can be:
- a MIG partition — a hardware-isolated fraction of the card (its own SMs and VRAM, enforced by the GPU), or
- a time-sliced / MPS-shared full GPU — several Pods packed onto one card with no memory isolation.
This guide requests a MIG slice through a ResourceClaimTemplate, then runs a supervised fine-tune inside it with Kubeflow Trainer v2. Every asset is under assets/dra/, and the flow is exercised end-to-end by the c15_dra_gpu_slice.sh case in the repo's e2e harness.
TOC
Device plugin vs. DRAPrerequisitesThe three objects in a slice requestStep 1 — Confirm the driver is advertising slicesStep 2 — Smoke-test a sliceStep 3 — Fine-tune inside the slice with Kubeflow Trainer v2Sizing the slice / choosing a profileNo MIG? Share a full GPU by time-slicingCombine slices with Kueue quotaWiring the same claim into other orchestratorsTurn this into a real fine-tuneEnabling the DRA driver (admin)TroubleshootingDevice plugin vs. DRA
DRA doesn't replace your scheduler or Kueue — it changes how the device is described and claimed. Quota, priority, and preemption still work (see Combine slices with Kueue quota).
Prerequisites
DeviceClass and ResourceSlice are cluster-scoped and owned by the driver/admin. ResourceClaimTemplate and ResourceClaim are namespaced and yours to create. A ResourceClaimTemplate must live in the same namespace as the Pods that reference it.
The three objects in a slice request
DeviceClass(created by the driver) names a kind of device and a base selector. This cluster's NVIDIA driver shipsgpu.nvidia.com(full GPUs) andmig.nvidia.com(MIG slices).ResourceClaimTemplate(you) says "give each Pod one device from this class, matching this CEL selector". The scheduler stamps out a per-PodResourceClaimfrom it.- The Pod references the template in
spec.resourceClaims, and the container opts in withresources.claims. Nonvidia.com/*limit is used at all.
Step 1 — Confirm the driver is advertising slices
DeviceClasses exist as soon as the driver is installed, but a Pod can only be scheduled once the kubelet-plugin publishes ResourceSlices for the node's GPUs:
If get resourceslices is empty, the driver is installed but not advertising devices on any node — jump to Enabling the DRA driver before continuing.
Inspect the advertised devices to learn the exact attribute keys and values your CEL selectors can match on — do not assume the profile strings; read them from your cluster:
Step 2 — Smoke-test a slice
Before wiring DRA into a training job, prove the driver hands you a slice with dra-smoke-pod.yaml. It claims one MIG slice and prints what the container sees:
You should see the slice's reduced memory ceiling — around 6 GiB for 1g.6gb, not the card's full 24 GiB — which is the whole point:
Check the allocation from the control plane:
Step 3 — Fine-tune inside the slice with Kubeflow Trainer v2
The dra-sft-trainingruntime.yaml TrainingRuntime is an ordinary Trainer v2 runtime with exactly two DRA hooks and no nvidia.com/* limit:
Its training script is self-contained — it builds a small causal-LM, wraps it in a LoRA adapter, and trains on a synthetic dataset, so the run needs no model or dataset download and finishes in about a minute. That makes it safe on air-gapped GPU nodes and ideal for validating the slice. Swap in a real base model and dataset to make it a production fine-tune (see Turn this into a real fine-tune); the DRA plumbing does not change.
Apply the runtime and submit the job:
Follow it to completion:
Success looks like:
peak_mem well under the slice's total_mem confirms the fine-tune ran entirely within its partition. Run a second TrainJob while the first is going: on a card partitioned into four 1g.6gb slices, up to four fine-tunes run concurrently and isolated on one physical GPU — each blind to the others' memory.
Sizing the slice / choosing a profile
Change the MIG profile in the CEL selector of mig-slice-resourceclaimtemplate.yaml:
Typical profiles (confirm the exact strings against your ResourceSlices — geometry is per-GPU):
Pick the smallest profile whose memory comfortably holds your model + LoRA + optimizer state + activations. A 0.5–1.5B model LoRA fits 1g.6gb; a 7B QLoRA usually wants 2g.12gb+.
No MIG? Share a full GPU by time-slicing
If the GPU isn't MIG-capable, or you don't want to partition it, shared-gpu-resourceclaimtemplate.yaml requests a full GPU shared by time-slicing via an opaque GpuConfig:
Point the runtime at this template instead (resourceClaimTemplateName: shared-gpu-timeslice) — nothing else changes. The e2e case takes DRA_SLICE_MODE=shared to run exactly this path.
Combine slices with Kueue quota
DRA is about the shape of the device; Kueue is about who may use how much. Kueue understands DRA DeviceClasses, so a ClusterQueue can quota mig.nvidia.com devices the same way it quotas nvidia.com/gpu — admitting TrainJobs against a slice budget and preempting borrowers when a higher-priority job needs a slice back. The Preemptible TrainJobs with Kueue guide's cohort pattern applies unchanged; swap the covered resource in the ResourceFlavor for the DRA device class. (DRA support in Kueue is evolving — verify against your Kueue version before relying on it in production.)
Wiring the same claim into other orchestrators
The DRA hooks live in the Pod spec, so anything that lets you shape a Pod can request a slice — Trainer v2 is just one caller:
- Kubeflow Pipelines (KFP v2): attach
resourceClaims/resources.claimsto a task's Pod through thekfp-kubernetesplatform config (or a pod-spec patch), so a pipeline component runs on a slice. - Volcano / plain
Job/Pod: the two hooks are byte-for-byte identical to the smoke Pod — setspec.resourceClaimsand the container'sresources.claims.
The smoke Pod is the minimal template for any of these.
Turn this into a real fine-tune
The bundled script trains a synthetic model so the slice can be validated offline. To fine-tune a real model on the slice, keep the two DRA hooks and swap the container for a real recipe — for example the LlamaFactory runtime from Fine-Tuning with Kubeflow Trainer v2, with its dataset-initializer / model-initializer steps downloading a base model and dataset onto a shared PVC. The only change to that runtime is deleting the nvidia.com/gpualloc / gpucores / gpumem limits and adding spec.resourceClaims + resources.claims. Size the MIG profile to the model (a 0.6B LoRA fits 1g.6gb; a 7B QLoRA wants 2g.12gb+).
Enabling the DRA driver (admin)
The NVIDIA DRA driver installs a controller plus a kubelet-plugin DaemonSet gated on a node label. Until a GPU node carries that label, the plugin publishes no ResourceSlices and DRA Pods stay Pending.
One device plugin per physical GPU. The NVIDIA DRA kubelet-plugin and a classic device plugin (e.g. HAMi's nvidia.com/gpualloc, or the stock nvidia.com/gpu plugin) cannot both manage the same card — they will double-book it. Dedicate a node (or a specific GPU) to DRA, and make sure the other plugin does not select it. Enabling MIG mode on a card that another plugin is actively serving will disrupt that plugin's workloads.
Troubleshooting
For repeatable coverage of this whole flow, the c15_dra_gpu_slice.sh case applies the template, runtime, and TrainJob, and asserts the fine-tune finishes inside the slice. It self-skips when no gpu.nvidia.com ResourceSlices are present, so it stays green on device-plugin-only clusters.