From cf39cf48954f62114bbf32827867a9b74b96bfc9 Mon Sep 17 00:00:00 2001 From: yyhp Date: Mon, 22 Apr 2024 15:11:19 +0200 Subject: [PATCH] Add AnalysisTemplate & update logic for canary rollout strategy --- .../ci/argo-bluegreen-values.yaml | 14 +++ .../ci/argo-canary-values.yaml | 42 +++++++++ .../templates/analysis-template.yaml | 52 +++++++++++ .../generic-service/templates/controller.yaml | 92 +++++++++---------- charts/generic-service/templates/service.yaml | 2 +- charts/generic-service/values.schema.json | 16 +++- charts/generic-service/values.yaml | 5 +- 7 files changed, 172 insertions(+), 51 deletions(-) create mode 100644 charts/generic-service/ci/argo-bluegreen-values.yaml create mode 100644 charts/generic-service/ci/argo-canary-values.yaml create mode 100644 charts/generic-service/templates/analysis-template.yaml diff --git a/charts/generic-service/ci/argo-bluegreen-values.yaml b/charts/generic-service/ci/argo-bluegreen-values.yaml new file mode 100644 index 0000000..bc85990 --- /dev/null +++ b/charts/generic-service/ci/argo-bluegreen-values.yaml @@ -0,0 +1,14 @@ +# Argo Rollout controller BlueGreen test + +image: + repository: jwilder/whoami + tag: latest + +ingress: + enabled: true + port: 8000 + +rollout: + controller: ArgoRollout + strategy: BlueGreen + autoPromotion: true diff --git a/charts/generic-service/ci/argo-canary-values.yaml b/charts/generic-service/ci/argo-canary-values.yaml new file mode 100644 index 0000000..dcfa884 --- /dev/null +++ b/charts/generic-service/ci/argo-canary-values.yaml @@ -0,0 +1,42 @@ +# Argo Rollout controller Canary test + +image: + repository: jwilder/whoami + tag: latest + +ingress: + enabled: true + port: 8000 + +replicas: 2 + +rollout: + controller: ArgoRollout + strategy: Canary + steps: + - setWeight: 20 + - analysis: + templates: + - templateName: success-rate + args: + - name: service-name + value: canary-demo + - pause: + duration: 1h + - setWeight: 40 + analysisTemplates: + - name: success-rate + args: + - name: service-name + metrics: + - name: success-rate + interval: 5m + count: 10 + successCondition: "result[0] >= 0.95" + query: | + sum(irate( + istio_requests_total{reporter="source",destination_service=~"{{args.service-name}}",response_code!~"5.*"}[5m] + )) / + sum(irate( + istio_requests_total{reporter="source",destination_service=~"{{args.service-name}}"}[5m] + )) diff --git a/charts/generic-service/templates/analysis-template.yaml b/charts/generic-service/templates/analysis-template.yaml new file mode 100644 index 0000000..330e2e2 --- /dev/null +++ b/charts/generic-service/templates/analysis-template.yaml @@ -0,0 +1,52 @@ +{{- if eq .Values.rollout.controller "ArgoRollout" }} +{{- range .Values.rollout.analysisTemplates }} +--- +apiVersion: argoproj.io/v1alpha1 +kind: AnalysisTemplate +metadata: + name: {{ include "generic-service.fullname" $ }}-{{ .name }} +spec: + {{- if .args }} + args: + {{- toYaml .args | nindent 4 }} + {{- end }} + {{- if .dryRun }} + dryRun: + {{- toYaml .dryRun | nindent 4 }} + {{- end }} + {{- if .measurementRetention }} + measurementRetention: + {{- toYaml .measurementRetention | nindent 4 }} + {{- end }} + metrics: + {{- range .metrics }} + - name: {{ .name }} + {{- if .consecutiveErrorLimit }} + consecutiveErrorLimit: {{ .consecutiveErrorLimit }} + {{- end }} + {{- if .count }} + count: {{ .count }} + {{- end }} + {{- if .failureCondition }} + failureCondition: {{ .failureCondition }} + {{- end }} + {{- if .failureLimit }} + failureLimit: {{ .failureLimit }} + {{- end }} + {{- if .inconclusiveLimit }} + inconclusiveLimit: {{ .inconclusiveLimit }} + {{- end }} + {{- if .initialDelay }} + initialDelay: {{ .initialDelay }} + {{- end }} + {{- if .interval }} + interval: {{ .interval }} + {{- end }} + provider: + prometheus: + address: {{ .prometheusAddress | default "http://prometheus-prometheus.monitoring:9090" }} + query: | + {{ .query | nindent 12 }} + {{- end }} +{{- end }} +{{- end }} diff --git a/charts/generic-service/templates/controller.yaml b/charts/generic-service/templates/controller.yaml index 4272530..b797d7e 100644 --- a/charts/generic-service/templates/controller.yaml +++ b/charts/generic-service/templates/controller.yaml @@ -51,59 +51,56 @@ spec: {{- end }} type: OnDelete {{- else if eq .Values.rollout.strategy "Canary" }} - {{- if not .Values.ingress.enabled }} - {{ fail "ingress.enabled must be true if rollout.strategy is Canary" }} - {{- end }} - {{- if not .Values.ingress.domains }} - {{ fail "ingress.domains must not be empty if rollout.strategy is Canary" }} - {{- end }} {{- if eq .Values.rollout.controller "ArgoRollout" }} - canary: - stableService: {{ include "generic-service.fullname" . }} - stableMetadata: - labels: - role: stable - canaryService: {{ include "generic-service.fullname" . }}-preview - canaryMetadata: - labels: - role: preview - analysis: {{- .Values.rollout.analysis | required "rollout.analysis is required if rollout.strategy is Canary" | toYaml | nindent 8 }} - trafficRouting: - {{- if .Values.ingress.istio.enabled }} - istio: - virtualService: - name: {{ include "generic-service.fullname" . }} - {{- else if eq .Values.ingress.class "nginx" }} - nginx: - stableIngress: {{ include "generic-service.fullname" . }} - {{- else }} - {{ fail "Ingress must use Istio or nginx if rollout.strategy is Canary" }} + canary: + {{- with .Values.rollout.analysis -}} + analysis: {{- . | toYaml | nindent 8 }} + {{- end }} + {{- if .Values.rollout.useTrafficRouting -}} + {{- if not eq .Values.rollout.strategy "Canary" }} + {{ fail "Traffic routing is only available if rollout.strategy is Canary" }} + {{- end }} + stableService: {{ include "generic-service.fullname" . }} + stableMetadata: + labels: + role: stable + canaryService: {{ include "generic-service.fullname" . }}-preview + canaryMetadata: + labels: + role: preview + trafficRouting: + {{- if .Values.ingress.istio.enabled }} + istio: + virtualService: + name: {{ include "generic-service.fullname" . }} + {{- else if eq .Values.ingress.class "nginx" }} + nginx: + stableIngress: {{ include "generic-service.fullname" . }} + {{- else }} + {{ fail "Ingress must use Istio or nginx if rollout.strategy is Canary" }} + {{- end }} + {{- end }} + {{- with .Values.rollout.steps }} + steps: {{- . | toYaml | nindent 10 }} {{- end }} - {{- else if not .Values.rollout.flagger }} - {{ fail "rollout.flagger must be true or rollout.controller must be ArgoRollout if rollout.strategy is Canary" }} {{- end }} {{- else if eq .Values.rollout.strategy "BlueGreen" }} - {{- if not .Values.ingress.enabled }} - {{ fail "ingress.enabled must be true if rollout.strategy is BlueGreen" }} - {{- end }} {{- if eq .Values.rollout.controller "ArgoRollout" }} - blueGreen: - activeService: {{ include "generic-service.fullname" . }} - activeMetadata: - labels: - role: stable - previewService: {{ include "generic-service.fullname" . }}-preview - previewMetadata: - labels: - role: preview - autoPromotionEnabled: {{ .Values.rollout.autoPromotion }} - {{- if .Values.rollout.analysis }} - prePromotionAnalysis: {{- .Values.rollout.analysis | toYaml | nindent 8 }} - {{- end }} - {{- else if not .Values.rollout.flagger }} - {{ fail "rollout.flagger must be true or rollout.controller must be ArgoRollout if rollout.strategy is BlueGreen" }} + blueGreen: + activeService: {{ include "generic-service.fullname" . }} + activeMetadata: + labels: + role: stable + previewService: {{ include "generic-service.fullname" . }}-preview + previewMetadata: + labels: + role: preview + autoPromotionEnabled: {{ .Values.rollout.autoPromotion }} + {{- if .Values.rollout.analysis }} + prePromotionAnalysis: {{- .Values.rollout.analysis | toYaml | nindent 8 }} + {{- end }} {{- end }} - {{ else }} + {{- else }} {{ fail "Unknown rollout.strategy" }} {{- end }} @@ -463,4 +460,3 @@ spec: {{- end }} {{- end }} {{- end }} - diff --git a/charts/generic-service/templates/service.yaml b/charts/generic-service/templates/service.yaml index 63285cf..3ccb5ac 100644 --- a/charts/generic-service/templates/service.yaml +++ b/charts/generic-service/templates/service.yaml @@ -93,7 +93,7 @@ spec: {{- end }} {{- end }} -{{- if eq .Values.rollout.controller "ArgoRollout" }} +{{- if and (eq .Values.rollout.controller "ArgoRollout") (or (eq .Values.rollout.strategy "BlueGreen") (eq .Values.rollout.useTrafficRouting true)) }} --- apiVersion: v1 kind: Service diff --git a/charts/generic-service/values.schema.json b/charts/generic-service/values.schema.json index f0f668c..97912ec 100644 --- a/charts/generic-service/values.schema.json +++ b/charts/generic-service/values.schema.json @@ -301,7 +301,21 @@ "description": "Use Flagger to control rollouts (rollout.controller must be Deployment or StatefulSet)" }, "analysis": { - "description": "Flagger or Argo Rollouts analysis for automatic Canary or BlueGreen promotion" + "type": "object", + "description": "Flagger or Argo Rollouts analysis for automatic Canary promotion" + }, + "steps": { + "type": "array", + "description": "Argo Rollouts list of steps to perform during a canary rollout" + }, + "analysisTemplates": { + "type": "array", + "description": "List of analysis templates to create (only applicable if rollout.controller is ArgoRollout)" + }, + "useTrafficRouting": { + "type": "boolean", + "default": false, + "description": "Whether to use Argo Rollouts traffig routing feature" }, "revisionHistoryLimit": { "type": ["integer", "null"], diff --git a/charts/generic-service/values.yaml b/charts/generic-service/values.yaml index d621d86..154b09e 100644 --- a/charts/generic-service/values.yaml +++ b/charts/generic-service/values.yaml @@ -63,9 +63,12 @@ resources: rollout: controller: Deployment strategy: RollingUpdate + useTrafficRouting: false autoPromotion: true flagger: false - analysis: null + analysis: {} + steps: [] + analysisTemplates: [] revisionHistoryLimit: null replicas: 1