diff --git a/deploy/charts/buzz/README.md b/deploy/charts/buzz/README.md index aa7985bab9..91a2b59ea3 100644 --- a/deploy/charts/buzz/README.md +++ b/deploy/charts/buzz/README.md @@ -52,7 +52,6 @@ security boundaries. See: -- [`docs/argo-rollouts.md`](docs/argo-rollouts.md) — optional manual canary updates and Deployment handover - [`examples/argocd-app.yaml`](examples/argocd-app.yaml) — ArgoCD Application - [`examples/flux-helmrelease.yaml`](examples/flux-helmrelease.yaml) — Flux HelmRelease v2 - [`examples/secret-sample.yaml`](examples/secret-sample.yaml) — Secret schema diff --git a/deploy/charts/buzz/docs/argo-rollouts.md b/deploy/charts/buzz/docs/argo-rollouts.md deleted file mode 100644 index d114e4e3e1..0000000000 --- a/deploy/charts/buzz/docs/argo-rollouts.md +++ /dev/null @@ -1,114 +0,0 @@ -# Optional Argo Rollouts - -The chart uses a Kubernetes Deployment by default. Setting `rollout.enabled: true` -adds an Argo `Rollout` in the same namespace. Install the Argo Rollouts controller -and its CRDs separately before enabling it. This configuration targets the -`argoproj.io/v1alpha1` API used by Argo Rollouts v1.8.3. - -The Deployment still contains the image, environment variables, probes, volumes, -and other pod settings. The Rollout reads those settings through `workloadRef` -and creates **new pods of its own**. It does not adopt the Deployment's pods. -Both sets use the existing Service and PodDisruptionBudget selectors. - -This first version supports fixed replica counts and manual promotion. It does -not add health analyses, automatic abort decisions, traffic routing, or HPA -support. `autoscaling.enabled: true` is rejected while Rollouts are enabled. -Existing installations with Rollouts disabled retain their Deployment/HPA path. - -## First handover: two replicas - -Keep the relay image, secrets, and pod settings unchanged throughout handover. -Use an immutable image digest. Confirm that both existing pods are healthy and -that the cluster has room for one extra pod. Redis must be configured for the -overlapping pods; all pods must use the same external database, Redis, and storage. - -Start with this addition to your existing production values: - -```yaml -replicaCount: 2 -autoscaling: - enabled: false -rollout: - enabled: true - deploymentReplicas: 2 - replicas: 0 - minReadySeconds: 60 - progressDeadlineSeconds: 900 - weights: [50, 100] -``` - -The two replica fields are intentionally required. Enabling the feature alone -fails rendering instead of silently scaling down the Deployment. `replicaCount` -remains the desired fleet size; the two explicit counts divide that fleet -between controllers during the handover. - -Apply **one row at a time** through your normal Helm or GitOps process: - -| Stage | `rollout.deploymentReplicas` | `rollout.replicas` | Action | -|---|---:|---:|---| -| Prepare | 2 | 0 | Create the Rollout with no requested pods. | -| Add first pod | 2 | 1 | Wait for the new Rollout pod to be healthy. | -| Drain first old pod | 1 | 1 | Wait for client reconnections and recovery. | -| Add second pod | 1 | 2 | Wait for the second Rollout pod to be healthy. | -| Finish | 0 | 2 | Drain the last Deployment pod and check recovery. | - -Before every drain, verify the replacement has been Ready for at least 60 seconds, -is serving through the Service, and the fleet has recovered: connections, -authenticated sessions, subscriptions, error rates, and database load. Before -each next row, wait for the previous change to finish, including pod termination. -If recovery is incomplete, stop and investigate. Desired counts alone do not prove -healthy capacity. The chart checks only that the desired counts total two or -three and that neither controller requests more than two. - -Argo skips canary steps when creating the first revision. **The pauses do not -protect this handover.** The explicit replica changes and checks above do. -`workloadRef.scaleDown: never` ensures Argo does not also scale the Deployment; -your values remain its replica source of truth. - -Keep the zero-replica Deployment after handover. Argo still needs its pod -configuration. Argo CD continues applying both the Deployment and the Rollout. -There is no switch that tells Argo CD to stop managing the Deployment. - -## Later image updates - -Leave the counts at Deployment `0`, Rollout `2`, and change the usual `image.tag` -or `image.digest`. Argo observes the changed Deployment template and manages the -new revision. With `[50, 100]`, the generated steps are: - -```yaml -- pause: {} -- setWeight: 50 -- pause: {} -- setWeight: 100 -- pause: {} -``` - -Each pause requires an operator to inspect health and promote one step using the -Argo Rollouts UI or `kubectl argo rollouts promote `. Do not use -`--full` to bypass the checks. The controller allows one surge pod and requests -zero unavailable pods; this does not guarantee every client has reconnected. -For larger fleets, choose smaller weights before enabling the feature. - -The weights approximate the share of Rollout pods running the new revision. -They do not split WebSocket traffic by percentage or move existing connections. -Pauses also do not stop ordinary scaling, pod replacement after failures, or a -direct change to the replica counts. - -## Returning to a Deployment - -Do not disable Rollouts while they still own serving pods: removing the Rollout -can delete its pods before the Deployment has replaced them. Freeze image and -pod settings, then reverse the table above. Add and verify one Deployment pod -before draining a Rollout pod. Once the Deployment has two healthy pods and the -Rollout has zero remaining pods, set `rollout.enabled: false` and remove the -unused rollout overrides. Leave `replicaCount: 2`. - -## What this does not prove - -This chart is a prerequisite for a staged migration, not an automatic recovery -system. Enabling it with nonzero replicas affects real pods and traffic. It is -not a report-only or shadow mode. Observation-only health analyses and enforced -recovery checks require a separate change and validation before activation. - -See the upstream [migration guide](https://argo-rollouts.readthedocs.io/en/stable/migrating/) -and [canary behavior](https://argo-rollouts.readthedocs.io/en/stable/features/canary/). diff --git a/deploy/charts/buzz/templates/NOTES.txt b/deploy/charts/buzz/templates/NOTES.txt index 6347fc0603..3cfbbb893c 100644 --- a/deploy/charts/buzz/templates/NOTES.txt +++ b/deploy/charts/buzz/templates/NOTES.txt @@ -94,7 +94,7 @@ {{- if $rollout.enabled }} kubectl -n {{ .Release.Namespace }} get deployment,rollout {{ include "buzz.fullname" . }} Argo Rollouts is enabled. The Deployment supplies the pod template. - Updates pause for manual promotion. See docs/argo-rollouts.md before handover. + Later updates pause for manual promotion; the initial revision skips pauses. {{- else }} kubectl -n {{ .Release.Namespace }} rollout status deployment/{{ include "buzz.fullname" . }} {{- end }} diff --git a/deploy/charts/buzz/templates/_rollout.tpl b/deploy/charts/buzz/templates/_rollout.tpl deleted file mode 100644 index 929cd36a22..0000000000 --- a/deploy/charts/buzz/templates/_rollout.tpl +++ /dev/null @@ -1,38 +0,0 @@ -{{/* Missing rollout values must remain compatible with --reuse-values. */}} -{{- define "buzz.validateRollout" -}} -{{- $rollout := .Values.rollout | default dict -}} -{{- if $rollout.enabled -}} - {{- if .Values.autoscaling.enabled -}} - {{- fail "rollout.enabled requires autoscaling.enabled=false; this chart currently supports fixed-replica Rollouts only" -}} - {{- end -}} - {{- if or (not (hasKey $rollout "deploymentReplicas")) (eq $rollout.deploymentReplicas nil) (not (hasKey $rollout "replicas")) (eq $rollout.replicas nil) -}} - {{- fail "rollout.enabled requires explicit rollout.deploymentReplicas and rollout.replicas; start with replicaCount and 0 for an existing Deployment" -}} - {{- end -}} - {{- $deployment := int $rollout.deploymentReplicas -}} - {{- $replicas := int $rollout.replicas -}} - {{- $target := int .Values.replicaCount -}} - {{- if or (lt $deployment 0) (lt $replicas 0) (gt $deployment $target) (gt $replicas $target) (lt (add $deployment $replicas) $target) (gt (add $deployment $replicas) (add $target 1)) -}} - {{- fail "rollout replica counts must each be between 0 and replicaCount, and their sum must be replicaCount or replicaCount + 1" -}} - {{- end -}} - {{- if and (not .Values.redis.enabled) (not .Values.externalRedis.url) (not .Values.secrets.existingSecret) -}} - {{- fail "rollout.enabled requires Redis for overlapping relay pods; configure redis.enabled, externalRedis.url, or secrets.existingSecret with REDIS_URL" -}} - {{- end -}} - {{- if not $rollout.weights -}} - {{- fail "rollout.weights must contain increasing percentages ending at 100" -}} - {{- end -}} - {{- if le (int ($rollout.progressDeadlineSeconds | default 900)) (int ($rollout.minReadySeconds | default 60)) -}} - {{- fail "rollout.progressDeadlineSeconds must exceed rollout.minReadySeconds" -}} - {{- end -}} - {{- $previous := 0 -}} - {{- range $rollout.weights -}} - {{- $weight := int . -}} - {{- if or (le $weight $previous) (gt $weight 100) -}} - {{- fail "rollout.weights must contain increasing percentages ending at 100" -}} - {{- end -}} - {{- $previous = $weight -}} - {{- end -}} - {{- if ne $previous 100 -}} - {{- fail "rollout.weights must contain increasing percentages ending at 100" -}} - {{- end -}} -{{- end -}} -{{- end -}} diff --git a/deploy/charts/buzz/templates/_validate.tpl b/deploy/charts/buzz/templates/_validate.tpl index 1b95ae543d..ef3ec04021 100644 --- a/deploy/charts/buzz/templates/_validate.tpl +++ b/deploy/charts/buzz/templates/_validate.tpl @@ -5,7 +5,42 @@ surface at template time regardless of which manifest helm renders first. {{- define "buzz.validate" -}} -{{- include "buzz.validateRollout" . -}} +{{/* Missing rollout values must remain compatible with --reuse-values. */}} +{{- $rollout := .Values.rollout | default dict -}} +{{- if $rollout.enabled -}} + {{- if .Values.autoscaling.enabled -}} + {{- fail "rollout.enabled requires autoscaling.enabled=false; this chart currently supports fixed-replica Rollouts only" -}} + {{- end -}} + {{- if or (not (hasKey $rollout "deploymentReplicas")) (eq $rollout.deploymentReplicas nil) (not (hasKey $rollout "replicas")) (eq $rollout.replicas nil) -}} + {{- fail "rollout.enabled requires explicit rollout.deploymentReplicas and rollout.replicas; start with replicaCount and 0 for an existing Deployment" -}} + {{- end -}} + {{- $deployment := int $rollout.deploymentReplicas -}} + {{- $replicas := int $rollout.replicas -}} + {{- $target := int .Values.replicaCount -}} + {{- if or (lt $deployment 0) (lt $replicas 0) (gt $deployment $target) (gt $replicas $target) (lt (add $deployment $replicas) $target) (gt (add $deployment $replicas) (add $target 1)) -}} + {{- fail "rollout replica counts must each be between 0 and replicaCount, and their sum must be replicaCount or replicaCount + 1" -}} + {{- end -}} + {{- if and (not .Values.redis.enabled) (not .Values.externalRedis.url) (not .Values.secrets.existingSecret) -}} + {{- fail "rollout.enabled requires Redis for overlapping relay pods; configure redis.enabled, externalRedis.url, or secrets.existingSecret with REDIS_URL" -}} + {{- end -}} + {{- if not $rollout.weights -}} + {{- fail "rollout.weights must contain increasing percentages ending at 100" -}} + {{- end -}} + {{- if le (int ($rollout.progressDeadlineSeconds | default 900)) (int ($rollout.minReadySeconds | default 60)) -}} + {{- fail "rollout.progressDeadlineSeconds must exceed rollout.minReadySeconds" -}} + {{- end -}} + {{- $previous := 0 -}} + {{- range $rollout.weights -}} + {{- $weight := int . -}} + {{- if or (le $weight $previous) (gt $weight 100) -}} + {{- fail "rollout.weights must contain increasing percentages ending at 100" -}} + {{- end -}} + {{- $previous = $weight -}} + {{- end -}} + {{- if ne $previous 100 -}} + {{- fail "rollout.weights must contain increasing percentages ending at 100" -}} + {{- end -}} +{{- end -}} {{/* relayUrl is required */}} {{- if not .Values.relayUrl -}} diff --git a/deploy/charts/buzz/values.yaml b/deploy/charts/buzz/values.yaml index 26ef527c02..04e2523439 100644 --- a/deploy/charts/buzz/values.yaml +++ b/deploy/charts/buzz/values.yaml @@ -43,7 +43,8 @@ replicaCount: 1 # Requires a separately installed Argo Rollouts controller and CRDs. # The Deployment remains the pod-template source via workloadRef. Set BOTH # counts explicitly; enabling this flag alone cannot scale down the Deployment. -# For a two-pod handover, see docs/argo-rollouts.md before changing these values. +# Start an existing Deployment handover with deploymentReplicas=replicaCount +# and replicas=0, then change one count at a time after verifying health. rollout: enabled: false deploymentReplicas: null