User guide: real CPU pinning (spec.resources.cpuPinning)
Real, exclusive host-core allocation -- a Machine actually owns specific
physical CPUs, with Kairon's own scheduler tracking and enforcing
non-overlapping allocation across every Machine competing for cores on a
node. The capability machine-cpu-numa.md's own
cpuSet field explicitly does not provide (that one is a
guest-visible topology hint only).
Why this exists
FluxVM already supports real cgroup v2 cpuset.cpus pinning via its own
resize API -- the missing piece was never the low-level primitive, it was
that allocating specific, non-overlapping host CPU numbers across every
Machine competing for them on one node is a real capacity-allocation
problem, and Kairon's scheduler used to do pure Machine-count
bin-packing with no notion of CPU capacity at all. This closes that gap.
Prerequisites: assert which CPUs are pinnable
Kairon has no way to independently discover which host CPUs are safe to
exclusively hand to a Machine -- an operator must assert this via a node
label, the same "operator asserts a fact Kairon can't otherwise know"
pattern kairon.zyvor.dev/storage-domain/network-domain/vfio-devices
already use:
kubectl label node worker-1 kairon.zyvor.dev/pinnable-cpus="2-15"
The value uses Linux's own cpuset.cpus list syntax ("2-15" or
"2,3,4-8,20") -- you can often paste the output of
cat /sys/fs/cgroup/cpuset.cpus.effective directly, after excluding
whatever cores you want reserved for the OS, kairon-node itself, and any
kubelet-managed (Guaranteed-QoS) Pods already running real workloads on
that node. This is a real, deliberate design choice: rather than
Kairon reading kubelet's own internal, undocumented, version-dependent
cpu_manager_state file (a known but fragile, unsupported community
pattern with no stability guarantee), the operator is the one source of
truth for which cores are actually safe to hand out -- the same posture
already established for storage/network domain compatibility and VFIO
allowlisting. A node with no pinnable-cpus label has zero pinnable
CPUs -- fail-closed, the same posture an empty KAIRON_VFIO_ALLOWLIST
already has.
Requesting real pinning
apiVersion: kairon.zyvor.dev/v1alpha1
kind: Machine
metadata: {name: latency-sensitive-db}
spec:
image: {path: /var/lib/fluxvm/images/db.qcow2}
resources:
cpu: "4"
memory: 8Gi
cpuPinning: true
runtime: {backend: qemu}
powerState: Running
At scheduling time, kairon-controller filters out any candidate node
without at least spec.resources.cpu free, currently-unclaimed cores from
its pinnable-cpus label, then deterministically picks the cores (a
Machine Pending with a clear message if no node has enough). The chosen
core numbers land in spec.resources.allocatedCpuSet -- system-computed,
the same relationship spec.nodeName itself already has to the
scheduler: not something you set yourself. kairon-node
applies it to the live FluxVM cgroup the same reconcile pass that already
applies spec.resources.limits
(machine-resource-limits.md) -- both merge
into one FluxVM call, never two competing writes.
Real limits today (first cut)
- QEMU only, matching every other NUMA/cpuSet/hugepages field.
- Not NUMA-topology-aware. Core selection is deterministic first-fit
over the
pinnable-cpuslabel's own ascending order -- it does not cross-validate againstspec.resources.numaNodeeven when both are set on the same Machine. If you need pinned cores to also fall within a specific NUMA node, thepinnable-cpuslabel itself must already be scoped to that node's own cores (an operator responsibility). - Creation-time allocation only.
spec.resources.allocatedCpuSetis computed once, at initial scheduling -- there's no live re-allocation if a Machine'sspec.resources.cpuchanges later (hotplug doesn't grow the pinned set) or if the node's ownpinnable-cpuslabel changes after the fact. - Races across reconcile ticks are possible in principle. There is no
distributed lock in this codebase -- two Machines scheduled in the same
kairon-controllerreconcile pass are allocated from the same consistent snapshot (safe), butkairon-controlleritself doesn't run more than one replica actively reconciling at once (leader election already guarantees this, seekairon-controller-ha.md), so this is a theoretical, not a practically-observed, risk today. - No enforcement against kubelet's own CPU Manager beyond what the
operator excludes from the label. If the operator's
pinnable-cpuslabel includes a core kubelet's static CPU Manager later exclusively grants to a new Guaranteed-QoS Pod, nothing here detects or prevents that collision -- keeping the label accurate as node workloads change is an ongoing operator responsibility, not something Kairon verifies. - No live-migration compatibility checking -- the same posture
machine-cpu-numa.md's own NUMA fields already have; a pinned Machine can attempt migration to a target with a completely differentpinnable-cpuslabel, and whether that's sensible is between you and the target node's own topology. - No dashboard support --
spec.resources.cpuPinningis spec-only.