Skip to content

fix(platform): spread co-located HA controllers across nodes - #2125

Merged
devantler merged 1 commit into
mainfrom
claude/ha-topology-spread
Jun 18, 2026
Merged

devantler merged 1 commit into
mainfrom
claude/ha-topology-spread

Conversation

@devantler

Copy link
Copy Markdown
Contributor

🤖 Generated by the Daily AI Assistant

What

Coroot flagged several control-plane controllers that already run ≥2 replicas as "all instances on one node — not resilient to node failure" (the replicas are co-located on a single node, so one node loss takes the whole controller down).

This PR adds soft topologySpreadConstraints (maxSkew: 1, topologyKey: kubernetes.io/hostname, whenUnsatisfiable: ScheduleAnyway) so the replicas prefer landing on different nodes. Because it is ScheduleAnyway, it never blocks scheduling — it only influences placement when a second node is available.

This is a pure spread change — no replica counts and no resource requests are touched, so there is no capacity/cost impact.

Workloads changed

Workload File Mechanism Pod selector
Kyverno backgroundController k8s/bases/infrastructure/controllers/kyverno/helm-release.yaml chart topologySpreadConstraints component=background-controller, instance=kyverno
Kyverno reportsController ″ chart topologySpreadConstraints component=reports-controller, instance=kyverno
Kyverno cleanupController ″ chart topologySpreadConstraints component=cleanup-controller, instance=kyverno
Cilium Hubble Relay k8s/bases/infrastructure/controllers/cilium/helm-release.yaml chart hubble.relay.topologySpreadConstraints k8s-app=hubble-relay
snapshot-controller k8s/providers/hetzner/infrastructure/controllers/snapshot-controller/helm-release.yaml chart controller.topologySpreadConstraints name=snapshot-controller, instance=snapshot-controller
VPA admissionController k8s/bases/infrastructure/controllers/vertical-pod-autoscaler/helm-release.yaml postRenderers (see below) component=admission-controller, name=vpa, instance=vertical-pod-autoscaler
VPA updater ″ postRenderers (see below) component=updater, name=vpa, instance=vertical-pod-autoscaler

The Kyverno change mirrors the already-present admissionController topologySpread idiom in the same file, applied to the three other ≥2-replica controllers.

Chart that needed a different approach (postRenderer)

vertical-pod-autoscaler (FairwindsOps vpa chart v4.11.0) exposes no topologySpreadConstraints (nor affinity) values for its components — confirmed with helm show values. So for the two flagged components (admissionController, updater) the spread is injected with a postRenderers Kustomize patch on each rendered Deployment — the same mechanism the flagger HelmRelease already uses in this repo. The VPA recommender is intentionally left untouched (Coroot did not flag it).

Validation

  • Confirmed each chart actually supports the key set (and VPA's absence of it) via helm show values / helm template at the exact chart versions/URLs in each helm-release.yaml.
  • Confirmed every labelSelector matches the chart's Deployment .spec.selector.matchLabels.
  • kubectl kustomize builds clean for all four changed dirs and for the merged k8s/providers/hetzner/infrastructure/controllers overlay — verified the relay spread coexists with the hetzner WireGuard strictMode patch, Kyverno shows all 4 spread blocks, and the VPA postRenderer survives the merge.
  • End-to-end simulated the VPA postRenderer (helm template → kustomize) and confirmed both Deployments get topologySpreadConstraints with correct selectors while the recommender stays unmodified.

All flagged ≥2-replica controllers in scope were spread; nothing was left unspread.

🤖 Generated with Claude Code

Coroot flagged several controllers running >=2 replicas as "all instances
on one node - not resilient to node failure". Add soft (ScheduleAnyway)
topologySpreadConstraints across kubernetes.io/hostname so the replicas
prefer separate nodes, without ever blocking scheduling. Pure spread
change: no replica counts or resource requests are touched.

Workloads:
- kyverno backgroundController / reportsController / cleanupController
  (chart topologySpreadConstraints, mirroring the existing
  admissionController idiom)
- cilium hubble.relay (chart topologySpreadConstraints)
- snapshot-controller controller (chart topologySpreadConstraints)
- vertical-pod-autoscaler admissionController + updater — the FairwindsOps
  vpa chart exposes no topologySpreadConstraints/affinity values for these
  components, so the spread is injected via a postRenderers Kustomize patch
  on each Deployment (the same mechanism flagger already uses). The VPA
  recommender is left untouched (not flagged by Coroot).

Each chart's support for the key (or absence, for vpa) was confirmed with
`helm show values`/`helm template`, and every selector matches the chart's
Deployment .spec.selector.matchLabels. `kubectl kustomize` builds clean for
all changed dirs and for the merged hetzner controllers overlay.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@devantler
devantler marked this pull request as ready for review June 17, 2026 22:27
@devantler
devantler enabled auto-merge June 17, 2026 22:27
@devantler
devantler added this pull request to the merge queue Jun 17, 2026
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to failed status checks Jun 17, 2026
@devantler
devantler merged commit 872e494 into main Jun 18, 2026
10 checks passed
@devantler
devantler deleted the claude/ha-topology-spread branch June 18, 2026 07:51
@github-project-automation github-project-automation Bot moved this from 🫴 Ready to ✅ Done in 🌊 Project Board Jun 18, 2026
@botantler-1

botantler-1 Bot commented Jun 18, 2026

Copy link
Copy Markdown
Contributor

🎉 This PR is included in version 1.64.0 🎉

The release is available on GitHub release

Your semantic-release bot 📦🚀

This branch was previously deployed

1 inactive deployment
ci — e9858d1c Deployed Jun 17, 2026 by devantler via 🧪 System Test #2877
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

Archived in project

Development

Successfully merging this pull request may close these issues.

1 participant