diff --git a/charts/base/Chart.yaml b/charts/base/Chart.yaml index f56ce8d..2b5cde9 100644 --- a/charts/base/Chart.yaml +++ b/charts/base/Chart.yaml @@ -15,13 +15,13 @@ type: application # This is the chart version. This version number should be incremented each time you make changes # to the chart and its templates, including the app version. # Versions are expected to follow Semantic Versioning (https://semver.org/) -version: 0.3.28 +version: 0.3.29 # This is the version number of the application being deployed. This version number should be # incremented each time you make changes to the application. Versions are not expected to # follow Semantic Versioning. They should reflect the version the application is using. # It is recommended to use it with quotes. -appVersion: "0.3.28" +appVersion: "0.3.29" dependencies: - name: gateway-api diff --git a/charts/base/README.md b/charts/base/README.md index 2a1b737..2dfa53b 100644 --- a/charts/base/README.md +++ b/charts/base/README.md @@ -645,6 +645,68 @@ autoscaling: awsRegion: ``` +### Migrating from plain HPA to KEDA without deleting the existing HPA + +When this chart is rendered with `autoscaling.enabled: true` and no `trigger`/`triggers`, +it produces a plain `HorizontalPodAutoscaler` named `{{ include "base.fullname" . }}`. +When `triggers` is set, the same template path produces a KEDA `ScaledObject` with the +same name. Flipping a release between the two modes via `helm upgrade` is rejected by +KEDA's `vscaledobject.kb.io` admission webhook because the same-named HPA still exists +when the new ScaledObject is created (Helm creates new resources before deleting old +ones during upgrade). See [kedacore/keda#6250](https://github.com/kedacore/keda/issues/6250) +for background. + +The chart now exposes the two fields KEDA's +[transfer-hpa-ownership](https://keda.sh/docs/2.19/concepts/scaling-deployments/#transfer-ownership-of-an-existing-hpa) +migration path needs: + +```yaml +autoscaling: + enabled: true + minReplicas: 2 + maxReplicas: 50 + scaledObjectAnnotations: + scaledobject.keda.sh/transfer-hpa-ownership: "true" + hpaName: my-release-name # must match the existing HPA's metadata.name + triggers: + - type: cpu + metricType: Utilization + metadata: + value: "70" +``` + +With the annotation, the webhook skips its duplicate-HPA check, and KEDA claims the +existing HPA in place by rewriting its `ownerReferences`. + +There is one residual subtlety: Helm still deletes the old HPA at the end of the +upgrade (because the chart no longer renders an HPA template when `triggers` is set), +which leaves a brief metrics-blind window of up to `pollingInterval` seconds before +KEDA's reconciler recreates the HPA. The deployment's replica count stays put during +this window because Kubernetes does not autoscale a Deployment without an autoscaler; +only metrics evaluation pauses. + +For a zero-gap migration, also tell Helm to keep the old HPA before the upgrade: + +```bash +kubectl annotate hpa/ -n helm.sh/resource-policy=keep +helm upgrade dasmeta/base --version -f values.yaml +``` + +After the upgrade has completed and KEDA owns the HPA, you can remove the annotation: + +```bash +kubectl annotate hpa/ -n helm.sh/resource-policy- +``` + +#### Reverse direction (KEDA → plain HPA) + +There is no equivalent transfer mechanism for going back from a `ScaledObject` to a +plain HPA via this chart. KEDA owns its managed HPA via `ownerReferences`, so deleting +the `ScaledObject` triggers garbage collection of the underlying HPA. Tracking issue: +[kedacore/keda#6250](https://github.com/kedacore/keda/issues/6250). If you need to +reverse, expect to manually recreate the HPA after the chart upgrade has removed the +ScaledObject. + ### custom rollout strategy(canary,blue/gree) configs by using flagger ```yaml # This config allows to enable custom rollout strategies by using different providers/operators diff --git a/charts/base/templates/keda.yaml b/charts/base/templates/keda.yaml index 59f012c..fac6bcd 100644 --- a/charts/base/templates/keda.yaml +++ b/charts/base/templates/keda.yaml @@ -5,6 +5,10 @@ metadata: name: {{ include "base.fullname" . }} labels: {{- include "base.labels" . | nindent 4 }} + {{- with .Values.autoscaling.scaledObjectAnnotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} spec: scaleTargetRef: name: {{ include "base.fullname" . }} @@ -42,10 +46,15 @@ spec: {{ $key }}: "{{ $value }}" {{- end }} {{- end }} - {{- if .Values.autoscaling.behavior }} + {{- if or .Values.autoscaling.behavior .Values.autoscaling.hpaName }} advanced: horizontalPodAutoscalerConfig: + {{- with .Values.autoscaling.hpaName }} + name: {{ . }} + {{- end }} + {{- with .Values.autoscaling.behavior }} behavior: -{{ toYaml .Values.autoscaling.behavior | nindent 8 }} +{{ toYaml . | nindent 8 }} + {{- end }} {{- end }} {{- end }} diff --git a/charts/base/values.yaml b/charts/base/values.yaml index 8200e04..d731bba 100644 --- a/charts/base/values.yaml +++ b/charts/base/values.yaml @@ -240,6 +240,16 @@ autoscaling: maxReplicas: 100 targetCPUUtilizationPercentage: 80 + # Optional. Annotations to apply to the rendered ScaledObject (KEDA mode only). + # Useful for KEDA's transfer-hpa-ownership migration path. + # scaledObjectAnnotations: + # scaledobject.keda.sh/transfer-hpa-ownership: "true" + + # Optional. Custom name for the HPA that KEDA manages on behalf of the ScaledObject. + # Defaults to "keda-hpa-{release-name}". Set to the existing HPA's name when migrating + # from plain HPA to KEDA via transfer-hpa-ownership (see README). + # hpaName: my-existing-hpa + # Optional HPA scale up/down behavior (Kubernetes autoscaling/v2+). # If set, it will be rendered under `spec.behavior` in the generated HPA. # @@ -314,6 +324,25 @@ autoscaling: # queueName: my-queue # queueLength: "50" +# Example: Migrate an existing plain HPA to a KEDA ScaledObject without manually +# deleting the old HPA. KEDA's vscaledobject.kb.io webhook normally rejects creating +# a ScaledObject when an HPA already manages the same workload; the transfer-hpa-ownership +# annotation, paired with `hpaName` matching the existing HPA's name, tells KEDA to +# claim the existing HPA in place. See charts/base/README.md for the full recipe +# (including the optional `helm.sh/resource-policy: keep` step for zero-gap migration). +# autoscaling: +# enabled: true +# minReplicas: 2 +# maxReplicas: 50 +# scaledObjectAnnotations: +# scaledobject.keda.sh/transfer-hpa-ownership: "true" +# hpaName: my-release-name # must match the existing HPA's metadata.name +# triggers: +# - type: cpu +# metricType: Utilization +# metadata: +# value: "70" + nodeSelector: {} tolerations: []