Feature - Pod inplace upgrades using resize feature#74
Open
Sasidharan3094 wants to merge 2 commits into
Open
Conversation
Signed-off-by: Sasidharan3094 <sasidharan.gopal94@gmail.com>
Sasidharan3094
requested review from
Kaushik-Vijayakumar-1,
Sasidharan-Gopal and
pratheep-kumar
as code owners
July 24, 2026 17:33
Signed-off-by: Sasidharan3094 <sasidharan.gopal94@gmail.com>
Open
6 tasks
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
Adds in-place resize support for Redis master pods, so a CPU/memory-only change to a
RedisFailover'sspec.redis.resourcesno longer forces a master restart on nodes that are already packed tight. Slaves are untouched — they continue through the existing delete-based OnDelete rollout exactly as before.Uses Kubernetes' in-place pod resize (
resizesubresource, stable since v1.35, beta and enabled by default since v1.33 — confirmed working against an EKS 1.34 test cluster).Scope: master-only. Slave resource changes still restart normally; this PR does not change that.
Why
Redis endpoints run 1 master + N slaves, memory-bound, on nodes packed close to capacity. Any resource bump today restarts the master via delete-and-recreate. This PR lets a resource-only change resize the master's live container in place when possible, and safely falls back to the existing restart-based path whenever it can't.
How it works
1. Detecting a resource-only change (
service/k8s/statefulset.go)CreateOrUpdateStatefulSetnow dry-runs the update first (UpdateOptions{DryRun: DryRunAll}) and compares the stored StatefulSet against the dry-run result — both API-server-defaulted, so the diff isn't polluted by implicit fields the server fills in on write (an early version of this compared the stored object against a raw, never-submitted one, which produced false positives on every change; fixed during testing). The comparison itself neutralizes onlyResourceson both container specs before runningapiequality.Semantic.DeepEqual— provably exhaustive (any other field difference, current or future, correctly fails the check) rather than an enumerated field list. Result is stashed as theredis-failover.freshworks.com/resize-onlyannotation on the StatefulSet.2. Master-branch decision (
operator/redisfailover/checker.go)In
UpdateRedisesPods, the master branch now reads that annotation:attemptMasterResize, which:resize-started-atpod annotation (no Status subresource or requeue mechanism exists in this operator, so this is evaluated fresh againsttime.Now()each ~30s reconcile).ResizePod, new method on thePodinterface, usingclient-go'sUpdateResize).PodResizePendingcondition to see the outcome: absent (with resources actually verified to match, see below) → success;Infeasible→ fail fast to delete;Deferred→ try to free room (step 3), re-issuing the resize call on every retry so a mid-flight CR edit gets picked up rather than staying pinned to the original ask.controller-revision-hashlabel to match the StatefulSet'sstatus.updateRevision— required because a resize never updates that label on its own, and without it the next reconcile would treat the pod as stale again.PodResourcesMatchDesired), not just the absence of a pending condition — found during live testing that an RBAC-rejected resize call also shows no pending condition, which had been silently treated as success.3. Freeing node headroom (
operator/redisfailover/service/resize_headroom.go,service/k8s/eviction.go)When a resize is
Deferred, evicts co-located slave pods belonging to otherRedisFailovers on the same node (never the same endpoint's own slaves) to free room:ComputeRequiredHeadroom:Node.Status.Allocatableminus everything else on the node minus the master's desired ask, computed independently for CPU and memory.selectEvictionSet: exhaustive subset search over candidate slave pods (redisfailovers-role=slavelabel, already maintained by the operator) for the minimal-pod-count, minimal-waste subset covering whichever of CPU/memory is actually short — a zero/negative requirement drops out of the constraint automatically, so "CPU only," "memory only," and "both" all fall out of the same code path. Greedy fallback above 24 candidates bounds runtime.policy/v1Eviction, not a raw delete), so PDBs are honored — a rejected eviction triggers recomputing the best remaining subset against the still-outstanding deficit, not a full restart.RBAC
Cross-endpoint eviction means this operator's ServiceAccount now needs to list/evict pods across all namespaces, not just its own managed ones. Added to both
charts/redisoperator/templates/service-account.yamlandmanifests/kustomize/components/rbac/clusterrole.yaml:pods/resize(patch/update)pods/eviction(create)nodes(get/list, forStatus.Allocatable)Deploy the updated RBAC before rolling out this operator image — without it, resize calls fail with a 403 that (pre-fix) was silently misread as success. Confirmed and fixed during live testing.
Testing
Tested live against an EKS 1.34 dev cluster with two co-located
RedisFailovertenants on a single node:Deferredretry.service/k8s/resize_diff_test.go,operator/redisfailover/service/resize_headroom_test.go).Files changed
service/k8s/statefulset.go,service/k8s/resize_diff_test.goservice/k8s/pod.goservice/k8s/eviction.go,service/k8s/k8s.gooperator/redisfailover/service/resize_headroom.go,resize_headroom_test.gooperator/redisfailover/checker.go,checker_test.go,operator/redisfailover/service/check.go,heal.go,constants.gooperator/redisfailover/service/generator.go(explicitResizePolicy)charts/redisoperator/templates/service-account.yaml,manifests/kustomize/components/rbac/clusterrole.yamlmocks/operator/redisfailover/service/RedisFailoverCheck.go,RedisFailoverHeal.go,mocks/service/k8s/Services.go