From d89e204ef6f263bd7944614ce9cb72935fb91362 Mon Sep 17 00:00:00 2001 From: James Thompson Date: Sun, 5 Apr 2026 16:16:47 -0700 Subject: [PATCH 1/3] docs: Balanced consolidation policy RFC A new consolidationPolicy: Balanced that scores each consolidation move by comparing savings and disruption as fractions of NodePool totals. Moves where disruption outweighs savings are rejected. - consolidationThreshold (integer, 1-3, default 2): a move passes when its disruption fraction is at most k times its savings fraction - Per-node disruption cost of 1.0 eliminates division-by-zero edge cases - Score-based ranking replaces disruption-only ranking when budget limits move count - Exhaustive verification across c7i/m7i/r7i confirms k=2 is the smallest value that makes within-family REPLACEs viable --- designs/balanced-consolidation.md | 442 ++++++++++++++++ designs/ranking-strategies.png | Bin 0 -> 176278 bytes .../balanced-consolidation-properties.py | 415 +++++++++++++++ .../scripts/balanced-consolidation-ranking.py | 475 ++++++++++++++++++ 4 files changed, 1332 insertions(+) create mode 100644 designs/balanced-consolidation.md create mode 100644 designs/ranking-strategies.png create mode 100644 designs/scripts/balanced-consolidation-properties.py create mode 100644 designs/scripts/balanced-consolidation-ranking.py diff --git a/designs/balanced-consolidation.md b/designs/balanced-consolidation.md new file mode 100644 index 0000000000..45c84f4e75 --- /dev/null +++ b/designs/balanced-consolidation.md @@ -0,0 +1,442 @@ +# Balanced Consolidation: Scoring Moves by Savings and Disruption + +## Motivation + +Today, Karpenter's consolidation is all-or-nothing. `WhenEmptyOrUnderutilized` consolidates any node where pods can be repacked more cheaply, regardless of how little is saved or how many pods are disrupted. `WhenEmpty` consolidates only nodes with no pods. A move that saves $0.02/day by evicting a pod with a 30-minute warm-up cache is treated the same as a move that saves $5/day by evicting a stateless proxy. + +Terminating a non-empty node requires evicting running pods and starting replacements. Customers report cases where the disruption is not worth the savings. Related issues: + +- Nodes at 93-99% CPU utilization disrupted instead of lightly utilized ones ([aws#8868](https://github.com/aws/karpenter-provider-aws/issues/8868), [kubernetes-sigs#2319](https://github.com/kubernetes-sigs/karpenter/issues/2319)) +- Multi-hour consolidation loops replacing the same instance types with no net savings ([aws#8536](https://github.com/aws/karpenter-provider-aws/issues/8536), [aws#6642](https://github.com/aws/karpenter-provider-aws/issues/6642), [aws#7146](https://github.com/aws/karpenter-provider-aws/issues/7146)) +- Rapid node churn where consolidation deletes nodes that are immediately re-provisioned ([kubernetes-sigs#1019](https://github.com/kubernetes-sigs/karpenter/issues/1019), [kubernetes-sigs#735](https://github.com/kubernetes-sigs/karpenter/issues/735), [kubernetes-sigs#1851](https://github.com/kubernetes-sigs/karpenter/issues/1851)) +- `consolidateAfter` not preventing disruption of well-packed nodes ([kubernetes-sigs#2705](https://github.com/kubernetes-sigs/karpenter/issues/2705), [aws#3577](https://github.com/aws/karpenter-provider-aws/issues/3577)) +- Direct requests for a savings threshold or utilization-based consolidation gating ([kubernetes-sigs#2883](https://github.com/kubernetes-sigs/karpenter/issues/2883), [kubernetes-sigs#1440](https://github.com/kubernetes-sigs/karpenter/issues/1440), [kubernetes-sigs#1686](https://github.com/kubernetes-sigs/karpenter/issues/1686), [kubernetes-sigs#1430](https://github.com/kubernetes-sigs/karpenter/issues/1430), [aws#5218](https://github.com/aws/karpenter-provider-aws/issues/5218)) + +This RFC calls each consolidation action a *move*. A move deletes one or more nodes, along with pod eviction and optional replacement node creation. We propose a new `consolidationPolicy` value, `Balanced`, that scores each move and rejects moves where the disruption outweighs the savings. A `consolidationThreshold` parameter (default 2) controls the tradeoff. + +## Alternatives Considered + +Five approaches were considered and rejected. Each fails to account for something the scoring approach captures. + +### Cost Improvement Factor + +Require a minimum price improvement ratio (e.g., old_price / new_price >= 2). Considered for spot consolidation ([spot-consolidation.md](https://github.com/kubernetes-sigs/karpenter/blob/main/designs/spot-consolidation.md)). A move that saves 50% of a node's cost passes a 2x factor whether it disrupts 2 default-cost pods or 20 high-cost pods. The factor ignores disruption entirely. + +### Absolute Dollar Threshold + +Require savings to exceed a fixed dollar amount (e.g., $1/day) ([kubernetes-sigs#2883](https://github.com/kubernetes-sigs/karpenter/issues/2883), [kubernetes-sigs#1440](https://github.com/kubernetes-sigs/karpenter/issues/1440)). Two moves that each save $1/day and disrupt 4 default-cost pods: on a $50/day NodePool with 40 pods, this saves 2% of cost for 10% of disruption. On a $5,000/day NodePool with 4000 pods, the same $1 saves 0.02% for 0.1% of disruption. A $1 threshold approves both. The threshold does not scale with NodePool size. + +### Utilization-Based Threshold + +Exclude nodes above a resource utilization percentage (e.g., 70%) from consolidation, like CA's `scale-down-utilization-threshold` ([kubernetes-sigs#1686](https://github.com/kubernetes-sigs/karpenter/issues/1686), [aws#5218](https://github.com/aws/karpenter-provider-aws/issues/5218)). This is the most frequently requested alternative. A node at 40% utilization running one pod with `pod-deletion-cost: 2147483647` (a model-serving pod with a 2-hour warm-up cache) and a node at 40% running ten stateless pods both pass a 70% threshold. The utilization threshold cannot distinguish them. + +### Selective Consolidation Type Disable + +Disable single-node consolidation (replace) while keeping multi-node and emptiness consolidation ([kubernetes-sigs#1430](https://github.com/kubernetes-sigs/karpenter/issues/1430), [kubernetes-sigs#684](https://github.com/kubernetes-sigs/karpenter/issues/684), [PR #1433](https://github.com/kubernetes-sigs/karpenter/pull/1433)). An m7i.2xlarge ($9.68/day) running 2 pods requesting 2 vCPU total could replace to an m7i.large ($2.42/day), saving $7.26/day for 2 pods of disruption. Disabling replace blocks this move along with every other replace, regardless of the savings-to-disruption ratio. + +### Separate Disruption Cost Annotation + +A dedicated `karpenter.sh/disruption-cost` annotation separate from the existing `EvictionCost` inputs. This would let application developers independently control eviction ordering and consolidation gating. We prefer reusing existing parameters. `controller.kubernetes.io/pod-deletion-cost` and pod priority already express disruption cost. A separate annotation could be introduced later if eviction ordering and consolidation gating need to diverge. + +### Related Work + +- [PR #2562](https://github.com/kubernetes-sigs/karpenter/pull/2562): `ConsolidationPriceImprovementFactor` field (0.0-1.0) with operator-level default and NodePool override. Cost Improvement Factor with a different UX. +- [PR #2893](https://github.com/kubernetes-sigs/karpenter/pull/2893): Decision ratio with a configurable `DecisionRatioThreshold` (default 1.0). Same scoring approach as this RFC but exposes the threshold from day one. +- [PR #2901](https://github.com/kubernetes-sigs/karpenter/pull/2901): External health signal probes on NodePools that block disruption when a probe fails. Orthogonal and complementary. +- [PR #2894](https://github.com/kubernetes-sigs/karpenter/pull/2894): Controller that automatically manages `controller.kubernetes.io/pod-deletion-cost` based on pluggable ranking strategies. Complementary. + +## Proposal + +### Proposed Spec + +```yaml +apiVersion: karpenter.sh/v1 +kind: NodePool +metadata: + name: default +spec: + disruption: + consolidationPolicy: Balanced + consolidationThreshold: 2 + consolidateAfter: 30s + budgets: + - nodes: 10% +``` + +`Balanced` scores each consolidation move and approves it when `score >= 1/consolidationThreshold`. + +The three consolidation policies sit on a spectrum from conservative to aggressive: + +| Policy | Behavior | +|---|---| +| `WhenEmpty` | Only empty nodes (emptiness controller, no scoring) | +| `Balanced` | Savings must justify disruption | +| `WhenEmptyOrUnderutilized` | Any positive savings | + +`WhenEmpty` and `WhenEmptyOrUnderutilized` are implemented by their existing controllers. `Balanced` uses the scoring formula. The spectrum is conceptual — `WhenEmpty` and `WhenEmptyOrUnderutilized` are not special cases of the formula. They remain separate code paths. + +`consolidationThreshold` controls how aggressively `Balanced` consolidates. Higher values approve more moves. At the default of 2, a move passes when its disruption fraction is at most 2x its savings fraction. The parameter accepts integer values 1, 2, or 3. Validation rejects values outside this range and `consolidationThreshold` without `consolidationPolicy: Balanced`. + +If an operator enables `BalancedConsolidation`, sets `consolidationPolicy: Balanced`, then disables the feature gate during rollback, the controller falls back to `WhenEmptyOrUnderutilized` behavior and sets a `ConsolidationPolicyUnsupported` status condition on the NodePool. The condition message directs the operator to change the policy or re-enable the gate. This avoids reconcile failures while making the fallback visible. + +### How Scoring Works + +The score compares a move's savings and disruption as fractions of NodePool totals. + +#### Per-Pod Disruption Cost + +[`EvictionCost`](../pkg/utils/disruption/disruption.go) starts at 1.0 per pod and adds two terms: + +1. **Pod deletion cost** ([`controller.kubernetes.io/pod-deletion-cost`](https://kubernetes.io/docs/concepts/workloads/controllers/replicaset/#pod-deletion-cost)), divided by 2^27, range -16 to +16. Default 0. The ReplicaSet controller uses this for scale-down ordering; Karpenter reuses it as a disruption signal. +2. **Pod priority**, divided by 2^25, range -64 to +30. Default 0. Higher-priority pods increase their node's disruption cost. + +With neither set, per-pod disruption cost is 1.0. `EvictionCost` clamps to [-10, 10]. The scoring path clamps negative values to 0 via `max(0, EvictionCost(pod))` in the per-node formula (see [NodePool Totals](#nodepool-totals)). Other consumers of `EvictionCost` (eviction ordering) still see negatives. Scoring range per pod: [0, 10]. + +#### Default Behavior: Savings vs. Pod Count + +Most clusters today do not set `pod-deletion-cost`. When every pod has default disruption cost 1.0, the score reduces to savings-versus-pod-count. It still rejects marginal moves, but the ability to distinguish expensive-to-restart pods from cheap ones is inactive until operators or automation ([PR #2894](https://github.com/kubernetes-sigs/karpenter/pull/2894)) set per-pod costs. + +#### NodePool Totals + +The score normalizes savings and disruption against NodePool totals. + +``` +nodepool_cost = sum(node.price for node in nodepool.nodes) +nodepool_total_disruption_cost = sum(node.disruption_cost for node in nodepool.nodes) +``` + +Each node's disruption cost is 1.0 (per-node cost) plus `sum(max(0, EvictionCost(pod)))` for its pods. Nodes have a disruption cost independent of their pods (cordoning, draining, API calls, replacement latency). We have not modeled this precisely and 1.0 is a placeholder. It eliminates division-by-zero for empty nodes and gives room to refine later (e.g., if GPU nodes should carry higher inherent disruption cost than CPU nodes). A 10-pod node has disruption cost 11; the per-node cost is 9% of the total. A node whose only pod has `EvictionCost = -10` has disruption cost 1.0, same as an empty node. + +For cross-NodePool moves, the source pool's totals, policy, and budget govern scoring. Cross-pool DELETEs require scheduling simulation to confirm pods fit on the destination. The destination pool's budget is not consumed (it absorbs pods, not loses a node). The source pool's policy governs regardless of the destination's policy; otherwise a permissive destination could override a conservative source. + +NodePool totals are snapshotted once per cycle. Later moves in the same cycle use stale totals. A move that scored 1.5 against a 100-node snapshot still passes against a 98-node pool. Moves near the boundary could flip; the next cycle corrects. + +#### Calculation + +``` +savings = sum(deleted_node.price) - sum(created_node.price) # price = Karpenter's pricing model (on-demand, current spot, or on-demand/10M for ODCRs) +disruption_cost = sum(max(0, EvictionCost(pod)) for pod in evicted_pods) + +savings_fraction = savings / nodepool_total_cost +disruption_fraction = disruption_cost / nodepool_total_disruption_cost + +score = savings_fraction / disruption_fraction +``` + +`evicted_pods` is all pods on deleted source nodes. Every pod is evicted regardless of where it lands. The disruption cost counts every eviction. + +A move is approved when `score >= 1/k`, where k is `consolidationThreshold` (default 2). At k=2, `score >= 0.5`. Both sides are dimensionless fractions, so the score is scale-invariant (see [Scale Invariance](#scale-invariance)). + +**Division-by-zero handling.** With the per-node disruption cost of 1.0, `disruption_cost` is always positive for any node, eliminating the zero-disruption special case. The remaining edge cases: + +| savings | nodepool_total_cost | nodepool_total_disruption_cost | decision | +|---|---|---|---| +| positive | positive | positive | compute score normally | +| zero | any | any | reject (no benefit) | +| negative | any | any | reject (net loss) | +| near-zero | near-zero (ODCR pool) | any | compute normally; the 1/10M divisor cancels and the score equals the on-demand price ratio | +| any | any | zero | cannot happen (per-node disruption cost ensures > 0) | + +See [Edge Cases](#edge-cases) for worked examples. + +Feasibility checks (PDBs, `karpenter.sh/do-not-disrupt`, scheduling constraints) filter which moves can be generated. `consolidateAfter` determines which nodes are candidates. Scoring evaluates which feasible moves are worth executing. Disruption budgets gate how many execute per cycle. + +### Move Score as Ranking Function + +When multiple moves pass the threshold and a disruption budget limits how many execute, the score determines execution order. Today, `WhenEmptyOrUnderutilized` ranks by disruption alone (lowest first). Score-based ranking accounts for both savings and disruption. Greedy-by-ratio is the standard knapsack heuristic, [optimal for the continuous case](https://en.wikipedia.org/wiki/Continuous_knapsack_problem#Solution) and near-optimal for the discrete case when items are small relative to the budget. + +![Ranking consolidation moves: score vs. single-dimension ranking](ranking-strategies.png) + +The graphs show REPLACE and DELETE moves from a simulated cluster: 5000 pods with log-normal CPU/memory requests, packed across c7i/m7i/r7i instances, after 10 rounds of workload churn. Each curve shows cumulative savings vs. cumulative disruption under a different ranking strategy. At every disruption level, score-based ranking delivers more savings than the alternatives (see [`balanced-consolidation-ranking.py`](scripts/balanced-consolidation-ranking.py)). + +### Edge Cases + +#### Empty Nodes + +An empty node has disruption cost 1.0 (per-node cost only). A DELETE saves the full node cost against a small disruption fraction. Empty DELETEs always pass. No special case needed. + +#### Single-Node Pool + +A single-node pool DELETE scores exactly 1.0 (`savings_fraction = 1.0`, `disruption_fraction = 1.0`). This passes the default threshold. Deleted pods become unschedulable and trigger a new provisioning cycle. See [Open Questions](#open-questions) for whether the formula should have a floor on pool size. + +#### Near-Zero-Cost Nodes (ODCRs, Reserved Capacity) + +Karpenter prices ODCRs and reserved instances at on-demand price divided by a large constant, keeping the most expensive ODCR cheaper than the cheapest spot node (see the provider's pricing implementation for the exact divisor). Within an ODCR-only pool, the divisor cancels in the score (it divides both savings and total cost). Scores equal the on-demand price ratios, so ODCR pools consolidate normally. This is correct: replacing an expensive reservation with a cheaper one frees capacity. The divided values are small but well within float64 precision. + +When a positive-cost source node is consolidated and its pods land on an ODCR destination node, this is a DELETE from the source pool's perspective. The score reflects the source pool's cost structure. The destination node's near-zero cost does not affect the score. + +### Candidate Filtering + +Move generation is expensive (find a destination, compute replacement costs, verify scheduling). A node's best possible score is its delete ratio: a DELETE saving the full node cost with no replacement. + +``` +delete_ratio = (node.price / nodepool_cost) / (node.disruption_cost / nodepool_total_disruption_cost) +``` + +If this ratio is below 1/k, this node is not a good consolidation candidate. A DELETE saves the full node cost — a REPLACE saves strictly less because the replacement has positive cost. If the best case (DELETE) doesn't pass, nothing will. The system skips move generation for that node. + +This filter applies to single-node consolidation. A group of individually-failing nodes could produce a passing batch if their combined savings outweigh combined disruption. The filter misses these opportunities. Evaluating all multi-node combinations is exponential, so the implementation takes single-node savings first and attempts multi-node moves only when single-node opportunities are exhausted. + +The NodePool totals only need to be sensible relative to each other. The implementation may cache totals or estimate them from a subset of nodes, as long as cost and disruption are estimated from the same sample. + +### Interaction with Existing Features + +Existing feasibility checks (disruption budgets, PDBs, `consolidateAfter`, `do-not-disrupt`) are unchanged. Scoring applies only to consolidation, not to spot interruptions, expiration, or drift. + +**Consolidation cycling loops.** The scoring filter is the primary mechanism that breaks cycling loops ([aws#8536](https://github.com/aws/karpenter-provider-aws/issues/8536), [aws#6642](https://github.com/aws/karpenter-provider-aws/issues/6642), [aws#7146](https://github.com/aws/karpenter-provider-aws/issues/7146)). A same-type REPLACE that saves $0 scores 0 and is rejected at any threshold. A near-zero-savings move with meaningful disruption scores below 1/k and is also rejected. These are the moves that produce multi-hour loops under `WhenEmptyOrUnderutilized`, where any positive savings (including rounding-error savings) triggers a replace. The `consolidateAfter` cooldown on destination nodes (described below) provides a secondary damping effect between rounds but is not sufficient on its own to prevent cycling. + +`consolidateAfter` determines candidacy: a node whose last pod event is within the `consolidateAfter` window is not a candidate. Non-candidate nodes still contribute to the denominators, preventing the post-replacement cycle from being artificially aggressive. When a move executes, pods landing on the destination node reset its `consolidateAfter` timer, temporarily removing it from candidacy. This provides a natural cooldown between consolidation rounds. + +### Observability + +Approved and rejected moves are surfaced as events. Single-node moves emit on the NodeClaim. Multi-node moves emit on the NodePool (the score describes the move, not any single node). + +- `ConsolidationApproved`: `"score %.2f >= threshold %.2f (consolidationThreshold: %.1f, savings %.1f%%, disruption %.1f%%)"` +- `ConsolidationRejected`: `"score %.2f < threshold %.2f (consolidationThreshold: %.1f, savings %.1f%%, disruption %.1f%%)"` + +Scored moves are also logged at DEBUG level. + +A Prometheus histogram `karpenter_consolidation_score` records scores by decision and NodePool, with buckets {0.1, 0.25, 0.33, 0.5, 1.0, 2.0, 5.0, 10.0}. A counter `karpenter_consolidation_moves_total` by decision and NodePool tracks move volume. + +How to use these: if `moves_total{decision="rejected"}` is high and the histogram shows most rejected scores clustered just below the threshold, the operator's k is slightly too conservative — raising it by 1 would capture those moves. If `moves_total{decision="approved"}` is zero and `moves_total{decision="rejected"}` is also zero, there are no candidates (the cluster is well-packed or `consolidateAfter` hasn't elapsed). If approved moves are high but savings aren't materializing, kube-scheduler divergence may be undoing the moves (check for rapidly churning NodeClaims). + +## Examples + +All examples use the default `consolidationThreshold` of 2. The NodePool has 10 nodes: eight m7i.xlarge (4 vCPU, 16 GiB, $4.84/day) and two m7i.2xlarge (8 vCPU, 32 GiB, $9.68/day). Total NodePool cost is $58.08/day. The NodePool runs 80 pods with total disruption cost 80. + +### Oversized Node (approved) + +One m7i.2xlarge runs 3 pods requesting 1.5 vCPU and 6 GiB total. Disruption cost is 3. These pods fit on an m7i.large at $2.42/day. Savings is $7.26. + +``` +savings_fraction = 7.26 / 58.08 = 12.5% +disruption_fraction = 3 / 80 = 3.75% +score = 0.125 / 0.0375 = 3.33 > 0.5 --> approved +``` + +### Spare Capacity Delete (approved) + +One m7i.xlarge runs 4 pods requesting 1.5 vCPU and 6 GiB. Disruption cost is 4. Another node has spare capacity. Savings is $4.84 (full node cost, no replacement needed). + +``` +savings_fraction = 4.84 / 58.08 = 8.3% +disruption_fraction = 4 / 80 = 5.0% +score = 0.083 / 0.05 = 1.67 > 0.5 --> approved +``` + +### Marginal Move (rejected) + +One m7i.xlarge runs 8 pods requesting 1.8 vCPU and 7 GiB. Disruption cost is 8. The pods fit on an m7i.large at $2.42/day. Savings is $2.42. + +``` +savings_fraction = 2.42 / 58.08 = 4.2% +disruption_fraction = 8 / 80 = 10.0% +score = 0.042 / 0.10 = 0.42 < 0.5 --> rejected +``` + +### Well-Packed Node (rejected) + +One m7i.xlarge runs 10 pods requesting 3.5 vCPU and 14 GiB. The smallest fitting replacement is another m7i.xlarge. Savings is $0. No threshold approves this move. + +### Uniform Pool Replace (approved) + +All 10 nodes are m7i.xlarge ($4.84/day each, $48.40/day total, 80 pods, disruption cost 80). One node's 8 pods fit on an m7i.large ($2.42/day). Savings is $2.42. + +``` +savings_fraction = 2.42 / 48.40 = 5.0% +disruption_fraction = 8 / 80 = 10.0% +score = 0.05 / 0.10 = 0.50 >= 0.5 --> approved +``` + +The replacement costs exactly half the original. In a uniform pool, this is the boundary at k=2: a replace that saves less than half the source node's cost is rejected. In a heterogeneous pool, the threshold depends on pool-level fractions, not per-node percentage. At k=1, the score simplifies to `1 - replacement_price / node_price`, which never reaches 1.0. k=2 is the smallest value that makes uniform-pool REPLACEs viable. + +### Scale Invariance + +The same oversized-node scenario on a 100-node NodePool ($580.80/day total cost, 800 total disruption cost) produces the same score: + +``` +savings_fraction = 7.26 / 580.80 = 1.25% +disruption_fraction = 3 / 800 = 0.375% +score = 0.0125 / 0.00375 = 3.33 +``` + +The threshold produces the same decision regardless of NodePool size. + +### Heterogeneous Disruption Cost + +Two m7i.xlarge nodes each run 4 pods and can be deleted (pods fit on other nodes). Both save $4.84/day. Node A runs 4 stateless proxies with default disruption cost (total 4). Node B runs 1 stateless proxy (cost 1) and 3 model-serving pods with `pod-deletion-cost: 2147483647` (cost ~10 each, total 31). The NodePool total disruption cost is 107 (76 default-cost pods + node B's 31). + +**Node A (approved):** + +``` +savings_fraction = 4.84 / 58.08 = 8.3% +disruption_fraction = 4 / 107 = 3.7% +score = 0.083 / 0.037 = 2.24 > 0.5 --> approved +``` + +**Node B (rejected):** + +``` +savings_fraction = 4.84 / 58.08 = 8.3% +disruption_fraction = 31 / 107 = 29.0% +score = 0.083 / 0.29 = 0.29 < 0.5 --> rejected +``` + +Same savings, same node count, same pod count. The score rejects node B because the model-serving pods are expensive to restart. This is the score's main advantage over alternatives that ignore disruption cost: it distinguishes nodes where disruption is cheap from nodes where it is not. + +### Cross-NodePool: On-Demand and Spot + +A cluster has two NodePools. The On-Demand pool has 10 m7i.xlarge nodes at $4.84/day each ($48.40/day total, 80 pods, total disruption cost 80). The Spot pool has 10 m7i.xlarge nodes at $1.45/day each ($14.50/day total, 80 pods, total disruption cost 80). + +One node in each pool runs 3 pods requesting 1 vCPU. Disruption cost is 3. The pods can be absorbed by other nodes. This is a DELETE of the source node. + +**On-Demand pool DELETE:** + +``` +savings_fraction = 4.84 / 48.40 = 10.0% +disruption_fraction = 3 / 80 = 3.75% +score = 0.10 / 0.0375 = 2.67 > 0.5 --> approved +``` + +**Spot pool DELETE:** + +``` +savings_fraction = 1.45 / 14.50 = 10.0% +disruption_fraction = 3 / 80 = 3.75% +score = 0.10 / 0.0375 = 2.67 > 0.5 --> approved +``` + +Both moves score identically because each node represents the same fraction of its pool's cost and disrupts the same fraction of its pool's pods. + +If the Spot pool node instead runs 8 pods with disruption cost 8: + +``` +savings_fraction = 1.45 / 14.50 = 10.0% +disruption_fraction = 8 / 80 = 10.0% +score = 0.10 / 0.10 = 1.0 > 0.5 --> approved +``` + +The move is approved. To reach the 0.5 boundary, each of the 8 pods would need disruption cost 2 (disruption fraction 20%, score 0.50). + +## Why k=2 + +The scoring formula has one free parameter: `consolidationThreshold` (k). We chose k=2 by exhaustive enumeration. + +### State Space + +We enumerate configurations in a bounded space using on-demand prices from three instance families in us-east-1: c7i (compute-optimized), m7i (general-purpose), and r7i (memory-optimized), medium through 4xlarge (15 price points). 1 to 6 nodes per pool, 0 to 4 pods per node, per-pod disruption cost in {1, 2, 5, 10} (max per-node disruption cost: 4 pods x 10 = 40). For each configuration, we evaluate every candidate move (Delete and Replace to every cheaper price) at every k from 1 through 5. Three families matter because cross-family replacement ratios are not power-of-2. + +Properties 1-4, 6, and 7 are algebraic properties of the ratio formula and hold for any pod count — the enumeration is a sanity check, not the proof. Properties 5 and 8 are empirical results bounded by the enumerated price structure and pod counts. + +### What a good scoring function does + +Eight properties define what we want from the scoring formula. The first seven are about the formula itself. The eighth is about what happens over time: a REPLACE creates a new node, and that node might itself be a consolidation candidate. An m7i.xlarge replaces to m7i.large, which replaces to c7i.medium, and so on. We call this sequence a **churn chain**. A good scoring function produces churn chains that converge (reach a stable instance type) rather than cycle. + +1. **Monotonicity in savings.** Cheaper replacement never makes approval harder. +2. **Monotonicity in disruption.** Higher disruption never makes approval easier. +3. **Empty nodes always deletable.** Zero disruption, positive savings: always approved. +4. **Zero-savings moves never approved.** Same-price replacement scores zero. +5. **Replaces work in uniform pools.** The minimum useful k is the smallest value where meaningful replaces pass. +6. **Skewed disruption differentiates.** High-disruption pods make their node harder to approve. +7. **Fleet size independence.** Pool size cancels algebraically in uniform pools. +8. **Bounded churn.** Churn chains converge and terminate quickly. + +Properties 1-4, 6, and 7 hold at all k values (structural properties of the ratio). Properties 5 and 8 select k. + +### Results + +| k | min score (1/k) | approved replace pairs | new pairs vs k-1 | max churn chain | +|---|-----------------|----------------------|-----------------|-----------------| +| 1 | 1.000 | 0 | — | 0 | +| 2 | 0.500 | 78 | 78 | 4 steps | +| 3 | 0.333 | 86 | 8 | 4 steps | +| 4 | 0.250 | 95 | 9 | 9 steps | +| 5 | 0.200 | 100 | 5 | 9 steps | + +At k=1, no replace is ever approved in a uniform pool. The score for a uniform-pool replace simplifies to `1 - replacement_price / node_price`, which requires a free replacement to reach 1.0. + +k=2 is the smallest integer where uniform-pool REPLACEs pass. Within a single family, prices follow power-of-2 scaling, so every replacement ratio is 0.5 or less and k>=3 adds nothing. Across families, k=3 opens 8 additional cross-family pairs (e.g., c7i.large → m7i.medium at 43% savings, score 0.43) without increasing the max churn chain. k=4 opens 9 more pairs but allows 9-step churn chains that zigzag through all three families. Max chain lengths are confirmed by exhaustive DFS over all approved replacement paths, not a greedy heuristic (see [`balanced-consolidation-properties.py`](scripts/balanced-consolidation-properties.py)). + +k=2 is the right default. It is the smallest value that makes within-family REPLACEs viable, and it captures all cross-family pairs where the replacement costs less than half the original. The 8 additional cross-family pairs at k=3 are available to operators who set `consolidationThreshold: 3` (see [`balanced-consolidation-properties.py`](scripts/balanced-consolidation-properties.py)). + +## API Choices + +### Consolidation Aggressiveness Tuning [Recommended: consolidationThreshold] + +`consolidationThreshold` exposes k directly. A move passes when its disruption fraction is at most k times its savings fraction. Higher k approves more moves. The formally motivated values are all integers: k=1 (break-even, deletes only), k=2 (within-family replaces viable, 4-step max churn), k=3 (adds cross-family pairs, same 4-step churn). At k=4, churn chains jump to 9 steps and the formal analysis argues against higher values. The field accepts integers 1, 2, or 3. + +Two alternatives were considered: + +**Named presets (Low/Medium/High).** A `consolidationAggressiveness` enum mapping to k values (e.g., Low=1, Medium=2, High=3). Karpenter does not have an existing ordinal enum pattern, and picking names that age well is hard. "Conservative/Balanced/Aggressive" reuses "Balanced" which is already the policy name. Not preferred because the integer is simpler. + +**Continuous slider (0-100).** A percentage-based field mapping to a log-scale threshold. This adds a nonlinear transformation that obscures the underlying math. Not preferred. + +### Per-NodePool vs. Per-Cluster Normalization [Recommended: Per-NodePool] + +The score denominators (total cost, total disruption cost) could be computed per-NodePool or across the entire cluster. This proposal recommends per-NodePool. + +Per-NodePool normalization matches Karpenter's existing architecture (policies, budgets, `consolidateAfter` are already per-NodePool). Per-cluster normalization would dilute small pool scores: a single node representing 10% of its own pool becomes 0.1% of the cluster. + +Con: scores reflect relative efficiency within a pool, not absolute dollar impact. A score of 2.0 in a $50/hr pool and 2.0 in a $10,000/hr pool look identical. This does not affect behavior but limits cross-pool comparison. + +### New consolidationPolicy Value with consolidationThreshold + +No behavior change for existing users. Migration: change `WhenEmptyOrUnderutilized` to `Balanced`. + +Con: `WhenEmpty` and `WhenEmptyOrUnderutilized` describe behavior; `Balanced` describes character. Alternatives: `Scored` (exposes implementation), `CostWeighted` (implies cost-only), `WhenWorthIt` (too informal). `Balanced` is the least-bad option. + +## Backward Compatibility + +- `WhenEmptyOrUnderutilized` and `WhenEmpty` are unchanged. Existing NodePool specs continue to work. +- Per-pod disruption cost is computed from the existing `controller.kubernetes.io/pod-deletion-cost` annotation and pod priority via `EvictionCost`. Pods without these inputs default to disruption cost 1.0, matching current behavior (all pods are equal). + +## Open Questions + +- **Is k=2 the right default?** [Why k=2](#why-k2) explains why k=2 is the smallest value that makes uniform-pool REPLACEs viable. We do not know whether k=2 works well across diverse workloads. The feature gate and opt-in rollout exist to answer this empirically. + +- **Should single-node pools be exempt from scoring?** A single-node pool DELETE scores 1.0 and passes, making all pods unschedulable until provisioning creates a replacement. Disruption budgets (`nodes: 0`) prevent this today. The formula could refuse by requiring pool size > 1, but that adds a special case for something disruption budgets already handle. + +- **How many customers use `pod-deletion-cost` today?** If few do, the score reduces to savings-versus-pod-count. [PR #2894](https://github.com/kubernetes-sigs/karpenter/pull/2894) would automate annotation. + +- **Move quality tracking.** Annotate moved pods with the move's score. Track re-disruption rate (pods evicted again within minutes). High re-disruption means the threshold is too aggressive. + +- **Async move generation.** Scoring enables a shift from synchronous per-cycle consolidation to a continuous pipeline: generate, score, enqueue by priority, execute as budget allows. This eliminates the per-cycle timeout problem. + + +## Frequently Asked Questions + +### What happens in a uniformly inefficient cluster where no single REPLACE clears the threshold? + +Every REPLACE scores low (e.g., 0.2 for a one-tier downsize) and is rejected. DELETEs still work: a DELETE of a node whose pods fit elsewhere scores 1.0 in a uniform pool. The system consolidates by deleting nodes, not replacing them. Operators who want all feasible moves can use `WhenEmptyOrUnderutilized`. + +### Does the score account for kube-scheduler pod placement? + +No. The score assumes pods land on the intended destination. In practice, kube-scheduler may scatter pods across existing nodes instead of packing onto the replacement. The replacement ends up nearly empty and becomes a consolidation candidate itself. + +This exists in all consolidation modes. The cost threshold concentrates the remaining moves onto higher-impact candidates. The system self-corrects: a nearly-empty replacement scores as a trivial DELETE next cycle. Cascades terminate because each round has strictly fewer displaced nodes. + +Configuring kube-scheduler with `MostAllocated` scoring reduces divergence. + +### Why doesn't the score account for reserved instance or ODCR opportunity cost? + +See [Near-Zero-Cost Nodes](#near-zero-cost-nodes-odcrs-reserved-capacity). The 1/10M factor cancels in the score, so ODCR pools consolidate using on-demand price ratios. Real opportunity cost (what freed capacity is worth to the organization) is a different question that requires billing API integration. This RFC defers opportunity-cost modeling. + +### Where is the score visible? + +See [Observability](#observability). DEBUG logs, NodeClaim events, and a Prometheus histogram. + +### Will this become the default consolidation policy? + +Not at launch. `Balanced` is opt-in behind a feature gate. Whether it becomes the default is a community decision deferred to GA graduation. + +### Does constraining maximum node size improve this proposal? + +Rejections are driven by high disruption fraction relative to savings fraction. A 50-pod node in a 500-pod pool has 10% disruption fraction regardless of instance type. Constraining maximum node size reduces the per-node share of pool disruption, making moves easier to approve. The formula works correctly regardless of node size. Operators can manage node size through NodePool instance-type constraints today. + +## Rollout + +Standard feature gate pattern (SpotToSpotConsolidation, NodeOverlay). + +- **Alpha**: disabled by default. `--feature-gates BalancedConsolidation=true` to opt in. +- **Beta**: enabled by default. Disable if issues arise. +- **GA**: gate removed. Whether `Balanced` becomes the default policy is a separate decision. diff --git a/designs/ranking-strategies.png b/designs/ranking-strategies.png new file mode 100644 index 0000000000000000000000000000000000000000..0a406730877fbc1c550e0768db05cc7f5682006a GIT binary patch literal 176278 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

%KA$X$wAe@T9PRj&>K`}sxlQ9^^4l$KI* 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≈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 disruption_fraction +Cross-multiplied: k * savings * pool_disruption >= move_disruption * pool_cost + +State space: + - 1-6 nodes + - Prices from c7i, m7i, r7i on-demand (us-east-1), scaled to integers + - 0-4 pods per node, disruption cost in {1, 2, 5, 10} + - Actions: Delete(node), Replace(node, new_price) + - k: the decision constant (1 = break-even, 2 = savings count 2x) +""" + +from itertools import product as cartesian +from collections import defaultdict + +# On-demand prices in us-east-1, $/hr × 10000 for exact integer arithmetic. +# c7i (compute-optimized) +# c7i.medium $0.0446/hr → 446 +# c7i.large $0.0892/hr → 892 +# c7i.xlarge $0.1785/hr → 1785 +# c7i.2xlarge $0.3570/hr → 3570 +# c7i.4xlarge $0.7140/hr → 7140 +# m7i (general-purpose) +# m7i.medium $0.0504/hr → 504 +# m7i.large $0.1008/hr → 1008 +# m7i.xlarge $0.2016/hr → 2016 +# m7i.2xlarge $0.4032/hr → 4032 +# m7i.4xlarge $0.8064/hr → 8064 +# r7i (memory-optimized) +# r7i.medium $0.0661/hr → 661 +# r7i.large $0.1323/hr → 1323 +# r7i.xlarge $0.2646/hr → 2646 +# r7i.2xlarge $0.5292/hr → 5292 +# r7i.4xlarge $1.0584/hr → 10584 +PRICES = sorted([ + 446, 892, 1785, 3570, 7140, # c7i + 504, 1008, 2016, 4032, 8064, # m7i + 661, 1323, 2646, 5292, 10584, # r7i +]) +DCOSTS = [1, 2, 5, 10] +K_VALUES = [1, 2, 3] + + +def approved(k, savings, move_disruption, pool_cost, pool_disruption): + """Cross-multiplied decision rule. Handles zero-disruption edge case.""" + if move_disruption == 0: + return savings > 0 + if pool_disruption == 0: + return savings > 0 + if pool_cost == 0: + return False + return k * savings * pool_disruption >= move_disruption * pool_cost + + +def score(savings, move_disruption, pool_cost, pool_disruption): + """Actual score (float). For reporting only, not for decisions.""" + if pool_cost == 0 or pool_disruption == 0: + return float('inf') if savings > 0 else 0.0 + sf = savings / pool_cost + df = move_disruption / pool_disruption + if df == 0: + return float('inf') if sf > 0 else 0.0 + return sf / df + + +# ─── P1: Monotonicity in savings ─── + +def check_p1(): + """If replace(n, q) is approved and p < q (more savings), then replace(n, p) is approved.""" + violations = [] + for k in K_VALUES: + for n_nodes in range(1, 5): + for node_price in PRICES: + for pod_config in pod_configs(max_pods=4): + move_disruption = sum(pod_config) + # Other nodes: try a few representative configs + for other_price in PRICES[:3]: + for other_pods in [0, 2, 4]: + other_disruption = other_pods * 1 # default cost + pool_cost = node_price + (n_nodes - 1) * other_price + pool_disruption = move_disruption + (n_nodes - 1) * other_disruption + for q in PRICES: + if q >= node_price: + continue + savings_q = node_price - q + if not approved(k, savings_q, move_disruption, pool_cost, pool_disruption): + continue + # q is approved. Check all p < q. + for p in PRICES: + if p >= q: + continue + savings_p = node_price - p + if not approved(k, savings_p, move_disruption, pool_cost, pool_disruption): + violations.append((k, node_price, q, p, n_nodes)) + return violations + + +# ─── P2: Monotonicity in disruption ─── + +def check_p2(): + """Higher disruption on a node should make approval at least as hard.""" + violations = [] + for k in K_VALUES: + for n_nodes in range(2, 5): + for price in PRICES: + for other_price in PRICES[:3]: + for other_pods in [0, 2, 4]: + other_disruption = other_pods * 1 + base_pool_cost = price + (n_nodes - 1) * other_price + base_other_disruption = (n_nodes - 1) * other_disruption + for d_high in range(1, 41): + for d_low in range(0, d_high): + pool_d_high = d_high + base_other_disruption + pool_d_low = d_low + base_other_disruption + # Delete savings = price for both + app_high = approved(k, price, d_high, base_pool_cost, pool_d_high) + app_low = approved(k, price, d_low, base_pool_cost, pool_d_low) + if app_high and not app_low: + violations.append((k, price, d_high, d_low, n_nodes)) + return violations + + +# ─── P3: Empty nodes always approved for delete ─── + +def check_p3(): + """Node with 0 pods, positive price: delete always approved.""" + violations = [] + for k in K_VALUES: + for n_nodes in range(1, 7): + for price in PRICES: + for other_price in PRICES: + for other_pods in range(0, 5): + other_disruption = other_pods * 1 + pool_cost = price + (n_nodes - 1) * other_price + pool_disruption = 0 + (n_nodes - 1) * other_disruption + if not approved(k, price, 0, pool_cost, pool_disruption): + violations.append((k, price, n_nodes)) + return violations + + +# ─── P4: Zero-savings moves never approved ─── + +def check_p4(): + """Replace with same price: savings = 0, never approved.""" + violations = [] + for k in K_VALUES: + for price in PRICES: + for pod_config in pod_configs(max_pods=4): + move_disruption = sum(pod_config) + for n_nodes in range(1, 5): + pool_cost = n_nodes * price + pool_disruption = n_nodes * move_disruption + if approved(k, 0, move_disruption, pool_cost, pool_disruption): + violations.append((k, price, pod_config)) + return violations + + +# ─── P5: Single-node pool replaces ─── + +def check_p5(): + """For each k, find all approved replaces in single-node pools.""" + results = defaultdict(list) + for k in K_VALUES: + for price in PRICES: + for pod_config in pod_configs(max_pods=4): + if not pod_config: + continue # empty node, trivial + move_disruption = sum(pod_config) + pool_cost = price + pool_disruption = move_disruption + for np in PRICES: + if np >= price: + continue + savings = price - np + if approved(k, savings, move_disruption, pool_cost, pool_disruption): + s = score(savings, move_disruption, pool_cost, pool_disruption) + results[k].append({ + 'price': price, 'replacement': np, + 'pods': pod_config, 'disruption': move_disruption, + 'savings_pct': 100 * savings / price, + 'score': s, + }) + return results + + +# ─── P6: Skewed disruption cost ─── + +def check_p6(): + """Find pools where high-disruption node is rejected but low-disruption is approved.""" + examples = [] + for k in [1, 2, 3]: + for n_nodes in range(2, 5): + for price in PRICES: + for d_high_pods in pod_configs(max_pods=4): + for d_low_pods in pod_configs(max_pods=4): + d_high = sum(d_high_pods) + d_low = sum(d_low_pods) + if d_high <= d_low or d_low == 0: + continue + other_disruption = (n_nodes - 1) * 4 * 1 # other nodes: 4 default pods + pool_cost = n_nodes * price + # Both nodes are in the pool + pool_disruption = d_high + d_low + (n_nodes - 2) * 4 * 1 if n_nodes > 2 else d_high + d_low + app_high = approved(k, price, d_high, pool_cost, pool_disruption) + app_low = approved(k, price, d_low, pool_cost, pool_disruption) + if app_low and not app_high: + examples.append({ + 'k': k, 'price': price, 'n_nodes': n_nodes, + 'high_pods': d_high_pods, 'low_pods': d_low_pods, + 'd_high': d_high, 'd_low': d_low, + }) + if len(examples) >= 3: + return examples + return examples + + +# ─── P7: Fleet size independence ─── + +def check_p7(): + """For uniform pools, check that the decision doesn't change with pool size.""" + violations = [] + for k in K_VALUES: + for price in PRICES: + for pod_config in pod_configs(max_pods=4): + if not pod_config: + continue + move_disruption = sum(pod_config) + for np in PRICES: + if np >= price: + continue + savings = price - np + decisions = {} + for n_nodes in range(1, 7): + pool_cost = n_nodes * price + pool_disruption = n_nodes * move_disruption + decisions[n_nodes] = approved(k, savings, move_disruption, pool_cost, pool_disruption) + vals = set(decisions.values()) + if len(vals) > 1: + violations.append((k, price, np, pod_config, decisions)) + return violations + + +# ─── P8: Churn chains ─── + +def check_p8(): + """Find the longest replace chain via exhaustive DFS over all approved next-steps. + + At each step, every cheaper price that the scoring formula approves is a valid + next node in the chain. DFS explores all of them and returns the true maximum + chain length — not just the greedy "least aggressive step" path, which could + miss longer cross-family zigzag paths.""" + results = defaultdict(list) + for k in K_VALUES: + for pod_config in pod_configs(max_pods=4): + if not pod_config: + continue + move_disruption = sum(pod_config) + for n_nodes in range(1, 5): + for start_price in PRICES: + other_cost = (n_nodes - 1) * start_price + other_disruption = (n_nodes - 1) * move_disruption + longest_chain = _dfs_longest_chain( + k, start_price, [start_price], + move_disruption, other_cost, other_disruption, + ) + if len(longest_chain) > 1: + results[k].append({ + 'n_nodes': n_nodes, 'pods': pod_config, + 'chain': longest_chain, 'steps': len(longest_chain) - 1, + }) + # Keep only the longest chains per k + for k in results: + results[k].sort(key=lambda x: -x['steps']) + results[k] = results[k][:5] + return results + + +def _dfs_longest_chain(k, current_price, chain, move_disruption, other_cost, other_disruption): + """Return the longest chain reachable from current_price via any sequence of approved replaces.""" + best = list(chain) + for np in PRICES: + if np >= current_price: + continue + savings = current_price - np + pool_cost = current_price + other_cost + pool_disruption = move_disruption + other_disruption + if approved(k, savings, move_disruption, pool_cost, pool_disruption): + candidate = _dfs_longest_chain( + k, np, chain + [np], + move_disruption, other_cost, other_disruption, + ) + if len(candidate) > len(best): + best = candidate + return best + + +# ─── Utility ─── + +def pod_configs(max_pods=4): + """Generate representative pod configurations (tuples of disruption costs).""" + configs = [()] # empty + for n in range(1, max_pods + 1): + # Don't enumerate all combos — use representative configs + for d in DCOSTS: + configs.append(tuple([d] * n)) # uniform + if n >= 2: + configs.append((1, 10)) # mixed + configs.append((1, 1, 10)) # mostly low, one high + configs.append((10, 10, 1)) # mostly high, one low + if n >= 3: + configs.append((1, 5, 10)) # spread + if n == 4: + configs.append((1, 1, 1, 10)) + configs.append((1, 2, 5, 10)) + configs.append((10, 10, 10, 1)) + return configs + + +# ─── Main ─── + +def main(): + print("=" * 70) + print("Consolidation Scoring Properties — Exhaustive Check") + print("=" * 70) + + print("\n── P1: Monotonicity in savings ──") + v = check_p1() + if v: + print(f" FAIL: {len(v)} violations found!") + for x in v[:3]: + print(f" k={x[0]} price={x[1]} approved_at={x[2]} rejected_at={x[3]} nodes={x[4]}") + else: + print(" PASS: no violations") + + print("\n── P2: Monotonicity in disruption ──") + v = check_p2() + if v: + print(f" FAIL: {len(v)} violations found!") + for x in v[:3]: + print(f" k={x[0]} price={x[1]} d_high={x[2]} d_low={x[3]} nodes={x[4]}") + else: + print(" PASS: no violations") + + print("\n── P3: Empty nodes always approved for delete ──") + v = check_p3() + if v: + print(f" FAIL: {len(v)} violations found!") + for x in v[:3]: + print(f" k={x[0]} price={x[1]} nodes={x[2]}") + else: + print(" PASS: no violations") + + print("\n── P4: Zero-savings moves never approved ──") + v = check_p4() + if v: + print(f" FAIL: {len(v)} violations found!") + for x in v[:3]: + print(f" k={x[0]} price={x[1]} pods={x[2]}") + else: + print(" PASS: no violations") + + print("\n── P5: Single-node pool replaces ──") + results = check_p5() + for k in K_VALUES: + moves = results.get(k, []) + pairs = set((m['price'], m['replacement']) for m in moves) + print(f" k={k}: {len(pairs)} unique price pairs, {len(moves)} configurations") + for m in moves[:5]: + print(f" {m['price']} → {m['replacement']} " + f"saves {m['savings_pct']:.0f}% " + f"disruption={m['disruption']} " + f"score={m['score']:.2f}") + + print("\n── P6: Skewed disruption cost ──") + examples = check_p6() + if examples: + for ex in examples[:3]: + print(f" k={ex['k']} price={ex['price']}¢ nodes={ex['n_nodes']}") + print(f" high-disruption pods {ex['high_pods']} (d={ex['d_high']}): REJECTED") + print(f" low-disruption pods {ex['low_pods']} (d={ex['d_low']}): APPROVED") + else: + print(" No examples found") + + print("\n── P7: Fleet size independence (uniform pools) ──") + v = check_p7() + if v: + print(f" FAIL: {len(v)} violations!") + for x in v[:3]: + print(f" k={x[0]} price={x[1]} replacement={x[2]} pods={x[3]}") + print(f" decisions by pool size: {x[4]}") + else: + print(" PASS: decision is identical across pool sizes 1-6") + + print("\n── P8: Churn chains (with actual post-replace pool updates) ──") + results = check_p8() + for k in K_VALUES: + chains = results.get(k, []) + if chains: + longest = chains[0] + print(f" k={k}: longest chain = {longest['steps']} steps") + for c in chains[:3]: + arrow = " → ".join(f"{p}¢" for p in c['chain']) + print(f" nodes={c['n_nodes']} pods={c['pods']} chain: {arrow}") + else: + print(f" k={k}: no churn (no replaces approved)") + + print("\n" + "=" * 70) + print("Done.") + + +if __name__ == "__main__": + main() diff --git a/designs/scripts/balanced-consolidation-ranking.py b/designs/scripts/balanced-consolidation-ranking.py new file mode 100644 index 0000000000..d868064717 --- /dev/null +++ b/designs/scripts/balanced-consolidation-ranking.py @@ -0,0 +1,475 @@ +#!/usr/bin/env python3 +""" +Generate ranking-strategies charts for the consolidation cost threshold RFC. + +Simulates a realistic cluster by: +1. Sampling pod workloads from AWS Fargate task sizes (vCPU, memory pairs) +2. Bin-packing pods onto EC2 instance types (c7i, m7i, r7i families) +3. Killing a variable fraction of pods per node to simulate scale-down +4. Generating REPLACE moves (reprovision remaining pods on cheapest instance) +5. Generating DELETE moves (scatter pods onto existing spare capacity) + +Produces two charts showing cumulative savings vs. cumulative disruption +under four ranking strategies: score, savings-only, disruption-only, random. + +Usage: + python3 designs/scripts/consolidation-cost-threshold-ranking.py + +Output: + designs/ranking-strategies-replace.png + designs/ranking-strategies-delete.png +""" + +import numpy as np +import matplotlib.pyplot as plt +from dataclasses import dataclass, field +from typing import List +from pathlib import Path + +SEED = 42 +OUTPUT_DIR = Path(__file__).parent.parent + +# --------------------------------------------------------------------------- +# EC2 instance types: c7i, m7i, r7i families (us-east-1, Linux, on-demand) +# Source: https://instances.vantage.sh +# --------------------------------------------------------------------------- +EC2_INSTANCES = [ + # c7i: compute optimized (2:1 memory:vCPU) + ("c7i.large", 2, 4, 0.0893), + ("c7i.xlarge", 4, 8, 0.1785), + ("c7i.2xlarge", 8, 16, 0.3570), + ("c7i.4xlarge", 16, 32, 0.7140), + ("c7i.8xlarge", 32, 64, 1.4280), + ("c7i.12xlarge", 48, 96, 2.1420), + ("c7i.16xlarge", 64, 128, 2.8560), + ("c7i.24xlarge", 96, 192, 4.2840), + # m7i: general purpose (4:1 memory:vCPU) + ("m7i.large", 2, 8, 0.1008), + ("m7i.xlarge", 4, 16, 0.2016), + ("m7i.2xlarge", 8, 32, 0.4032), + ("m7i.4xlarge", 16, 64, 0.8064), + ("m7i.8xlarge", 32, 128, 1.6128), + ("m7i.12xlarge", 48, 192, 2.4190), + ("m7i.16xlarge", 64, 256, 3.2260), + ("m7i.24xlarge", 96, 384, 4.8384), + ("m7i.48xlarge",192, 768, 9.6768), + # r7i: memory optimized (8:1 memory:vCPU) + ("r7i.large", 2, 16, 0.1323), + ("r7i.xlarge", 4, 32, 0.2646), + ("r7i.2xlarge", 8, 64, 0.5292), + ("r7i.4xlarge", 16, 128, 1.0584), + ("r7i.8xlarge", 32, 256, 2.1168), + ("r7i.12xlarge", 48, 384, 3.1750), + ("r7i.16xlarge", 64, 512, 4.2336), + ("r7i.24xlarge", 96, 768, 6.3500), + ("r7i.48xlarge",192, 1536, 12.7008), +] + +# Fixed overhead of consolidating any node (independent of pod count) +NODE_BASELINE_DISRUPTION = 1.0 + + +@dataclass +class Pod: + cpu: float + mem: float + disruption_cost: float = 1.0 + + +@dataclass +class Node: + name: str + cpu_cap: float + mem_cap: float + cost_per_hr: float + pods: list = field(default_factory=list) + + @property + def cpu_used(self): + return sum(p.cpu for p in self.pods) + + @property + def mem_used(self): + return sum(p.mem for p in self.pods) + + def fits(self, pod): + return (pod.cpu <= self.cpu_cap - self.cpu_used and + pod.mem <= self.mem_cap - self.mem_used) + + @property + def disruption_cost(self): + return NODE_BASELINE_DISRUPTION + sum( + max(0, p.disruption_cost) for p in self.pods) + + +def find_cheapest_fitting(cpu_needed, mem_needed): + """Find the cheapest EC2 instance that fits the given resources.""" + best = None + for name, vcpu, mem, cost in EC2_INSTANCES: + if vcpu >= cpu_needed and mem >= mem_needed: + if best is None or cost < best[3]: + best = (name, vcpu, mem, cost) + return best + + +def cheapest_instance_for(pods): + """Find the cheapest instance type that fits all pods.""" + total_cpu = sum(p.cpu for p in pods) + total_mem = sum(p.mem for p in pods) + return find_cheapest_fitting(total_cpu, total_mem) + + +def build_cluster(rng, n_pods=20000): + """ + Build a cluster by bin-packing pods onto EC2 instances. + + Uses first-fit-decreasing: sort all pods by resource footprint (cpu * mem) + descending, then greedily place each pod onto the first existing node that + fits. When no node fits, provision a new one -- picking the cheapest + instance that fits the pod, but upsizing to a ~4x bigger instance if it + costs less than 2x as much. This produces denser packing and a more + realistic instance type distribution. + """ + # Generate pods with random resource requests. CPU and memory are drawn + # independently from log-normal distributions, producing a realistic mix + # of compute-heavy, memory-heavy, and balanced pods. This naturally + # spreads pods across different instance families (c7i for compute-heavy, + # r7i for memory-heavy, m7i for balanced), creating cost diversity. + cpu_raw = rng.lognormal(mean=0.0, sigma=1.0, size=n_pods) + mem_raw = rng.lognormal(mean=1.5, sigma=1.2, size=n_pods) + + # Clamp to reasonable ranges and round to Fargate-like granularity + all_pods = [] + for c, m in zip(cpu_raw, mem_raw): + cpu = float(np.clip(round(c * 2) / 2, 0.25, 16)) # 0.25 to 16, step 0.5 + mem = float(np.clip(round(m), 1, 120)) # 1 to 120 GiB, step 1 + dc = float(rng.choice([0, 1, 1, 1, 1, 1, 10])) + all_pods.append(Pod(cpu=cpu, mem=mem, disruption_cost=dc)) + + # First-fit-decreasing bin packing: sort all pods by resource footprint + # descending, then greedily place each onto the first existing node that + # fits. When no node fits, provision the cheapest instance that can hold + # the pod. This mirrors Karpenter's provisioning behavior. + all_pods.sort(key=lambda p: p.cpu * p.mem, reverse=True) + + nodes = [] + for pod in all_pods: + placed = False + for node in nodes: + if node.fits(pod): + node.pods.append(pod) + placed = True + break + if not placed: + chosen = find_cheapest_fitting(pod.cpu, pod.mem) + if chosen is None: + raise ValueError( + f"No instance fits pod ({pod.cpu}, {pod.mem})") + new_node = Node( + name=chosen[0], + cpu_cap=chosen[1], mem_cap=chosen[2], + cost_per_hr=chosen[3], + ) + new_node.pods.append(pod) + nodes.append(new_node) + + return nodes + + +def churn_pods(rng, nodes): + """Simulate workload churn: kill some pods, add some new ones. + + For each node, kill 0-80% of pods (simulating scale-down or + redeployment), then add 0-3 new randomly-sized pods if they fit + (simulating new deployments landing on nodes with spare capacity). + This changes the resource mix on each node over time, creating + mismatches between the node's instance type and its actual workload + -- exactly the situation that consolidation is designed to fix. + """ + for node in nodes: + # Kill phase: remove a random fraction of pods + if len(node.pods) > 1: + kill_fraction = rng.uniform(0.0, 0.8) + n_kill = int(len(node.pods) * kill_fraction) + if n_kill > 0: + kill_indices = set( + rng.choice(len(node.pods), size=n_kill, replace=False)) + node.pods = [p for i, p in enumerate(node.pods) + if i not in kill_indices] + + # Add phase: place 0-3 new pods if they fit + n_add = rng.integers(0, 4) + for _ in range(n_add): + cpu = float(np.clip(round(rng.lognormal(0.0, 1.0) * 2) / 2, 0.25, 16)) + mem = float(np.clip(round(rng.lognormal(1.5, 1.2)), 1, 120)) + dc = float(rng.choice([0, 1, 1, 1, 1, 1, 10])) + pod = Pod(cpu=cpu, mem=mem, disruption_cost=dc) + if node.fits(pod): + node.pods.append(pod) + + +def generate_replace_moves(rng, nodes): + """ + For each node, find the cheapest instance that fits its current pods. + If cheaper than the current node, that's a REPLACE move. + """ + moves = [] + nodepool_cost = sum(n.cost_per_hr for n in nodes) + nodepool_disruption = sum(n.disruption_cost for n in nodes) + + for node in nodes: + if len(node.pods) == 0: + continue + + result = cheapest_instance_for(node.pods) + if result is None: + continue + + _, _, _, new_cost = result + savings = node.cost_per_hr - new_cost + disruption = node.disruption_cost + + if savings <= 0 or disruption <= 0: + continue + if nodepool_cost <= 0 or nodepool_disruption <= 0: + continue + + savings_frac = savings / nodepool_cost + disruption_frac = disruption / nodepool_disruption + score = savings_frac / disruption_frac + + moves.append({ + "savings": savings, + "disruption": disruption, + "savings_frac": savings_frac, + "disruption_frac": disruption_frac, + "score": score, + }) + + return moves + + +def generate_delete_moves(rng, nodes): + """ + For each node, check if its pods could fit on other nodes' spare capacity. + If so, generate a DELETE move (full node cost saved, all pods disrupted). + """ + moves = [] + nodepool_cost = sum(n.cost_per_hr for n in nodes) + nodepool_disruption = sum(n.disruption_cost for n in nodes) + + for i, node in enumerate(nodes): + if len(node.pods) == 0: + continue + + # Check if all pods fit on spare capacity of other nodes + sorted_pods = sorted(node.pods, key=lambda p: (p.cpu, p.mem), + reverse=True) + + remaining = {} + for j in range(len(nodes)): + if j != i: + remaining[j] = ( + nodes[j].cpu_cap - nodes[j].cpu_used, + nodes[j].mem_cap - nodes[j].mem_used, + ) + + can_fit = True + for pod in sorted_pods: + best_j = None + best_waste = float("inf") + for j, (cpu_f, mem_f) in remaining.items(): + if cpu_f >= pod.cpu and mem_f >= pod.mem: + waste = (cpu_f - pod.cpu) + (mem_f - pod.mem) + if waste < best_waste: + best_waste = waste + best_j = j + if best_j is not None: + cpu_f, mem_f = remaining[best_j] + remaining[best_j] = (cpu_f - pod.cpu, mem_f - pod.mem) + else: + can_fit = False + break + + if not can_fit: + continue + + savings = node.cost_per_hr + disruption = node.disruption_cost + + if savings <= 0 or disruption <= 0: + continue + if nodepool_cost <= 0 or nodepool_disruption <= 0: + continue + + savings_frac = savings / nodepool_cost + disruption_frac = disruption / nodepool_disruption + score = savings_frac / disruption_frac + + moves.append({ + "savings": savings, + "disruption": disruption, + "savings_frac": savings_frac, + "disruption_frac": disruption_frac, + "score": score, + }) + + return moves + + +def plot_ranking(moves, title, output_path): + """Plot cumulative savings vs disruption for four ranking strategies.""" + if not moves: + print(f" No moves for {title}, skipping") + return + + rng = np.random.default_rng(SEED + 1) + n = len(moves) + + savings = np.array([m["savings_frac"] for m in moves]) + disruption = np.array([m["disruption_frac"] for m in moves]) + scores = np.array([m["score"] for m in moves]) + + strategies = { + "Score (benefit/cost)": np.argsort(-scores), + "Savings only": np.argsort(-savings), + "Disruption only (asc)": np.argsort(disruption), + "Random": rng.permutation(n), + } + + fig, ax = plt.subplots(figsize=(8, 6)) + + for label, order in strategies.items(): + cum_disruption = np.cumsum(disruption[order]) + cum_savings = np.cumsum(savings[order]) + if cum_disruption[-1] > 0: + cum_disruption = cum_disruption / cum_disruption[-1] + if cum_savings[-1] > 0: + cum_savings = cum_savings / cum_savings[-1] + ax.plot(cum_disruption, cum_savings, label=label, linewidth=2) + + ax.set_xlabel("Cumulative disruption (fraction of total)") + ax.set_ylabel("Cumulative savings (fraction of total)") + ax.set_title(title) + ax.legend(loc="lower right") + ax.set_xlim(0, 1) + ax.set_ylim(0, 1) + ax.set_aspect("equal") + ax.grid(True, alpha=0.3) + + fig.tight_layout() + fig.savefig(output_path, dpi=150) + print(f" Saved {output_path}") + + +def _plot_panel(ax, moves, title): + """Plot one panel of cumulative savings vs disruption.""" + rng = np.random.default_rng(SEED + 1) + n = len(moves) + + savings = np.array([m["savings_frac"] for m in moves]) + disruption = np.array([m["disruption_frac"] for m in moves]) + scores = np.array([m["score"] for m in moves]) + + strategies = { + "Score (benefit/cost)": np.argsort(-scores), + "Savings only": np.argsort(-savings), + "Disruption only (asc)": np.argsort(disruption), + "Random": rng.permutation(n), + } + + for label, order in strategies.items(): + cum_disruption = np.cumsum(disruption[order]) + cum_savings = np.cumsum(savings[order]) + if cum_disruption[-1] > 0: + cum_disruption = cum_disruption / cum_disruption[-1] + if cum_savings[-1] > 0: + cum_savings = cum_savings / cum_savings[-1] + ax.plot(cum_disruption, cum_savings, label=label, linewidth=2) + + ax.set_xlabel("Cumulative disruption (fraction of total)") + ax.set_ylabel("Cumulative savings (fraction of total)") + ax.set_title(title) + ax.legend(loc="lower right", fontsize=8) + ax.set_xlim(0, 1) + ax.set_ylim(0, 1) + ax.set_aspect("equal") + ax.grid(True, alpha=0.3) + + +def plot_ranking_sidebyside(replace_moves, delete_moves, output_path): + """Plot REPLACE and DELETE ranking charts side by side.""" + fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(14, 6)) + + if replace_moves: + _plot_panel(ax1, replace_moves, "REPLACE moves") + if delete_moves: + _plot_panel(ax2, delete_moves, "DELETE moves") + + fig.tight_layout() + fig.savefig(output_path, dpi=150) + print(f" Saved {output_path}") + + +def main(): + rng = np.random.default_rng(SEED) + + print("Building cluster...") + nodes = build_cluster(rng, n_pods=5000) + total_pods = sum(len(n.pods) for n in nodes) + print(f" {len(nodes)} nodes, {total_pods} pods, " + f"{total_pods/len(nodes):.1f} pods/node") + print(f" NodePool cost: ${sum(n.cost_per_hr for n in nodes):.2f}/hr") + + # Run multiple rounds of churn to accumulate consolidation candidates. + # Each round kills some pods and adds new ones, then we collect moves. + # This simulates a cluster that has been running for a while with + # ongoing deployments and scale-downs. + all_replace_moves = [] + all_delete_moves = [] + n_rounds = 10 + print(f"Simulating {n_rounds} rounds of workload churn...") + for round_num in range(n_rounds): + churn_pods(rng, nodes) + nodes = [n for n in nodes if len(n.pods) > 0] + replace_moves = generate_replace_moves(rng, nodes) + delete_moves = generate_delete_moves(rng, nodes) + all_replace_moves.extend(replace_moves) + all_delete_moves.extend(delete_moves) + if round_num == 0 or round_num == n_rounds - 1: + total_pods = sum(len(n.pods) for n in nodes) + print(f" Round {round_num+1}: {len(nodes)} nodes, {total_pods} pods, " + f"{len(replace_moves)} REPLACE, {len(delete_moves)} DELETE") + + replace_moves = all_replace_moves + delete_moves = all_delete_moves + print(f" Total: {len(replace_moves)} REPLACE, {len(delete_moves)} DELETE") + + # Combine all moves -- this is what the consolidation controller evaluates + all_moves = replace_moves + delete_moves + print(f" Combined: {len(all_moves)} moves") + + print("Plotting...") + plot_ranking_sidebyside( + replace_moves, delete_moves, + OUTPUT_DIR / "ranking-strategies.png", + ) + + # Write CSVs for inspection + for label, moves in [("all", all_moves), + ("replace", replace_moves), + ("delete", delete_moves)]: + csv_path = Path(__file__).parent / f"ranking-strategies-{label}.csv" + with open(csv_path, "w") as f: + f.write("savings_per_hr,disruption,savings_frac,disruption_frac,score\n") + for m in sorted(moves, key=lambda m: -m["score"]): + f.write(f"{m['savings']:.4f},{m['disruption']:.1f}," + f"{m['savings_frac']:.6f},{m['disruption_frac']:.6f}," + f"{m['score']:.4f}\n") + print(f" Saved {csv_path}") + + print("Done.") + + +if __name__ == "__main__": + main() From e078d08c03f32dcb5bd186e36792682f3645d160 Mon Sep 17 00:00:00 2001 From: James Thompson Date: Mon, 20 Apr 2026 15:48:50 -0700 Subject: [PATCH 2/3] Fold consolidationThreshold into consolidationPolicy as IntOrString consolidationPolicy is now an IntOrString field per ellistarn's feedback. Balanced maps to k=2. Integer values (1-3) pass k directly as an escape hatch. Removes the separate consolidationThreshold field. --- designs/balanced-consolidation.md | 49 ++++++++++++++++--------------- 1 file changed, 25 insertions(+), 24 deletions(-) diff --git a/designs/balanced-consolidation.md b/designs/balanced-consolidation.md index 45c84f4e75..342c5e05b1 100644 --- a/designs/balanced-consolidation.md +++ b/designs/balanced-consolidation.md @@ -12,7 +12,7 @@ Terminating a non-empty node requires evicting running pods and starting replace - `consolidateAfter` not preventing disruption of well-packed nodes ([kubernetes-sigs#2705](https://github.com/kubernetes-sigs/karpenter/issues/2705), [aws#3577](https://github.com/aws/karpenter-provider-aws/issues/3577)) - Direct requests for a savings threshold or utilization-based consolidation gating ([kubernetes-sigs#2883](https://github.com/kubernetes-sigs/karpenter/issues/2883), [kubernetes-sigs#1440](https://github.com/kubernetes-sigs/karpenter/issues/1440), [kubernetes-sigs#1686](https://github.com/kubernetes-sigs/karpenter/issues/1686), [kubernetes-sigs#1430](https://github.com/kubernetes-sigs/karpenter/issues/1430), [aws#5218](https://github.com/aws/karpenter-provider-aws/issues/5218)) -This RFC calls each consolidation action a *move*. A move deletes one or more nodes, along with pod eviction and optional replacement node creation. We propose a new `consolidationPolicy` value, `Balanced`, that scores each move and rejects moves where the disruption outweighs the savings. A `consolidationThreshold` parameter (default 2) controls the tradeoff. +This RFC calls each consolidation action a *move*. A move deletes one or more nodes, along with pod eviction and optional replacement node creation. We propose new `consolidationPolicy` values that score each move and reject moves where the disruption outweighs the savings. `Balanced` (k=2) is the recommended default. Integer values (1-3) provide direct control over the threshold. ## Alternatives Considered @@ -57,27 +57,26 @@ metadata: spec: disruption: consolidationPolicy: Balanced - consolidationThreshold: 2 consolidateAfter: 30s budgets: - nodes: 10% ``` -`Balanced` scores each consolidation move and approves it when `score >= 1/consolidationThreshold`. +`consolidationPolicy` is an `IntOrString` field. Named string values and integer values sit on a spectrum from conservative to aggressive: -The three consolidation policies sit on a spectrum from conservative to aggressive: +| Value | k | Behavior | +|---|---|---| +| `WhenEmpty` | — | Only empty nodes (emptiness controller, no scoring) | +| `1` | 1 | Scoring with break-even threshold (deletes only, no replaces in uniform pools) | +| `Balanced` | 2 | Scoring with default threshold (within-family replaces viable) | +| `3` | 3 | Scoring with aggressive threshold (adds cross-family replace pairs) | +| `WhenEmptyOrUnderutilized` | — | Any positive savings (existing behavior, no scoring) | -| Policy | Behavior | -|---|---| -| `WhenEmpty` | Only empty nodes (emptiness controller, no scoring) | -| `Balanced` | Savings must justify disruption | -| `WhenEmptyOrUnderutilized` | Any positive savings | +`Balanced` is shorthand for k=2. An integer value uses the scoring formula directly with that k. `WhenEmpty` and `WhenEmptyOrUnderutilized` are implemented by their existing controllers and do not use the scoring formula. Validation rejects integers outside 1-3. -`WhenEmpty` and `WhenEmptyOrUnderutilized` are implemented by their existing controllers. `Balanced` uses the scoring formula. The spectrum is conceptual — `WhenEmpty` and `WhenEmptyOrUnderutilized` are not special cases of the formula. They remain separate code paths. +A move is approved when `score >= 1/k`. At the default `Balanced` (k=2), `score >= 0.5`. -`consolidationThreshold` controls how aggressively `Balanced` consolidates. Higher values approve more moves. At the default of 2, a move passes when its disruption fraction is at most 2x its savings fraction. The parameter accepts integer values 1, 2, or 3. Validation rejects values outside this range and `consolidationThreshold` without `consolidationPolicy: Balanced`. - -If an operator enables `BalancedConsolidation`, sets `consolidationPolicy: Balanced`, then disables the feature gate during rollback, the controller falls back to `WhenEmptyOrUnderutilized` behavior and sets a `ConsolidationPolicyUnsupported` status condition on the NodePool. The condition message directs the operator to change the policy or re-enable the gate. This avoids reconcile failures while making the fallback visible. +If an operator enables `BalancedConsolidation`, sets `consolidationPolicy: Balanced` (or an integer), then disables the feature gate during rollback, the controller falls back to `WhenEmptyOrUnderutilized` behavior and sets a `ConsolidationPolicyUnsupported` status condition on the NodePool. The condition message directs the operator to change the policy or re-enable the gate. This avoids reconcile failures while making the fallback visible. ### How Scoring Works @@ -125,7 +124,7 @@ score = savings_fraction / disruption_fraction `evicted_pods` is all pods on deleted source nodes. Every pod is evicted regardless of where it lands. The disruption cost counts every eviction. -A move is approved when `score >= 1/k`, where k is `consolidationThreshold` (default 2). At k=2, `score >= 0.5`. Both sides are dimensionless fractions, so the score is scale-invariant (see [Scale Invariance](#scale-invariance)). +A move is approved when `score >= 1/k`, where k comes from `consolidationPolicy` (`Balanced` = 2, or an integer value directly). At k=2, `score >= 0.5`. Both sides are dimensionless fractions, so the score is scale-invariant (see [Scale Invariance](#scale-invariance)). **Division-by-zero handling.** With the per-node disruption cost of 1.0, `disruption_cost` is always positive for any node, eliminating the zero-disruption special case. The remaining edge cases: @@ -191,8 +190,8 @@ Existing feasibility checks (disruption budgets, PDBs, `consolidateAfter`, `do-n Approved and rejected moves are surfaced as events. Single-node moves emit on the NodeClaim. Multi-node moves emit on the NodePool (the score describes the move, not any single node). -- `ConsolidationApproved`: `"score %.2f >= threshold %.2f (consolidationThreshold: %.1f, savings %.1f%%, disruption %.1f%%)"` -- `ConsolidationRejected`: `"score %.2f < threshold %.2f (consolidationThreshold: %.1f, savings %.1f%%, disruption %.1f%%)"` +- `ConsolidationApproved`: `"score %.2f >= threshold %.2f (k: %d, savings %.1f%%, disruption %.1f%%)"` +- `ConsolidationRejected`: `"score %.2f < threshold %.2f (k: %d, savings %.1f%%, disruption %.1f%%)"` Scored moves are also logged at DEBUG level. @@ -202,7 +201,7 @@ How to use these: if `moves_total{decision="rejected"}` is high and the histogra ## Examples -All examples use the default `consolidationThreshold` of 2. The NodePool has 10 nodes: eight m7i.xlarge (4 vCPU, 16 GiB, $4.84/day) and two m7i.2xlarge (8 vCPU, 32 GiB, $9.68/day). Total NodePool cost is $58.08/day. The NodePool runs 80 pods with total disruption cost 80. +All examples use `consolidationPolicy: Balanced` (k=2). The NodePool has 10 nodes: eight m7i.xlarge (4 vCPU, 16 GiB, $4.84/day) and two m7i.2xlarge (8 vCPU, 32 GiB, $9.68/day). Total NodePool cost is $58.08/day. The NodePool runs 80 pods with total disruption cost 80. ### Oversized Node (approved) @@ -320,7 +319,7 @@ The move is approved. To reach the 0.5 boundary, each of the 8 pods would need d ## Why k=2 -The scoring formula has one free parameter: `consolidationThreshold` (k). We chose k=2 by exhaustive enumeration. +The scoring formula has one free parameter: k (exposed via `consolidationPolicy`). We chose k=2 as the default by exhaustive enumeration. ### State Space @@ -357,19 +356,21 @@ At k=1, no replace is ever approved in a uniform pool. The score for a uniform-p k=2 is the smallest integer where uniform-pool REPLACEs pass. Within a single family, prices follow power-of-2 scaling, so every replacement ratio is 0.5 or less and k>=3 adds nothing. Across families, k=3 opens 8 additional cross-family pairs (e.g., c7i.large → m7i.medium at 43% savings, score 0.43) without increasing the max churn chain. k=4 opens 9 more pairs but allows 9-step churn chains that zigzag through all three families. Max chain lengths are confirmed by exhaustive DFS over all approved replacement paths, not a greedy heuristic (see [`balanced-consolidation-properties.py`](scripts/balanced-consolidation-properties.py)). -k=2 is the right default. It is the smallest value that makes within-family REPLACEs viable, and it captures all cross-family pairs where the replacement costs less than half the original. The 8 additional cross-family pairs at k=3 are available to operators who set `consolidationThreshold: 3` (see [`balanced-consolidation-properties.py`](scripts/balanced-consolidation-properties.py)). +k=2 is the right default. It is the smallest value that makes within-family REPLACEs viable, and it captures all cross-family pairs where the replacement costs less than half the original. The 8 additional cross-family pairs at k=3 are available to operators who set `consolidationPolicy: 3` (see [`balanced-consolidation-properties.py`](scripts/balanced-consolidation-properties.py)). ## API Choices -### Consolidation Aggressiveness Tuning [Recommended: consolidationThreshold] +### Consolidation Aggressiveness Tuning [Recommended: IntOrString consolidationPolicy] + +`consolidationPolicy` is an `IntOrString` field. `Balanced` maps to k=2. Integer values (1-3) pass k directly. This gives most users a single named value while preserving an escape hatch for operators who need a different threshold. -`consolidationThreshold` exposes k directly. A move passes when its disruption fraction is at most k times its savings fraction. Higher k approves more moves. The formally motivated values are all integers: k=1 (break-even, deletes only), k=2 (within-family replaces viable, 4-step max churn), k=3 (adds cross-family pairs, same 4-step churn). At k=4, churn chains jump to 9 steps and the formal analysis argues against higher values. The field accepts integers 1, 2, or 3. +The formally motivated values are all integers: k=1 (break-even, deletes only), k=2 (within-family replaces viable, 4-step max churn), k=3 (adds cross-family pairs, same 4-step churn). At k=4, churn chains jump to 9 steps and the formal analysis argues against higher values. Two alternatives were considered: -**Named presets (Low/Medium/High).** A `consolidationAggressiveness` enum mapping to k values (e.g., Low=1, Medium=2, High=3). Karpenter does not have an existing ordinal enum pattern, and picking names that age well is hard. "Conservative/Balanced/Aggressive" reuses "Balanced" which is already the policy name. Not preferred because the integer is simpler. +**Separate `consolidationThreshold` field.** A dedicated integer field alongside `consolidationPolicy: Balanced`. This works but adds a field that only applies to one policy value. Folding k into `consolidationPolicy` keeps the API surface smaller. -**Continuous slider (0-100).** A percentage-based field mapping to a log-scale threshold. This adds a nonlinear transformation that obscures the underlying math. Not preferred. +**Named presets (Low/Medium/High).** A `consolidationAggressiveness` enum mapping to k values. Karpenter does not have an existing ordinal enum pattern, and picking names that age well is hard. "Conservative/Balanced/Aggressive" reuses "Balanced" which is already the policy name. IntOrString is simpler. ### Per-NodePool vs. Per-Cluster Normalization [Recommended: Per-NodePool] @@ -379,7 +380,7 @@ Per-NodePool normalization matches Karpenter's existing architecture (policies, Con: scores reflect relative efficiency within a pool, not absolute dollar impact. A score of 2.0 in a $50/hr pool and 2.0 in a $10,000/hr pool look identical. This does not affect behavior but limits cross-pool comparison. -### New consolidationPolicy Value with consolidationThreshold +### consolidationPolicy Naming No behavior change for existing users. Migration: change `WhenEmptyOrUnderutilized` to `Balanced`. From 6d29106598629a7d89a218729785c22aefe2d56d Mon Sep 17 00:00:00 2001 From: James Thompson Date: Mon, 20 Apr 2026 15:59:10 -0700 Subject: [PATCH 3/3] Unify all consolidation policies under disruption cost model consolidationPolicy is now IntOrString. All policies expressed through the disruption cost model: - WhenEmpty: approve only when move disruption cost equals per-node disruption cost (no pod contributes positive disruption cost). Behavioral change from today: pods with large negative pod-deletion-cost no longer block consolidation. - Balanced (k=2): scoring with default threshold - Integer values (1-3): pass k directly as escape hatch - WhenEmptyOrUnderutilized: k=+inf (any positive savings) Removes separate consolidationThreshold field per ellistarn feedback. --- designs/balanced-consolidation.md | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/designs/balanced-consolidation.md b/designs/balanced-consolidation.md index 342c5e05b1..b2cad2432a 100644 --- a/designs/balanced-consolidation.md +++ b/designs/balanced-consolidation.md @@ -62,17 +62,19 @@ spec: - nodes: 10% ``` -`consolidationPolicy` is an `IntOrString` field. Named string values and integer values sit on a spectrum from conservative to aggressive: +`consolidationPolicy` is an `IntOrString` field. All values are expressed through the disruption cost model: -| Value | k | Behavior | -|---|---|---| -| `WhenEmpty` | — | Only empty nodes (emptiness controller, no scoring) | -| `1` | 1 | Scoring with break-even threshold (deletes only, no replaces in uniform pools) | -| `Balanced` | 2 | Scoring with default threshold (within-family replaces viable) | -| `3` | 3 | Scoring with aggressive threshold (adds cross-family replace pairs) | -| `WhenEmptyOrUnderutilized` | — | Any positive savings (existing behavior, no scoring) | +| Value | Behavior | +|---|---| +| `WhenEmpty` | Approve only when move disruption cost equals the per-node disruption cost (no pod contributes positive disruption cost) | +| `1` | Scoring with break-even threshold (deletes only, no replaces in uniform pools) | +| `Balanced` | Scoring with k=2 (within-family replaces viable) | +| `3` | Scoring with k=3 (adds cross-family replace pairs) | +| `WhenEmptyOrUnderutilized` | Any positive savings (k=+inf) | -`Balanced` is shorthand for k=2. An integer value uses the scoring formula directly with that k. `WhenEmpty` and `WhenEmptyOrUnderutilized` are implemented by their existing controllers and do not use the scoring formula. Validation rejects integers outside 1-3. +`Balanced` is shorthand for k=2. An integer value uses the scoring formula directly with that k. `WhenEmptyOrUnderutilized` is equivalent to k=+inf (any move with positive savings passes). Validation rejects integers outside 1-3. + +`WhenEmpty` approves a move when its disruption cost equals the per-node disruption cost, meaning no pod on the candidate node has positive disruption cost. This is a behavioral change from today's `WhenEmpty`, which checks for literally zero pods. Under the new definition, a node whose pods all have large negative `pod-deletion-cost` (driving their disruption cost to 0 after clamping) qualifies as "empty" for consolidation purposes. This is an improvement: pods that declared themselves free to disrupt should not block consolidation. A move is approved when `score >= 1/k`. At the default `Balanced` (k=2), `score >= 0.5`.