ADR 0013 — Dedicated-CPU d1 Instance Types + CPU Manager Static Policy
Status: Accepted (build-discovered, commit 8a8a79f; not logged in the phase log
despite the commit message describing it as a fix — see Architecture
§4.3 for the grid-workload/licensing rationale that motivated choosing d1 in the
first place).
Context
Windows compute VMs use KubeVirt’s d1 dedicated-CPU instance-type family
(Architecture §4.3), chosen for two independent reasons: Symphony
compute hosts are a CPU-bound grid-scheduling workload that benefits from real CPU
isolation rather than shared/overcommitted vCPUs, and Red Hat’s own guidance ties
AWS Windows License Included compliance to dedicatedCpuPlacement — non-compliant
VMs may fail KMS activation, suffer degraded CPU pinning, and risk licensing audits.
What was not obvious until it broke in the build: d1 bakes in
dedicatedCPUPlacement: true at the instancetype level. This is a hard scheduling
prerequisite, not a preference — it requires the target node’s kubelet to run with
cpuManagerPolicy: static. Without that KubeletConfig in place, every VM using a
d1.* instancetype is permanently ErrorUnschedulable ("didn’t match Pod’s node
affinity/selector"), even though the Argo CD Application itself shows
Synced/Healthy — the failure is invisible at the GitOps layer and only visible as
a stuck VM.
Two further constraints shaped how the fix was rolled out:
-
The
KubeletConfighad to be scoped to a dedicatedvirt-metalMachineConfigPool(selecting only thenode-role.symphonygrid/virt-labeled metal node) rather than the shared worker pool, specifically to avoid forcing a rolling reboot of the three non-metal workers that don’t run any VMs at all. -
The metal node itself still reboots when the Machine Config Operator rolls this out. Because
sym-master1/sym-master2(the RHEL management-host VMs) are not live-migratable — their disks are EBS/RWO, not RWX — the MCO’s node drain would hang indefinitely on KubeVirt’s eviction-blocking PodDisruptionBudget unless those masters are gracefully stopped first, before the pool rollout, and restarted after.
Decision
Ship a scoped virt-metal MachineConfigPool + KubeletConfig
(gitops/clusters/rosa/virt-metal-cpumanager.yaml) that:
-
Selects only the metal virtualization node (not the shared worker pool).
-
Sets
cpuManagerPolicy: static,cpuManagerReconcilePeriod: 5s,reservedSystemCPUs: "0-3". -
Is applied with
sym-master1/sym-master2gracefully stopped first, to avoid an MCO drain hang on non-migratable VM PDBs.
Treat this KubeletConfig as a hard platform prerequisite for the d1
instance-type recommendation, not an optional tuning knob — document it alongside
the d1 recommendation itself (Architecture §4.3), since at the time of
the build it was only discoverable from a manifest comment and a commit message, not
from the phase log or the reference architecture text.
Consequences
Pros
-
d1.*VMs schedule correctly and satisfy the AWS Windows LI compliance requirement fordedicatedCpuPlacement, with real CPU isolation appropriate to a CPU-bound grid workload. -
Scoping the
MachineConfigPoolto the metal node avoids an unnecessary rolling reboot of the three non-metal workers — a real cost/disruption saving on a shared PoC cluster. -
The graceful-stop-masters-first sequencing is now a documented, repeatable procedure rather than a rediscovered trap on every future MCO rollout against this pool.
Cons
-
reservedSystemCPUs: "0-3"(4 cores) on the metal node is capacity permanently lost to the system and unavailable for VM scheduling — this must be subtracted from every capacity-planning calculation for thevirt-metalpool, not treated as free headroom. -
Any future MCO-driven change to the
virt-metalpool (KubeletConfigupdate, kernel arg, etc.) reboots the metal node and therefore requires the same non-migratable-master stop-first choreography every time — this is now a standing operational procedure, not a one-off migration step. -
Because the failure mode (
ErrorUnschedulableon aSynced/HealthyApplication) gives no GitOps-layer signal, any other unmet dedicated-CPU prerequisite in the future would fail the same silent way unless it, too, is checked explicitly.
Continue to ADR 0014: Layer2 UDN + MTU.