hands-on labKubernetes in your browser

PriorityClass & Preemption: Who Survives the GPU Squeeze

When GPU capacity is full and a critical training job lands, who wins?

time
35 min
checked steps
5
level
Intermediate
setup
None

$29.99a month with Pro

Pro opens this lab and every other one. Cancel any time.

The job

You inherit a 4-GPU cluster shared by three teams: inference (always running), training (long jobs, batch), and research (short experimental runs). The cluster is fully occupied 60% of the time. Without rules, every new training job sits Pending behind whoever submitted first. With PriorityClass + preemption, training can preempt lower-priority research pods, and inference critical-path pods are protected from disruption. This lab walks you through building exactly that policy.

5 steps, each checked when you finish it

A check runs your work at the end of every step. Hints and the full solution are there if you get stuck.

  1. 1

    The contention problem: what happens when GPUs run out

    You're sharing a 4-GPU cluster with three teams.

    read this step below ↓

  2. 2

    Reading PriorityClasses: value, preemptionPolicy, globalDefault

    A PriorityClass is a tiny cluster-scoped object, three fields do all the work.

  3. 3

    Preemption in action: watch the scheduler evict a low-pri pod

    The cluster is already full.

  4. 4

    preemptionPolicy: Never: high priority, polite waiting

    Sometimes a workload is genuinely high-priority but you don't want it to evict running pods.

  5. 5

    Triage Day: three pods broken in priority-related ways

    The cluster is full again, 4 low-priority batch-* pods are using all 4 GPUs.

Step 1 as it appears in the lab

The lab’s own text. The hint and the solution stay inside the lab.

Step 1: The contention problem: what happens when GPUs run out

The contention problem — what happens when the cluster is full

You're sharing a 4-GPU cluster with three teams. By 2pm, all 4 GPUs are running long batch jobs. A critical inference deployment ships at 3pm and needs 1 GPU. What happens?

Out of the box: the inference pod sits Pending forever. Kubernetes' default scheduler is strict first-come-first-served. The pod that was submitted earlier holds its GPU until it finishes or crashes — there's no notion of "this new pod is more important." This is a design choice (predictability over disruption), but for production multi-tenant clusters it's the wrong default.

The fix is PriorityClass + preemption — Kubernetes' answer to GPU contention with intent. This step builds the picture; the next four make it real.

Two limits, one bottleneck

There are two completely different things that decide whether a pod gets scheduled:

LimitWhere it's enforcedWhat it controlsWhen you hit it
Namespace ResourceQuotaAPI server admissionThe total requests a namespace can have summed across all its podsPod creation rejected with Forbidden: exceeded quota
Node capacitySchedulerWhether the node has enough free Allocatable for the pod's requestsPod is admitted but stuck Pending with Insufficient nvidia.com/gpu

PriorityClass + preemption only operates against the second limit. If the namespace quota is the bottleneck, no amount of priority will save your pod — it gets rejected at admission, before the scheduler even sees it. If capacity is the bottleneck, the scheduler can preempt lower-priority pods to make room.

Lab setup note: for this lab, the GPU quota is intentionally set higher (8) than the cluster's capacity (4). That's the production-realistic shape — quota is a budget, capacity is the physical floor. With 4 GPU pods running, the cluster is at capacity but quota still has 4 GPUs of slack — exactly the regime where the scheduler can do meaningful work with priority.

Read the cluster

Run the step's commands (or the Run button) to see the three things you'll work with for the rest of the lab:

# 1. Capacity — how many GPUs the real node can actually run. The four kwok-*
#    nodes are simulated and tainted NoSchedule, so they never count here.
kubectl get nodes -l 'type!=kwok' -o custom-columns='NAME:.metadata.name,CAPACITY:.status.capacity.nvidia\.com/gpu,ALLOCATABLE:.status.allocatable.nvidia\.com/gpu'

# 2. Namespace quota — how many GPU requests this namespace is allowed to admit.
kubectl get resourcequota session-quota -o jsonpath='{"hard.requests.nvidia.com/gpu: "}{.spec.hard.requests\.nvidia\.com/gpu}{"\n"}'

# 3. PriorityClasses — the four custom ones plus two system ones.
kubectl get priorityclass

Note that capacity (4) is lower than the namespace's GPU quota (8). That gap is what makes preemption meaningful in this lab — admission allows up to 8 requests, but only 4 can actually run, so the scheduler decides who gets to run.

What "priority" buys you

Every pod has a .spec.priority integer (default 0). When the scheduler looks at the Pending queue and decides what to admit next, it processes pods in descending priority order. Same priority? FIFO by submission time. So priority alone reorders the queue.

Preemption goes one step further: when a high-priority pod can't fit on any node because lower-priority pods are using the resources, the scheduler can evict (preempt) one or more of those lower-priority pods to free space. The preempted pods go back to Pending and re-queue.

You don't set priority directly — you set priorityClassName to reference a PriorityClass object whose value field carries the integer. We'll see those PriorityClasses in the next step.

Why this matters in practice: in production, GPU contention is the rule, not the exception. The decision which job gets to run when the cluster is full is the most consequential one your scheduling policy makes. Without PriorityClasses, that decision is "whoever submitted first" — which is fine for one team but disastrous for multi-tenant. Every NVIDIA-shop platform engineer needs to know the priority + preemption mechanic; the NCA-AIIO exam tests it directly under the AI Infrastructure & Operations domain.

what you get

  • A Kubernetes cluster and terminal in your browser
  • A check at the end of every step
  • Hints and the full solution
  • Nothing to install or configure

before you start

  • Completed `nca-aiio-resource-requests-limits` (or comfortable with QoS classes, ResourceQuota, and pod admission)
  • Familiar with `kubectl get pods -w`, `kubectl describe pod`, and reading scheduler events

Exam domains covered

AI Infrastructure & OperationsGPU Acceleration & Distributed Training

Part of the GPU and Kubernetes track

Lab 6 of 20. GPU scheduling on your own Kubernetes cluster, the GPU Operator, CUDA kernels and profiling.

Questions about this lab

What's the difference between priority and preemption?

Priority is a ranking, every pod has an integer priority value (default 0) that the scheduler uses when deciding the order of pending pods to admit. Preemption is the action that priority enables, when the scheduler can't fit a high-priority pod on any node because lower-priority pods are using the resources, it can evict (preempt) one or more of those lower-priority pods to make room. Priority always works (it just changes queue order); preemption only fires when the scheduler determines that evicting specific lower-priority pods would let the new pod schedule.

What is preemptionPolicy: Never and when should I use it?

It's a per-PriorityClass setting that turns off the eviction part of preemption while keeping the ranking part. A pod with preemptionPolicy: Never and a high priority value will be admitted ahead of low-priority pods at the front of the Pending queue, but it will not evict any running lower-priority pod to make room. If the cluster is full, it just waits. Use this for high-priority workloads that are still tolerant of waiting (long batch training that can run overnight) when disruption to lower-priority work would be worse than the wait. Most teams set it incorrectly, they want disruption-free ordering but accidentally turn off the entire preemption mechanism.

How does the scheduler pick which lower-priority pod to evict?

The scheduler picks victim pods to minimize disruption while still making room. It considers: lower-priority first (starting with PriorityClass values lower than the incoming pod's, in ascending order), then PodDisruptionBudgets (avoid evicting pods if it would violate a PDB), then minimizing the number of evicted pods (sometimes just one is enough). Pods running with the system PriorityClasses system-cluster-critical (2000000000) and system-node-critical (2000001000) are essentially never preempted, those values are above any reasonable user-defined priority and protect things like the kubelet, control-plane components, and DNS.

Why would my high-priority GPU pod stay Pending even with preemption enabled?

Three common reasons: (1) the pods occupying GPU capacity are at equal or higher priority, the scheduler won't preempt them. (2) Your pod's request can't be satisfied even after evicting all evictable low-priority pods (e.g., asking for 4 GPUs when the cluster has 2 GPUs of low-priority work and 2 of medium-priority, only the 2 low get evicted, you still need 2 more). (3) preemptionPolicy: Never is set on your PriorityClass, you got the priority ordering but explicitly turned off eviction. Diagnose with kubectl describe pod and look at the scheduler events for the explanation.

What you'll learn

PriorityClass and preemption are how production teams resolve GPU contention at the Kubernetes scheduler level. Without them, the only outcome of a full cluster is a Pending pod queue ordered by submission time, there's no notion of "this training job matters more than that throwaway notebook." This lab teaches the complete model: how Kubernetes uses an integer priority value to rank pods, how the scheduler decides which low-priority pods to evict to make room for a high-priority pod, why preemptionPolicy: Never is what you use when you want priority ranking without disruption, and the failure modes when a pod references a PriorityClass that doesn't exist.

By the end you'll have authored three PriorityClasses (low, medium, high), watched the scheduler preempt a low-priority pod when GPU capacity is exhausted, and diagnosed three pods broken in priority-related ways. This is the daily work of a platform engineer running a multi-tenant GPU cluster, and it's a major topic in the NCA-AIIO exam's AI Infrastructure & Operations domain.