Alright, let’s talk about Karpenter. Because apparently, the idea of “constraints” is more of a polite suggestion than a rule. I’ve been running it in production for a few months now, and while the autoscaling is fast—I’ll give it that—its interpretation of my node pool constraints feels increasingly… creative.
We defined a `NodePool` with some pretty basic requirements: AMD instances only, on-demand for this particular workload, and a specific set of labels for the team selector. Simple, right? Here’s the relevant snippet:
```yaml
apiVersion: karpenter.sh/v1beta1
kind: NodePool
metadata:
name: team-a-app-pool
spec:
template:
metadata:
labels:
workload-type: latency-sensitive
team: team-a
spec:
nodeClassRef:
name: default
requirements:
- key: node.kubernetes.io/instance-type
operator: In
values: ["c5a.large", "c6a.large", "r5a.large"]
- key: karpenter.sh/capacity-type
operator: In
values: ["on-demand"]
- key: kubernetes.io/arch
operator: In
values: ["amd64"]
```
What did we get? ARM nodes. Spot instances. An `m6g.xlarge` happily joined the party, wearing our `team-a` label like a stolen badge. The logs showed Karpenter evaluating the pod against the NodePool, deciding the constraints were “mostly” met if you squint hard enough. It seems like if the immediate capacity isn’t available, it starts playing fast and loose with the requirement list, prioritizing just getting something—anything—provisioned.
I’ve seen chatter about this being “flexibility,” but when you’re trying to keep costs predictable and have architecture-specific binaries, this isn’t flexibility—it’s a breach of contract. The real kicker? The drift wasn’t flagged anywhere in our monitoring until a batch of pods failed to start due to the ARM mismatch.
So, is this just me being unlucky with my config, or have others watched Karpenter treat your carefully crafted `NodePool` as a loose guideline? Are we all just waiting for the eventual reconciliation to fix it, or is there a secret incantation to make it actually respect the rules?