Scheduling Shards
July 29, 2026 ยท View on GitHub
Scheduling shards partition a cluster into logical groups, each with a dedicated scheduler instance that operates on a subset of nodes, queues, and pod groups.
How It Works
Each shard creates a separate KAI scheduler deployment that filters resources by the partitionLabelValue. The scheduler only considers resources with matching labels:
- Nodes:
<nodePoolLabelKey>=<partitionLabelValue> - Queues: Same label structure
- Pod Groups: Same label structure
The nodePoolLabelKey is configurable in the global configuration (default: kai.scheduler/node-pool). For example, with the default configuration, nodes are labeled as kai.scheduler/node-pool=gpu-nodes.
Shards operate independently with their own configuration for placement strategies, queue depths, and runtime requirements.
Creating Scheduling Shards
Basic Shard Default Configuration
This shard will define the partition of all nodes that are not labeled by the partition label:
apiVersion: kai.scheduler/v1
kind: SchedulingShard
metadata:
name: default
Advanced Shard Configuration
apiVersion: kai.scheduler/v1
kind: SchedulingShard
metadata:
name: gpu-shard
spec:
partitionLabelValue: gpu-nodes
# Custom scheduler arguments
args:
leader-elect: "true"
v: "3"
# Placement strategy
placementStrategy:
gpu: binpack
cpu: binpack
# Queue depth configuration
queueDepthPerAction:
preempt: 15
reclaim: 8
allocate: 25
# Minimum runtime requirements
minRuntime:
preemptMinRuntime: "10m"
reclaimMinRuntime: "5m"
The args field maps directly to scheduler CLI flags (--<flag>=<value>), for example v, qps, burst, and leader-elect.
When installing with Helm, configure these via scheduler.args in your values file (for example scheduler.args.v: "3").
Go memory limit
Each scheduler shard derives Go's memory limit from its effective cgroup memory limit. Set goMemLimitRatio to reserve headroom for memory not managed by the Go runtime; it defaults to 0.9 and is refreshed every 15 seconds when the cgroup limit changes.
Set goMemLimit only to override automatic calculation for a shard. It is passed to the scheduler as GOMEMLIMIT and disables automatic updates.
apiVersion: kai.scheduler/v1
kind: SchedulingShard
metadata:
name: gpu-shard
spec:
goMemLimitRatio: 0.85
# goMemLimit: 6Gi
The scheduler warns and preserves its existing Go memory limit when its cgroup limit is unavailable or unlimited. An abrupt VPA memory decrease can still OOM the process before the next refresh.
For customizing which plugins and actions run in a shard (disabling, reordering, overriding arguments), see Scheduler Config Customization.
Node Preparation
Labeling Nodes
To use the scheduling shards, label your nodes appropriately:
# Label GPU nodes
kubectl label nodes node-1 kai.scheduler/node-pool=gpu-nodes
kubectl label nodes node-2 kai.scheduler/node-pool=gpu-nodes
# Label CPU nodes
kubectl label nodes node-3 kai.scheduler/node-pool=cpu-nodes
kubectl label nodes node-4 kai.scheduler/node-pool=cpu-nodes
# Label high-memory nodes
kubectl label nodes node-5 kai.scheduler/node-pool=high-memory-nodes
Queue Configuration
Shard-Specific Queues
Create queues that target specific shards:
apiVersion: scheduling.run.ai/v2
kind: Queue
metadata:
name: gpu-queue-parent
labels:
kai.scheduler/node-pool: gpu-nodes # Targets GPU shard
spec:
priority: 100
resources:
gpu:
quota: -1
---
apiVersion: scheduling.run.ai/v2
kind: Queue
metadata:
name: gpu-queue
labels:
kai.scheduler/node-pool: gpu-nodes # Targets GPU shard
spec:
parentQueue: gpu-queue-parent
priority: 100
resources:
gpu:
quota: 10
apiVersion: scheduling.run.ai/v2
kind: Queue
metadata:
name: cpu-queue
labels:
kai.scheduler/node-pool: cpu-nodes # Targets CPU shard
spec:
priority: 50
parentQueue: cpu-queue-parent
resourceQuota:
cpu:
quota: 200
memory:
quota: -1
Job Submission
Direct Shard Targeting
Jobs should be directly submitted to a specific shard:
apiVersion: v1
kind: Pod
metadata:
name: gpu-pod
namespace: test
labels:
kai.scheduler/queue: foo-queue-test
kai.scheduler/node-pool: foo
spec:
schedulerName: kai-scheduler
containers:
- name: main
image: ubuntu
command: ["bash", "-c"]
args: ["nvidia-smi; trap 'exit 0' TERM; sleep infinity & wait"]
resources:
limits:
nvidia.com/gpu: "1"
The created pod group will have the same labels as the top owner of the pod, which will then include the node-pool label
apiVersion: scheduling.run.ai/v2alpha2
kind: PodGroup
metadata:
annotations:
kai.scheduler/top-owner-metadata: |
name: gpu-pod
uid:
group: ""
version: v1
kind: Pod
labels:
kai.scheduler/queue: foo-queue-test
name: pg-gpu-pod-d81e6f2c-8da7-4e61-8758-d8a2c38d2bfb
namespace: test
ownerReferences:
- apiVersion: v1
kind: Pod
name: gpu-pod
uid:
uid:
spec:
minMember: 1
priorityClassName: train
queue: foo-queue-test
The PodGroup's label can later be updated manually to direct the job to a different shard.
Monitoring and Observability
Shard Status
Monitor shard status and health:
# Check shard status
kubectl get schedulingshard
kubectl describe schedulingshard gpu-shard
# Check shard deployments
kubectl get deployments -n kai-system -l kai.scheduler/shard=gpu-shard
Shard Logs
View logs for specific shards:
# View shard scheduler logs
kubectl logs -n kai-system deployment/kai-scheduler-gpu-shard
See Also
- Scheduling Deep Dive โ How scheduling concepts interact within and across shards