Skip to content

feat: add topologyMinDomains opt-in field to force AZ spreading with Karpenter/Cluster Autoscaler - #330

Open
gangavh1008 wants to merge 2 commits into
ClickHouse:mainfrom
gangavh1008:feature/topology-min-domains
Open

gangavh1008 wants to merge 2 commits into
ClickHouse:mainfrom
gangavh1008:feature/topology-min-domains

Conversation

@gangavh1008

Copy link
Copy Markdown

Problem

The topologyZoneKey shorthand generates a TopologySpreadConstraint with maxSkew: 1 and whenUnsatisfiable: DoNotSchedule. This is the right setup for spreading pods across availability zones — but with demand-driven autoscalers (Karpenter, Cluster Autoscaler), the constraint alone is insufficient.

The Kubernetes scheduler counts only topology domains (AZs) that already have eligible nodes when evaluating spread. If nodes exist in only two AZs at scheduling time, the scheduler considers the constraint satisfied with a 2/1 split — the autoscaler never receives a signal to provision a node in the third AZ. The result: one replica is stuck permanently unspread, and the cluster loses a zone of fault tolerance without any warning.

This is a known Kubernetes limitation addressed by the minDomains field introduced in KEP-3022 (beta in Kubernetes 1.25, GA in 1.28).

Solution

Setting minDomains on the TopologySpreadConstraint tells the scheduler to treat N zones as topology domains even if nodes don't exist in all of them. This makes the constraint unsatisfiable until the autoscaler provisions into all required zones.

Change

This PR adds an opt-in topologyMinDomains field (*int32) to PodTemplateSpec. When set, it is forwarded to minDomains on the TopologySpreadConstraint generated by topologyZoneKey — for both ClickHouseCluster (per-shard constraint) and KeeperCluster.

Disabled by default: when topologyMinDomains is nil (the default), no minDomains is set and behaviour is completely unchanged.

Usage

podTemplate:
  topologyZoneKey: topology.kubernetes.io/zone
  topologyMinDomains: 3   # set to number of AZs in your cluster

API change (api/v1alpha1/common.go)

// TopologyMinDomains sets the minDomains field on the TopologySpreadConstraint generated by TopologyZoneKey.
// The Kubernetes scheduler counts only zones that already have eligible nodes as topology domains.
// With demand-driven autoscalers (e.g. Karpenter, Cluster Autoscaler) this means that if nodes
// exist in only two AZs at scheduling time, a maxSkew:1/DoNotSchedule constraint is satisfied with
// a 2/1 split and the autoscaler never provisions a node in the third AZ.
// Setting TopologyMinDomains to the expected number of zones (typically 3) tells the scheduler to
// treat that many zones as domains regardless of whether nodes exist in all of them, forcing the
// autoscaler to provision into the missing zone.
// Only takes effect when TopologyZoneKey is set. Omit (nil) to preserve the default behaviour.
// Requires Kubernetes 1.28+ (GA); available behind a feature gate from 1.25+.
// +optional
TopologyMinDomains *int32 `json:"topologyMinDomains,omitempty"`

Tests

New test blocks for both ClickHouseCluster and KeeperCluster covering:

  • No constraint generated when topologyZoneKey is unset (baseline — no change)
  • Constraint generated without minDomains when topologyMinDomains is nil (baseline — no change)
  • minDomains forwarded correctly when the field is set
  • Per-shard label selector scoping for ClickHouseCluster (each shard's replicas spread independently)

Compatibility

  • No breaking change. topologyMinDomains defaults to nil; omitting it preserves all existing behaviour.
  • Requires Kubernetes 1.28+ for minDomains GA. Clusters on earlier versions should leave topologyMinDomains unset or ensure the MinDomainsInPodTopologySpread feature gate is enabled (available from 1.25+).

@CLAassistant

CLAassistant commented Sep 13, 2026

Copy link
Copy Markdown

CLA assistant check
All committers have signed the CLA.

With demand-driven autoscalers (Karpenter, Cluster Autoscaler), the Kubernetes
scheduler counts only topology domains (AZs) that already have eligible nodes.
When topologyZoneKey generates a maxSkew:1/DoNotSchedule constraint and nodes
exist in only two AZs at scheduling time, the constraint is satisfied with a
2/1 split — the autoscaler never provisions into a third AZ, leaving one replica
unspread.

Setting minDomains on the TopologySpreadConstraint tells the scheduler to treat
N zones as domains regardless of whether nodes currently exist in all of them,
forcing the autoscaler to provision into the missing zone.

This commit adds an opt-in TopologyMinDomains *int32 field to PodTemplateSpec.
When nil (the default), behaviour is unchanged. When set (e.g. 3 for a
three-AZ cluster), minDomains is forwarded to the generated constraint for
both ClickHouseCluster and KeeperCluster.

Example usage:

  podTemplate:
    topologyZoneKey: topology.kubernetes.io/zone
    topologyMinDomains: 3
@gangavh1008
gangavh1008 force-pushed the feature/topology-min-domains branch from 1f5da84 to 062fa0d Compare September 13, 2026 23:56
…docs

Fixes golangci-lint wsl_v5 failures: adds required blank lines before
assignments that follow a closing block and before return statements in
templates_test.go for both clickhouse and keeper controllers.

Also adds the missing topologyMinDomains row to docs/reference/api-reference.mdx
so the API Reference Generated CI check passes.
@gangavh1008

Copy link
Copy Markdown
Author

@GrigoryPervakov , please review the PR. Thank you.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants