Working a devoted Kubernetes cluster per crew typically ends in extra isolation than a company requires. Whereas one cluster may be efficiently shared throughout many groups, the coordination prices enhance because the variety of groups grows. Challenges embrace conflicting CRD variations, overlapping RBAC, and no clear method to carve GPU capability into team-level budgets. At a sure scale, groups would possibly begin asking for their very own clusters simply to regain autonomy.
This publish offers a sample that preserves crew autonomy with out splitting the {hardware}. This answer entails a single management aircraft cluster with a GPU pool, GPU sharing with per-team quotas, and remoted Kubernetes management aircraft per crew together with an API server, controller, knowledge retailer, syncer, and scheduler. This may be achieved utilizing two open supply instruments: KAI Scheduler and vCluster.
Comply with together with the tutorial steps and also you’ll have three groups operating actual GPU Kubernetes pods in their very own tenant clusters, all sharing a single bodily GPU. You’ll additionally be capable to confirm that every crew solely sees their very own workloads.
Tutorial conditions and notes
To maintain the method reproducible for customers which have restricted sources, this tutorial makes use of a cluster with one NVIDIA L40S GPU and three groups sharing fractions of it. This makes the transferring components simple to see and check out. The method works the identical means on a bigger cluster with tons of of GPU nodes and dozens of groups. You possibly can scale the node pool, the queue hierarchy, and the variety of tenant clusters.
KAI Scheduler is a strong, environment friendly, and scalable topology-aware Kubernetes scheduler that was purpose-built for optimizing GPU useful resource allocation for AI workloads. It’s designed to handle large-scale GPU clusters, together with hundreds of nodes, and a excessive throughput of workloads. With KAI Scheduler, you possibly can dynamically allocate GPU sources to workloads. It may run alongside the default kube-scheduler. Any pod with schedulerName: kai-scheduler is dealt with by KAI Scheduler. Every thing else goes by means of the standard kube-scheduler course of.
The vCluster Kubernetes platform provisions absolutely remoted tenant clusters in your infrastructure or instantly on naked steel. Every tenant cluster will get its personal API server, customized useful resource definitions (CRDs), and role-based entry management (RBAC), indistinguishable from a devoted Kubernetes cluster, whereas sharing the underlying nodes and {hardware}. The virtualized management aircraft is invisible to tenants: no shared management aircraft nodes, no in-cluster agent pods, and no lateral path between environments. That makes vCluster a pure match for GPU infrastructure the place groups want their very own clear cluster expertise with out splitting the {hardware}.
The vCluster shared-nodes mannequin is used for this tutorial, so groups share the GPU node whereas every will get its personal remoted management aircraft, the suitable match for trusted inner groups. For untrusted tenants needing node-, network-, and storage-level separation, the identical sample extends to vCluster personal nodes.
The instance on this publish makes use of three groups: NLP Group, Imaginative and prescient Group, and Recommender System Group. The NLP Group needs to put in their very own CRDs. The Imaginative and prescient Group needs cluster-admin to debug scheduling. The Recommender Group is on a unique Kubeflow model. No one needs to share a kubectl context and unintentionally break each other’s environments.
Utilizing vCluster, every crew will get their very own remoted management aircraft, RBAC, namespaces, and CRDs. They will every have cluster-admin entry. Beneath, all tenant clusters share the identical nodes and GPUs.
Demo setting
This demo runs on an NVIDIA Brev GPU occasion on Nebius with:
One NVIDIA L40S, 40 vCPUs, 160 GiB RAM, 256 GiB disk (48 GB VRAM)
Ubuntu 24.04.4 LTS
MicroK8s v1.36.2 – Kubernetes was preconfigured by Brev, together with the MicroK8s gpu addon, which pre-installs the NVIDIA GPU Operator into the gpu-operator-resources namespace
KAI Scheduler v0.16.4
vCluster CLI 0.35.1
Notice: For various setups, the cluster-creation and GPU Operator set up steps will differ on GKE/EKS/AKS/vanilla k8s/k3s. Step 3 (KAI Scheduler) onward is an identical on any Kubernetes that has the NVIDIA GPU Operator operating with Container Machine Interface (CDI) enabled.


Set up standalone kubectl and helm to keep away from prefixing each command with microk8s. Then wire up a kubeconfig and pin MicroK8s so snap doesn’t auto-upgrade the management aircraft in the course of the demo.
sudo snap set up kubectl –classic –channel=1.35/secure
sudo snap set up helm –classic
mkdir -p ~/.kube
sudo microk8s config > ~/.kube/config
sudo chown $USER:$USER ~/.kube/config
chmod 600 ~/.kube/config
Subsequent, be certain the required MicroK8s addons are enabled:
microk8s allow hostpath-storage # vCluster wants PVCs
Then confirm:
kubectl get storageclass
brev-8dq0cch1j Prepared 57d v1.36.2 10.0.0.20 Ubuntu 24.04.4 LTS 6.11.0-1016-nvidia (amd64) containerd://2.2.3
NAME PROVISIONER RECLAIMPOLICY VOLUMEBINDINGMODE ALLOWVOLUMEEXPANSION AGE
microk8s-hostpath (default) microk8s.io/hostpath Delete WaitForFirstConsumer false 20m
Step 2: Add the Helm repo
Add the NVIDIA Helm repo:
helm repo replace
Step 3: Verify the GPU Operator
gpu-feature-discovery-wpcjm 1/1 Working 0 8m
gpu-operator-57d75775c8-npjzz 1/1 Working 0 9m
gpu-operator-node-feature-discovery-… 1/1 Working 0 9m
nvidia-container-toolkit-daemonset-ft4hb 1/1 Working 0 8m
nvidia-cuda-validator-8vqdv 0/1 Accomplished 0 8m
nvidia-device-plugin-daemonset-l6fj6 1/1 Working 0 8m
nvidia-operator-validator-cdnmj 1/1 Working 0 8m
In case you’re utilizing an older GPU Operator, improve in place to 26.3.x:
-n gpu-operator-resources
–version v26.3.3
–reset-then-reuse-values
If wanted, set up NVIDIA GPU Operator instantly:
-n gpu-operator-resources –create-namespace
–version v26.3.3
–set driver.enabled=false
–set operator.defaultRuntime=containerd
–set toolkit.env[0].identify=CONTAINERD_CONFIG
–set toolkit.env[0].worth=/var/snap/microk8s/present/args/containerd.toml
–set toolkit.env[1].identify=CONTAINERD_SOCKET
–set toolkit.env[1].worth=/var/snap/microk8s/widespread/run/containerd.sock
–set-string toolkit.env[2].identify=CONTAINERD_SET_AS_DEFAULT
–set-string toolkit.env[2].worth=1
Step 4: Set up KAI Scheduler
Subsequent, set up the KAI Scheduler:
oci://ghcr.io/kai-scheduler/kai-scheduler/kai-scheduler
-n kai-scheduler –create-namespace
–version v0.16.4
–set “international.gpuSharing=true”
Then confirm:
admission-57556f949-sp98t 1/1 Working 0 47s
binder-66785d8dd9-9frgk 1/1 Working 0 46s
kai-operator-6fdf595c4d-292d7 1/1 Working 0 52s
kai-scheduler-default-6bb667b767-vq2q4 1/1 Working 0 46s
pod-grouper-84dfc7759b-v5qtb 1/1 Working 0 47s
podgroup-controller-5878f48dbb-v7wcn 1/1 Working 0 47s
queue-controller-7796bb8984-hdn5r 1/1 Working 0 46s
Step 5: Outline crew queues
KAI Scheduler makes use of a Queue CRD to mannequin an org → crew hierarchy. This step entails creating one mother or father (ml-org, with a complete funds of 1 GPU) and three youngster queues. Every is assured 0.33 of the GPU and allowed to extend to the total GPU when others are idle.
Save the next as create-queues.yaml:
form: Queue
metadata:
identify: ml-org
spec:
sources:
gpu: { quota: 1, restrict: -1, overQuotaWeight: 1 }
—
apiVersion: scheduling.run.ai/v2
form: Queue
metadata:
identify: team-nlp
spec:
parentQueue: ml-org
precedence: 100
sources:
gpu: { quota: 0.33, restrict: 1, overQuotaWeight: 1 }
—
apiVersion: scheduling.run.ai/v2
form: Queue
metadata:
identify: team-vision
spec:
parentQueue: ml-org
precedence: 100
sources:
gpu: { quota: 0.33, restrict: 1, overQuotaWeight: 1 }
—
apiVersion: scheduling.run.ai/v2
form: Queue
metadata:
identify: team-recommender
spec:
parentQueue: ml-org
precedence: 100
sources:
gpu: { quota: 0.33, restrict: 1, overQuotaWeight: 1 }
Right here, quota is the assured minimal, restrict is the utmost allowed, and overQuotaWeight controls how surplus is break up.
Subsequent, apply and record:
kubectl get queues
queue.scheduling.run.ai/team-nlp created
queue.scheduling.run.ai/team-vision created
queue.scheduling.run.ai/team-recommender created
NAME PRIORITY PARENT CHILDREN DISPLAYNAME
default-parent-queue [“default-queue”]
default-queue default-parent-queue
ml-org [“team-nlp”,”team-vision”,”team-recommender”]
team-nlp 100 ml-org
team-recommender 100 ml-org
team-vision 100 ml-org
Notice that default-parent-queue and default-queue are created mechanically by KAI Scheduler on first set up. They’re the fallback queue for any pod that doesn’t specify one.
Step 6: Spin up a vCluster per crew
Subsequent, set up the vCluster CLI:
sudo set up -m 755 vcluster /usr/native/bin/vcluster
rm vcluster
vcluster –version
Outline the vCluster config. The crucial setting is setOwner: false. The KAI Scheduler pod-grouper walks possession chains (Job → Pod, Deployment → ReplicaSet → Pod) to auto-group workloads. Disabling vCluster proprietor rewriting permits KAI Scheduler to see the actual hierarchy.
experimental:
syncSettings:
setOwner: false
sync:
fromHost:
nodes:
enabled: true
selector:
all: true
EOF
Then create one vCluster per crew:
vcluster create team-vision –values vcluster.yaml –connect=false
vcluster create team-recommender –values vcluster.yaml –connect=false
Confirm that every one three vClusters are operating:
——————-+—————————+———+———+———–+——-
team-nlp | vcluster-team-nlp | Working | 0.35.1 | | 102s
team-recommender | vcluster-team-recommender | Working | 0.35.1 | | 88s
team-vision | vcluster-team-vision | Working | 0.35.1 | | 93s
Every crew sees the actual nodes, together with the GPU node:
NAME STATUS ROLES AGE VERSION
brev-8dq0cch1j Prepared 6m20s v1.36.2
Step 7: Deploy a workload from every crew
Every crew deploys their GPU workload from their very own vCluster. The pod spec is straightforward, with three fields telling KAI Scheduler what to do:
apiVersion: v1
form: Pod
metadata:
identify: nlp-sentiment-model
labels:
kai.scheduler/queue: team-nlp # Which crew
annotations:
gpu-fraction: “0.33” # How a lot GPU
spec:
schedulerName: kai-scheduler. # Use KAI, not default
tolerations:
– key: nvidia.com/gpu
operator: Exists
impact: NoSchedule
containers:
– identify: nlp-inference
picture: nvidia/cuda:12.4.0-base-ubuntu22.04
command: [“bash”, “-c”, “nvidia-smi; sleep infinity”]
nodeSelector:
nvidia.com/gpu.current: “true”
EOF
pod/nlp-sentiment-model created
Do the identical to deploy from every crew, altering the queue identify.
Now, confirm:
vcluster-team-nlp nlp-sentiment-model-x-default-x-team-nlp 1/1 Working 0 110s 10.1.171.180 brev-8dq0cch1j
vcluster-team-recommender recommender-model-x-default-x-team-recommender 1/1 Working 0 6s 10.1.171.187 brev-8dq0cch1j
vcluster-team-vision vision-classifier-model-x-default-x-team-vision 1/1 Working 0 14s 10.1.171.145 brev-8dq0cch1j
Three pods, three totally different vcluster-team-* namespaces, all operating on the identical bodily node brev-8dq0cch1j.
Every crew sees solely their very own pod from inside their vCluster:
vcluster join team-vision — kubectl get pods -o huge
vcluster join team-recommender — kubectl get pods -o huge
nlp-sentiment-model 1/1 Working 0 3m11s 10.1.171.180 brev-8dq0cch1j
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
vision-classifier-model 1/1 Working 0 3m59s 10.1.171.145 brev-8dq0cch1j
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
recommender-model 1/1 Working 0 2m45s 10.1.171.187 brev-8dq0cch1j
Lastly, the queue confirms the allocation. You possibly can test all three on the identical time:
kubectl describe queue team-vision | grep -A4 Standing
kubectl describe queue team-recommender | grep -A4 Standing
You will note the identical end result for all:
Allotted:
nvidia.com/gpu: 330m
Requested:
nvidia.com/gpu: 330m
Notice that KAI Scheduler handles scheduling—which pods land on which GPU and in what quantity. It doesn’t implement GPU reminiscence isolation on the {hardware} degree when GPU sharing is used. Functions have to respect their reminiscence quantity (for instance, setting –gpu-memory-utilization in vLLM).
Beneath the hood, the GPU time-slices between CUDA contexts from every pod at kernel boundaries. For laborious reminiscence isolation on supported {hardware}, NVIDIA Multi-Occasion GPU (MIG) offers hardware-level partitioning, which may be scheduled by KAI Scheduler as nicely.
Get began operating remoted tenant Kubernetes clusters
KAI Scheduler decides pretty who will get GPU slices and schedules them as a gaggle utilizing GPU sharing and DRA Driver assist, hierarchical queues with assured quotas plus over-quota capabilities, gang scheduling, and topology consciousness. As soon as AI workload scheduling is solved, groups need their very own clusters. vCluster offers every crew its personal remoted Kubernetes management aircraft with out the price of separate infrastructure.
Collectively, KAI Scheduler and vCluster present a devoted cluster expertise for 3 groups with zero waste on a single GPU. The reply isn’t at all times extra GPUs, however higher utilization of infrastructure.
Able to get began? Try KAI Scheduler, vCluster, and NVIDIA GPU Operator on GitHub.
Study extra about KAI Scheduler and vCluster integration at KubeCon 2026 North America, November 9-12.

