fix(redis): identify master by FQDN + self-labeling sidecar to prevent roll split-brain#1652
Draft
sd2k wants to merge 1 commit into
Draft
fix(redis): identify master by FQDN + self-labeling sidecar to prevent roll split-brain#1652sd2k wants to merge 1 commit into
sd2k wants to merge 1 commit into
Conversation
…t roll split-brain Rolling the redis StatefulSet repeatedly stranded the redis-master Service (zero endpoints) and caused two-live-master split-brain, because the chart identified peers by raw pod IP (which changes on every roll) and routed via a mutable label mutated cross-pod by a Sentinel client-reconfig-script. - Configure replication and Sentinel by stable StatefulSet FQDN (replica-announce-ip, sentinel announce-ip, sentinel resolve-hostnames / announce-hostnames, monitor-by-FQDN) so a roll no longer leaves Sentinel on dead "ghost" IPs. Requires Redis >= 6.2. - init.sh: a pod replicaof's the discovered master and never self-promotes on restart, preventing the "restarting pod self-promotes from stale data -> two masters" split-brain. - Replace init-time + client-reconfig-script labeling with a redis-label- reconciler sidecar: each pod self-labels redis-role from its own `redis-cli role`, so the redis-master Service always converges on the true master and can never be stranded with zero endpoints. Removes label.sh. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Rolling the
redisStatefulSet from this lib repeatedly caused production outages for the Grafana ML team: theredis-masterService ended up with zero endpoints (clients gotconnection refused) and, in the worst cases, a two-live-master data split-brain (one even served reverted data that crashlooped a downstream service). This PR fixes the chart's two root causes.Context / incident write-up: grafana/machine-learning#6261 (ML have also filed it there because their image pin makes them the most-affected consumer).
Root causes
init.sh/sentinel.confmonitor and announce by pod IP, with noannounce-ip. A roll changes every pod IP, so Sentinel keeps monitoring dead "ghost" IPs → spurious failovers and split-brain.redis-masterService selects on aredis-rolepod label that is set once at init and otherwise mutated cross-pod by a Sentinelclient-reconfig-script(label.sh). When a reconfig event resolved to a stale/ghost IP,label.shdemoted the old master and then failed to promote the new one (the IP matched no pod) → zero pods labelled master → Service stranded.init.shcould let a restarting pod (esp. index 0) self-promote when it couldn't reach the master during init → two masters.What this changes
replica-announce-ip $(hostname -f),sentinel announce-ip $(hostname -f),sentinel resolve-hostnames yes/announce-hostnames yes, andsentinel monitor <group> <master-FQDN> …. FQDNs survive pod-IP churn, so rolls no longer create ghosts. Requires Redis ≥ 6.2 (the default image here is alreadyredis:6.2-bullseye).init.shdiscovers the current master's FQDN (Sentinel if reachable, else index 0) and configures the pod asreplicaofit unless it is the master. A restarting pod rejoins as a replica instead of self-promoting.label.sh. Newredis-label-reconcilersidecar: each pod continuously labels its own pod'sredis-rolefrom the ground-truthredis-cli role. Because a pod only ever writes its own label from its own actual role, the Service endpoint always converges on the true master within a few seconds and can never be stranded with zero endpoints, regardless of Sentinel state.label.shand theclient-reconfig-scriptare removed.The
redis-master/redis-slaveServices and theirredis-roleselectors are unchanged — only the mechanism that maintains the label is replaced.This lib is vendored by multiple teams (within Grafana: machine-learning, fleet-management, agent-management, grafana_com, play-grafana, …). This is a behavioural change to redis HA — please don't merge as-is. Suggested split into reviewable, independently-shippable pieces:
label.shhardening or removal — strictly safer on its own; could ship first.pods: get/list/patchRBAC.Other open questions for reviewers:
apt installs kubectl into a shared volume purely for the sidecar — worth replacing with a slim image that shipskubectl.OnDelete(the default here) vsRollingUpdate(some consumers override) changes how this rolls out.Validation done
jsonnetevaluates the full lib with a minimal config.init.sh/label-reconciler.shpassshellcheckclean;%%→%escaping verified in the rendered output.POD_NAME), and the deadlabel.sh/kubectl mounts are removed from the sentinel container.Not yet done: deployed/tested on a live cluster. That's the key gap before this is more than a draft.
🤖 Generated with Claude Code