Verify batch admission
Work from demo/:
cd "$LAB_DIR"Prerequisites for this page
Section titled “Prerequisites for this page”- Queues, NodePool
batch, andexamples/workloads/sample-jobs.yamlfrom Queues and flavors - Optional but useful: KueueViz open at http://localhost:8080 while you burst
- A
sample-jobstill present in each namespace you will burst (Complete is fine — the script clones the Job spec) jqon your PATH (used to build clean Job manifests)
jq --versionkubectl get job sample-job -n project-akubectl get job sample-job -n project-bkubectl get job sample-job -n project-cIf a sample Job is missing, re-apply:
kubectl apply -f examples/workloads/sample-jobs.yamlConfirm the three sample Workloads finished
Section titled “Confirm the three sample Workloads finished”Optional — if you just finished the previous page, samples may already be Complete / FINISHED=True:
kubectl get workloads -Akubectl get localqueue -AFinished samples release Spot quota. If samples are still Running when you burst, they count toward the 10 CPU cap (you may see fewer than 10 new admits until they finish).
Burst test (quota cap)
Section titled “Burst test (quota cap)”Create a helper that clones each namespace’s own sample-job into many burst Jobs. It uses jq (not raw sed on live Job YAML) so controller UIDs and selectors are not copied — those break apply.
Burst Jobs sleep 180s by default so quota stays reserved long enough to inspect Queued Workloads (SLEEP_SECONDS overrides).
mkdir -p examples/workloads
cat > examples/workloads/burst-jobs.sh <<'EOF'#!/usr/bin/env bashset -euo pipefail
NAMESPACE="${1:-project-a}"COUNT="${2:-20}"QUEUE="${3:-$NAMESPACE}"SLEEP_SECONDS="${SLEEP_SECONDS:-180}"
if ! kubectl get job sample-job -n "$NAMESPACE" >/dev/null 2>&1; then echo "Missing sample-job in ${NAMESPACE}. Apply examples/workloads/sample-jobs.yaml first." >&2 exit 1fi
tmpdir=$(mktemp -d)trap 'rm -rf "$tmpdir"' EXIT
for i in $(seq 1 "$COUNT"); do kubectl get job sample-job -n "$NAMESPACE" -o json | jq --arg name "burst-${i}" --arg queue "$QUEUE" --argjson sleep "$SLEEP_SECONDS" ' { apiVersion: .apiVersion, kind: .kind, metadata: { name: $name, namespace: .metadata.namespace, labels: { "kueue.x-k8s.io/queue-name": $queue } }, spec: { parallelism: .spec.parallelism, completions: .spec.completions, backoffLimit: (.spec.backoffLimit // 6), template: { spec: ( .spec.template.spec | .restartPolicy = "Never" | .containers[0].command = ["/bin/sh", "-c", ("sleep " + ($sleep | tostring))] | del(.nodeName) ) } } } ' > "${tmpdir}/burst-${i}.json"done
kubectl apply -f "$tmpdir"echo "Submitted ${COUNT} jobs to namespace ${NAMESPACE} (queue ${QUEUE}, sleep ${SLEEP_SECONDS}s)"echo "Watch: kubectl get workloads -A; kubectl get localqueue -A"EOF
chmod +x examples/workloads/burst-jobs.sh./examples/workloads/burst-jobs.sh project-a 30Inspect immediately:
kubectl get localqueue -Akubectl get clusterqueue shared-batchkubectl get workloads -n project-akubectl get pods -n project-akubectl get nodes -L karpenter.sh/capacity-type,karpenter.sh/nodepoolIf KueueViz is open, watch Admitted vs waiting Workloads update while these commands show the quota numbers.
What you should see
Section titled “What you should see”With SPOT_CPU_QUOTA=10 and 1 CPU per Job, a 30-Job burst into project-a (and no other active admits) yields roughly:
NAMESPACE NAME CLUSTERQUEUE PENDING WORKLOADS ADMITTED WORKLOADSproject-a project-a shared-batch 20 10project-b project-b shared-batch 0 0project-c project-c shared-batch 0 0
NAME COHORT PENDING WORKLOADSshared-batch 20ClusterQueue Spot usage should show 10 CPU reserved (names/ages vary):
Flavors Usage: Name: spot ... cpu Total: 10 memory Total: 2000MiKueueViz Local Queues during that burst (10 admitted / 20 pending on project-a):
Same story at the ClusterQueue — shared-batch with 10 admitted and 20 pending:
| Observation | Meaning |
|---|---|
~10 Workloads Admitted (ADMITTED=True, RESERVED IN=shared-batch) |
Spot CPU quota cap working |
~20 Workloads with empty RESERVED IN / not Admitted |
Queued — waiting for Kueue quota, not a broken scheduler |
| Some Admitted pods Running, others Pending | Quota reserved; Auto Mode / Spot node capacity still catching up (batch NodePool) |
Extra Spot nodes appearing (NODEPOOL=batch) |
Auto Mode scaling for admitted work |
Fair share across tenants (shared ClusterQueue)
Section titled “Fair share across tenants (shared ClusterQueue)”Delete the previous burst first so quota is free, then submit from each tenant’s own sample-job:
for ns in project-a project-b project-c; do jobs=$(kubectl get jobs -n "$ns" -o name | grep burst || true) [ -n "$jobs" ] && kubectl delete -n "$ns" $jobsdone
./examples/workloads/burst-jobs.sh project-a 10./examples/workloads/burst-jobs.sh project-b 10./examples/workloads/burst-jobs.sh project-c 10
kubectl get localqueue -Akubectl get workloads -A | head -40All LocalQueues map to shared-batch with BestEffortFIFO. If you submit project-a first while quota is free, expect something like:
NAMESPACE NAME CLUSTERQUEUE PENDING WORKLOADS ADMITTED WORKLOADSproject-a project-a shared-batch 0 10project-b project-b shared-batch 10 0project-c project-c shared-batch 10 0KueueViz view of that FIFO outcome:
That is correct for this lab: tenants share one ClusterQueue, not three isolated 10-CPU budgets. When project-a Jobs finish, Kueue admits Workloads from project-b / project-c next. True weighted multi-tenant fairness needs extra Kueue policy (priorities / cohorts) beyond this walkthrough.
Clean up workloads (keep cluster)
Section titled “Clean up workloads (keep cluster)”kubectl delete jobs -n project-a --allkubectl delete jobs -n project-b --allkubectl delete jobs -n project-c --allRe-apply samples later if you need them again:
kubectl apply -f examples/workloads/sample-jobs.yamlNext: Teardown when finished with the lab.