diff --git a/artifacts/routing/destination-rule.yaml b/artifacts/routing/destination-rule.yaml new file mode 100644 index 00000000..2bdea8f2 --- /dev/null +++ b/artifacts/routing/destination-rule.yaml @@ -0,0 +1,45 @@ +apiVersion: networking.istio.io/v1alpha3 +kind: VirtualService +metadata: + name: podinfo + namespace: test +spec: + gateways: + - public-gateway.istio-system.svc.cluster.local + - mesh + hosts: + - podinfo.istio.weavedx.com + - podinfo + http: + - route: + - destination: + host: podinfo + subset: primary + weight: 50 + - destination: + host: podinfo + subset: canary + weight: 50 +--- +apiVersion: networking.istio.io/v1alpha3 +kind: DestinationRule +metadata: + name: podinfo-destination + namespace: test +spec: + host: podinfo + trafficPolicy: + loadBalancer: + consistentHash: + httpCookie: + name: istiouser + ttl: 30s + subsets: + - name: primary + labels: + app: podinfo + role: primary + - name: canary + labels: + app: podinfo + role: canary diff --git a/charts/grafana/Chart.yaml b/charts/grafana/Chart.yaml index 3b952c18..2e317aec 100644 --- a/charts/grafana/Chart.yaml +++ b/charts/grafana/Chart.yaml @@ -1,7 +1,7 @@ apiVersion: v1 name: grafana -version: 0.1.0 -appVersion: 5.4.2 +version: 1.0.0 +appVersion: 5.4.3 description: Grafana dashboards for monitoring Flagger canary deployments icon: https://raw.githubusercontent.com/stefanprodan/flagger/master/docs/logo/flagger-icon.png home: https://flagger.app diff --git a/charts/grafana/dashboards/canary.json b/charts/grafana/dashboards/canary.json index d3647bcd..5a1d969e 100644 --- a/charts/grafana/dashboards/canary.json +++ b/charts/grafana/dashboards/canary.json @@ -2,7 +2,6 @@ "annotations": { "list": [ { - "$$hashKey": "object:1587", "builtIn": 1, "datasource": "-- Grafana --", "enable": true, @@ -16,8 +15,8 @@ "editable": true, "gnetId": null, "graphTooltip": 0, - "id": null, - "iteration": 1534587617141, + "id": 1, + "iteration": 1549736611069, "links": [], "panels": [ { @@ -179,7 +178,6 @@ "tableColumn": "", "targets": [ { - "$$hashKey": "object:2857", "expr": "sum(irate(istio_requests_total{reporter=\"destination\",destination_workload_namespace=~\"$namespace\",destination_workload=~\"$primary\",response_code!~\"5.*\"}[30s])) / sum(irate(istio_requests_total{reporter=\"destination\",destination_workload_namespace=~\"$namespace\",destination_workload=~\"$primary\"}[30s]))", "format": "time_series", "intervalFactor": 1, @@ -344,7 +342,6 @@ "tableColumn": "", "targets": [ { - "$$hashKey": "object:2810", "expr": "sum(irate(istio_requests_total{reporter=\"destination\",destination_workload_namespace=~\"$namespace\",destination_workload=~\"$canary\",response_code!~\"5.*\"}[30s])) / sum(irate(istio_requests_total{reporter=\"destination\",destination_workload_namespace=~\"$namespace\",destination_workload=~\"$canary\"}[30s]))", "format": "time_series", "intervalFactor": 1, @@ -363,7 +360,7 @@ "value": "null" } ], - "valueName": "avg" + "valueName": "current" }, { "aliasColors": {}, @@ -432,6 +429,7 @@ ], "thresholds": [], "timeFrom": null, + "timeRegions": [], "timeShift": null, "title": "Primary: Request Duration", "tooltip": { @@ -464,7 +462,11 @@ "min": null, "show": false } - ] + ], + "yaxis": { + "align": false, + "alignLevel": null + } }, { "aliasColors": {}, @@ -533,6 +535,7 @@ ], "thresholds": [], "timeFrom": null, + "timeRegions": [], "timeShift": null, "title": "Canary: Request Duration", "tooltip": { @@ -565,7 +568,11 @@ "min": null, "show": false } - ] + ], + "yaxis": { + "align": false, + "alignLevel": null + } }, { "content": "
\nUSE: $canary.$namespace\n
", @@ -623,7 +630,6 @@ "steppedLine": false, "targets": [ { - "$$hashKey": "object:1685", "expr": "sum(rate(container_cpu_usage_seconds_total{cpu=\"total\",namespace=\"$namespace\",pod_name=~\"$primary.*\", container_name!~\"POD|istio-proxy\"}[1m])) by (pod_name)", "format": "time_series", "hide": false, @@ -634,6 +640,7 @@ ], "thresholds": [], "timeFrom": null, + "timeRegions": [], "timeShift": null, "title": "Primary: CPU Usage by Pod", "tooltip": { @@ -651,7 +658,6 @@ }, "yaxes": [ { - "$$hashKey": "object:1845", "format": "s", "label": "CPU seconds / second", "logBase": 1, @@ -660,7 +666,6 @@ "show": true }, { - "$$hashKey": "object:1846", "format": "short", "label": null, "logBase": 1, @@ -668,7 +673,11 @@ "min": null, "show": false } - ] + ], + "yaxis": { + "align": false, + "alignLevel": null + } }, { "aliasColors": {}, @@ -711,7 +720,6 @@ "steppedLine": false, "targets": [ { - "$$hashKey": "object:1685", "expr": "sum(rate(container_cpu_usage_seconds_total{cpu=\"total\",namespace=\"$namespace\",pod_name=~\"$canary.*\", pod_name!~\"$primary.*\", container_name!~\"POD|istio-proxy\"}[1m])) by (pod_name)", "format": "time_series", "hide": false, @@ -722,6 +730,7 @@ ], "thresholds": [], "timeFrom": null, + "timeRegions": [], "timeShift": null, "title": "Canary: CPU Usage by Pod", "tooltip": { @@ -739,7 +748,6 @@ }, "yaxes": [ { - "$$hashKey": "object:1845", "format": "s", "label": "CPU seconds / second", "logBase": 1, @@ -748,7 +756,6 @@ "show": true }, { - "$$hashKey": "object:1846", "format": "short", "label": null, "logBase": 1, @@ -756,7 +763,11 @@ "min": null, "show": false } - ] + ], + "yaxis": { + "align": false, + "alignLevel": null + } }, { "aliasColors": {}, @@ -799,7 +810,6 @@ "steppedLine": false, "targets": [ { - "$$hashKey": "object:1685", "expr": "sum(container_memory_working_set_bytes{namespace=\"$namespace\",pod_name=~\"$primary.*\", container_name!~\"POD|istio-proxy\"}) by (pod_name)", "format": "time_series", "hide": false, @@ -811,6 +821,7 @@ ], "thresholds": [], "timeFrom": null, + "timeRegions": [], "timeShift": null, "title": "Primary: Memory Usage by Pod", "tooltip": { @@ -828,7 +839,6 @@ }, "yaxes": [ { - "$$hashKey": "object:1845", "decimals": null, "format": "bytes", "label": "", @@ -838,7 +848,6 @@ "show": true }, { - "$$hashKey": "object:1846", "format": "short", "label": null, "logBase": 1, @@ -846,7 +855,11 @@ "min": null, "show": false } - ] + ], + "yaxis": { + "align": false, + "alignLevel": null + } }, { "aliasColors": {}, @@ -889,7 +902,6 @@ "steppedLine": false, "targets": [ { - "$$hashKey": "object:1685", "expr": "sum(container_memory_working_set_bytes{namespace=\"$namespace\",pod_name=~\"$canary.*\", pod_name!~\"$primary.*\", container_name!~\"POD|istio-proxy\"}) by (pod_name)", "format": "time_series", "hide": false, @@ -901,6 +913,7 @@ ], "thresholds": [], "timeFrom": null, + "timeRegions": [], "timeShift": null, "title": "Canary: Memory Usage by Pod", "tooltip": { @@ -918,7 +931,6 @@ }, "yaxes": [ { - "$$hashKey": "object:1845", "decimals": null, "format": "bytes", "label": "", @@ -928,7 +940,6 @@ "show": true }, { - "$$hashKey": "object:1846", "format": "short", "label": null, "logBase": 1, @@ -936,7 +947,11 @@ "min": null, "show": false } - ] + ], + "yaxis": { + "align": false, + "alignLevel": null + } }, { "aliasColors": {}, @@ -975,12 +990,10 @@ "renderer": "flot", "seriesOverrides": [ { - "$$hashKey": "object:3641", "alias": "received", "color": "#f9d9f9" }, { - "$$hashKey": "object:3649", "alias": "transmited", "color": "#f29191" } @@ -990,7 +1003,6 @@ "steppedLine": false, "targets": [ { - "$$hashKey": "object:2598", "expr": "sum(rate (container_network_receive_bytes_total{namespace=\"$namespace\",pod_name=~\"$primary.*\"}[1m])) ", "format": "time_series", "intervalFactor": 1, @@ -998,7 +1010,6 @@ "refId": "A" }, { - "$$hashKey": "object:3245", "expr": "-sum (rate (container_network_transmit_bytes_total{namespace=\"$namespace\",pod_name=~\"$primary.*\"}[1m]))", "format": "time_series", "intervalFactor": 1, @@ -1008,6 +1019,7 @@ ], "thresholds": [], "timeFrom": null, + "timeRegions": [], "timeShift": null, "title": "Primary: Network I/O", "tooltip": { @@ -1025,7 +1037,6 @@ }, "yaxes": [ { - "$$hashKey": "object:1845", "decimals": null, "format": "Bps", "label": "", @@ -1035,7 +1046,6 @@ "show": true }, { - "$$hashKey": "object:1846", "format": "short", "label": null, "logBase": 1, @@ -1043,7 +1053,11 @@ "min": null, "show": false } - ] + ], + "yaxis": { + "align": false, + "alignLevel": null + } }, { "aliasColors": {}, @@ -1082,12 +1096,10 @@ "renderer": "flot", "seriesOverrides": [ { - "$$hashKey": "object:3641", "alias": "received", "color": "#f9d9f9" }, { - "$$hashKey": "object:3649", "alias": "transmited", "color": "#f29191" } @@ -1097,7 +1109,6 @@ "steppedLine": false, "targets": [ { - "$$hashKey": "object:2598", "expr": "sum(rate (container_network_receive_bytes_total{namespace=\"$namespace\",pod_name=~\"$canary.*\",pod_name!~\"$primary.*\"}[1m])) ", "format": "time_series", "intervalFactor": 1, @@ -1105,7 +1116,6 @@ "refId": "A" }, { - "$$hashKey": "object:3245", "expr": "-sum (rate (container_network_transmit_bytes_total{namespace=\"$namespace\",pod_name=~\"$canary.*\",pod_name!~\"$primary.*\"}[1m]))", "format": "time_series", "intervalFactor": 1, @@ -1115,6 +1125,7 @@ ], "thresholds": [], "timeFrom": null, + "timeRegions": [], "timeShift": null, "title": "Canary: Network I/O", "tooltip": { @@ -1132,7 +1143,6 @@ }, "yaxes": [ { - "$$hashKey": "object:1845", "decimals": null, "format": "Bps", "label": "", @@ -1142,7 +1152,6 @@ "show": true }, { - "$$hashKey": "object:1846", "format": "short", "label": null, "logBase": 1, @@ -1150,7 +1159,11 @@ "min": null, "show": false } - ] + ], + "yaxis": { + "align": false, + "alignLevel": null + } }, { "content": "
\nIN/OUTBOUND: $canary.$namespace\n
", @@ -1205,7 +1218,6 @@ "steppedLine": false, "targets": [ { - "$$hashKey": "object:1953", "expr": "round(sum(irate(istio_requests_total{connection_security_policy=\"mutual_tls\", destination_workload_namespace=~\"$namespace\", destination_workload=~\"$primary\", reporter=\"destination\"}[30s])) by (source_workload, source_workload_namespace, response_code), 0.001)", "format": "time_series", "hide": false, @@ -1215,7 +1227,6 @@ "step": 2 }, { - "$$hashKey": "object:1954", "expr": "round(sum(irate(istio_requests_total{connection_security_policy!=\"mutual_tls\", destination_workload_namespace=~\"$namespace\", destination_workload=~\"$primary\", reporter=\"destination\"}[30s])) by (source_workload, source_workload_namespace, response_code), 0.001)", "format": "time_series", "hide": false, @@ -1227,6 +1238,7 @@ ], "thresholds": [], "timeFrom": null, + "timeRegions": [], "timeShift": null, "title": "Primary: Incoming Requests by Source And Response Code", "tooltip": { @@ -1246,7 +1258,6 @@ }, "yaxes": [ { - "$$hashKey": "object:1999", "format": "ops", "label": null, "logBase": 1, @@ -1255,7 +1266,6 @@ "show": true }, { - "$$hashKey": "object:2000", "format": "short", "label": null, "logBase": 1, @@ -1263,7 +1273,11 @@ "min": null, "show": false } - ] + ], + "yaxis": { + "align": false, + "alignLevel": null + } }, { "aliasColors": {}, @@ -1323,6 +1337,7 @@ ], "thresholds": [], "timeFrom": null, + "timeRegions": [], "timeShift": null, "title": "Canary: Incoming Requests by Source And Response Code", "tooltip": { @@ -1357,7 +1372,11 @@ "min": null, "show": false } - ] + ], + "yaxis": { + "align": false, + "alignLevel": null + } }, { "aliasColors": {}, @@ -1416,6 +1435,7 @@ ], "thresholds": [], "timeFrom": null, + "timeRegions": [], "timeShift": null, "title": "Primary: Outgoing Requests by Destination And Response Code", "tooltip": { @@ -1450,7 +1470,11 @@ "min": null, "show": false } - ] + ], + "yaxis": { + "align": false, + "alignLevel": null + } }, { "aliasColors": {}, @@ -1509,6 +1533,7 @@ ], "thresholds": [], "timeFrom": null, + "timeRegions": [], "timeShift": null, "title": "Canary: Outgoing Requests by Destination And Response Code", "tooltip": { @@ -1543,7 +1568,11 @@ "min": null, "show": false } - ] + ], + "yaxis": { + "align": false, + "alignLevel": null + } } ], "refresh": "10s", @@ -1555,10 +1584,12 @@ { "allValue": null, "current": { - "text": "demo", - "value": "demo" + "selected": true, + "text": "test", + "value": "test" }, "datasource": "prometheus", + "definition": "", "hide": 0, "includeAll": false, "label": "Namespace", @@ -1568,6 +1599,7 @@ "query": "query_result(sum(istio_requests_total) by (destination_workload_namespace) or sum(istio_tcp_sent_bytes_total) by (destination_workload_namespace))", "refresh": 1, "regex": "/.*_namespace=\"([^\"]*).*/", + "skipUrlSync": false, "sort": 0, "tagValuesQuery": "", "tags": [], @@ -1578,10 +1610,12 @@ { "allValue": null, "current": { - "text": "primary", - "value": "primary" + "selected": false, + "text": "backend-primary", + "value": "backend-primary" }, "datasource": "prometheus", + "definition": "", "hide": 0, "includeAll": false, "label": "Primary", @@ -1591,6 +1625,7 @@ "query": "query_result(sum(istio_requests_total{destination_workload_namespace=~\"$namespace\"}) by (destination_service_name))", "refresh": 1, "regex": "/.*destination_service_name=\"([^\"]*).*/", + "skipUrlSync": false, "sort": 1, "tagValuesQuery": "", "tags": [], @@ -1601,10 +1636,12 @@ { "allValue": null, "current": { - "text": "canary", - "value": "canary" + "selected": true, + "text": "backend", + "value": "backend" }, "datasource": "prometheus", + "definition": "", "hide": 0, "includeAll": false, "label": "Canary", @@ -1614,6 +1651,7 @@ "query": "query_result(sum(istio_requests_total{destination_workload_namespace=~\"$namespace\"}) by (destination_service_name))", "refresh": 1, "regex": "/.*destination_service_name=\"([^\"]*).*/", + "skipUrlSync": false, "sort": 1, "tagValuesQuery": "", "tags": [], @@ -1653,7 +1691,7 @@ ] }, "timezone": "", - "title": "Canary analysis", + "title": "Flagger canary", "uid": "RdykD7tiz", - "version": 2 -} + "version": 3 +} \ No newline at end of file diff --git a/charts/grafana/templates/deployment.yaml b/charts/grafana/templates/deployment.yaml index 756da994..f3235011 100644 --- a/charts/grafana/templates/deployment.yaml +++ b/charts/grafana/templates/deployment.yaml @@ -38,12 +38,21 @@ spec: # path: / # port: http env: + - name: GF_PATHS_PROVISIONING + value: /etc/grafana/provisioning/ + {{- if .Values.password }} - name: GF_SECURITY_ADMIN_USER value: {{ .Values.user }} - name: GF_SECURITY_ADMIN_PASSWORD value: {{ .Values.password }} - - name: GF_PATHS_PROVISIONING - value: /etc/grafana/provisioning/ + {{- else }} + - name: GF_AUTH_BASIC_ENABLED + value: "false" + - name: GF_AUTH_ANONYMOUS_ENABLED + value: "true" + - name: GF_AUTH_ANONYMOUS_ORG_ROLE + value: Admin + {{- end }} volumeMounts: - name: grafana mountPath: /var/lib/grafana diff --git a/charts/grafana/values.yaml b/charts/grafana/values.yaml index 37ff1a1f..d6a4a3f5 100644 --- a/charts/grafana/values.yaml +++ b/charts/grafana/values.yaml @@ -6,7 +6,7 @@ replicaCount: 1 image: repository: grafana/grafana - tag: 5.4.2 + tag: 5.4.3 pullPolicy: IfNotPresent service: @@ -28,7 +28,7 @@ tolerations: [] affinity: {} user: admin -password: admin +password: # Istio Prometheus instance url: http://prometheus:9090 diff --git a/charts/podinfo/.helmignore b/charts/podinfo/.helmignore new file mode 100644 index 00000000..f0c13194 --- /dev/null +++ b/charts/podinfo/.helmignore @@ -0,0 +1,21 @@ +# Patterns to ignore when building packages. +# This supports shell glob matching, relative path matching, and +# negation (prefixed with !). Only one pattern per line. +.DS_Store +# Common VCS dirs +.git/ +.gitignore +.bzr/ +.bzrignore +.hg/ +.hgignore +.svn/ +# Common backup files +*.swp +*.bak +*.tmp +*~ +# Various IDEs +.project +.idea/ +*.tmproj diff --git a/charts/podinfo/Chart.yaml b/charts/podinfo/Chart.yaml new file mode 100644 index 00000000..d54ecf63 --- /dev/null +++ b/charts/podinfo/Chart.yaml @@ -0,0 +1,12 @@ +apiVersion: v1 +version: 2.0.0 +appVersion: 1.4.0 +name: podinfo +engine: gotpl +description: Flagger canary deployment demo chart +home: https://github.com/stefanprodan/flagger +maintainers: +- email: stefanprodan@users.noreply.github.com + name: stefanprodan +sources: +- https://github.com/stefanprodan/flagger diff --git a/charts/podinfo/README.md b/charts/podinfo/README.md new file mode 100644 index 00000000..6a626022 --- /dev/null +++ b/charts/podinfo/README.md @@ -0,0 +1,79 @@ +# Podinfo + +Podinfo is a tiny web application made with Go +that showcases best practices of running canary deployments with Flagger and Istio. + +## Installing the Chart + +Add Flagger Helm repository: + +```console +helm repo add flagger https://flagger.app +``` + +To install the chart with the release name `frontend`: + +```console +helm upgrade -i frontend flagger/podinfo \ +--namespace test \ +--set nameOverride=frontend \ +--set backend=http://backend.test:9898/echo \ +--set canary.enabled=true \ +--set canary.istioIngress.enabled=true \ +--set canary.istioIngress.gateway=public-gateway.istio-system.svc.cluster.local \ +--set canary.istioIngress.host=frontend.istio.example.com +``` + +To install the chart as `backend`: + +```console +helm upgrade -i backend flagger/podinfo \ +--namespace test \ +--set nameOverride=backend \ +--set canary.enabled=true +``` + +## Uninstalling the Chart + +To uninstall/delete the `frontend` deployment: + +```console +$ helm delete --purge frontend +``` + +The command removes all the Kubernetes components associated with the chart and deletes the release. + +## Configuration + +The following tables lists the configurable parameters of the podinfo chart and their default values. + +Parameter | Description | Default +--- | --- | --- +`image.repository` | image repository | `quay.io/stefanprodan/podinfo` +`image.tag` | image tag | `` +`image.pullPolicy` | image pull policy | `IfNotPresent` +`hpa.enabled` | enables HPA | `true` +`hpa.cpu` | target CPU usage per pod | `80` +`hpa.memory` | target memory usage per pod | `512Mi` +`hpa.minReplicas` | maximum pod replicas | `2` +`hpa.maxReplicas` | maximum pod replicas | `4` +`resources.requests/cpu` | pod CPU request | `1m` +`resources.requests/memory` | pod memory request | `16Mi` +`backend` | backend URL | None +`faults.delay` | random HTTP response delays between 0 and 5 seconds | `false` +`faults.error` | 1/3 chances of a random HTTP response error | `false` + +Specify each parameter using the `--set key=value[,key=value]` argument to `helm install`. For example, + +```console +$ helm install flagger/podinfo --name frontend \ + --set=image.tag=1.4.1,hpa.enabled=false +``` + +Alternatively, a YAML file that specifies the values for the above parameters can be provided while installing the chart. For example, + +```console +$ helm install flagger/podinfo --name frontend -f values.yaml +``` + + diff --git a/charts/podinfo/templates/NOTES.txt b/charts/podinfo/templates/NOTES.txt new file mode 100644 index 00000000..92c7709d --- /dev/null +++ b/charts/podinfo/templates/NOTES.txt @@ -0,0 +1 @@ +podinfo {{ .Release.Name }} deployed! \ No newline at end of file diff --git a/charts/podinfo/templates/_helpers.tpl b/charts/podinfo/templates/_helpers.tpl new file mode 100644 index 00000000..3fe7a70f --- /dev/null +++ b/charts/podinfo/templates/_helpers.tpl @@ -0,0 +1,43 @@ +{{/* vim: set filetype=mustache: */}} +{{/* +Expand the name of the chart. +*/}} +{{- define "podinfo.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}} +{{- end -}} + +{{/* +Create a default fully qualified app name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +If release name contains chart name it will be used as a full name. +*/}} +{{- define "podinfo.fullname" -}} +{{- if .Values.fullnameOverride -}} +{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}} +{{- else -}} +{{- $name := default .Chart.Name .Values.nameOverride -}} +{{- if contains $name .Release.Name -}} +{{- .Release.Name | trunc 63 | trimSuffix "-" -}} +{{- else -}} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}} +{{- end -}} +{{- end -}} +{{- end -}} + +{{/* +Create chart name and version as used by the chart label. +*/}} +{{- define "podinfo.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}} +{{- end -}} + +{{/* +Create chart name suffix. +*/}} +{{- define "podinfo.suffix" -}} +{{- if .Values.canary.enabled -}} +{{- "-primary" -}} +{{- else -}} +{{- "" -}} +{{- end -}} +{{- end -}} \ No newline at end of file diff --git a/charts/podinfo/templates/canary.yaml b/charts/podinfo/templates/canary.yaml new file mode 100644 index 00000000..92cc5865 --- /dev/null +++ b/charts/podinfo/templates/canary.yaml @@ -0,0 +1,54 @@ +{{- if .Values.canary.enabled }} +apiVersion: flagger.app/v1alpha3 +kind: Canary +metadata: + name: {{ template "podinfo.fullname" . }} + labels: + app: {{ template "podinfo.name" . }} + chart: {{ template "podinfo.chart" . }} + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} +spec: + targetRef: + apiVersion: apps/v1 + kind: Deployment + name: {{ template "podinfo.fullname" . }} + progressDeadlineSeconds: 60 + autoscalerRef: + apiVersion: autoscaling/v2beta1 + kind: HorizontalPodAutoscaler + name: {{ template "podinfo.fullname" . }} + service: + port: {{ .Values.service.port }} + {{- if .Values.canary.istioIngress.enabled }} + gateways: + - {{ .Values.canary.istioIngress.gateway }} + hosts: + - {{ .Values.canary.istioIngress.host }} + {{- end }} + canaryAnalysis: + interval: {{ .Values.canary.analysis.interval }} + threshold: {{ .Values.canary.analysis.threshold }} + maxWeight: {{ .Values.canary.analysis.maxWeight }} + stepWeight: {{ .Values.canary.analysis.stepWeight }} + metrics: + - name: istio_requests_total + threshold: {{ .Values.canary.thresholds.successRate }} + interval: 1m + - name: istio_request_duration_seconds_bucket + threshold: {{ .Values.canary.thresholds.latency }} + interval: 1m + {{- if .Values.canary.loadtest.enabled }} + webhooks: + - name: load-test-get + url: {{ .Values.canary.loadtest.url }} + timeout: 5s + metadata: + cmd: "hey -z 1m -q 5 -c 2 http://{{ template "podinfo.fullname" . }}.{{ .Release.Namespace }}:{{ .Values.service.port }}" + - name: load-test-post + url: {{ .Values.canary.loadtest.url }} + timeout: 5s + metadata: + cmd: "hey -z 1m -q 5 -c 2 -m POST -d '{\"test\": true}' http://{{ template "podinfo.fullname" . }}.{{ .Release.Namespace }}:{{ .Values.service.port }}/echo" + {{- end }} +{{- end }} \ No newline at end of file diff --git a/charts/podinfo/templates/deployment.yaml b/charts/podinfo/templates/deployment.yaml new file mode 100644 index 00000000..20a6751e --- /dev/null +++ b/charts/podinfo/templates/deployment.yaml @@ -0,0 +1,86 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ template "podinfo.fullname" . }} + labels: + app: {{ template "podinfo.name" . }} + chart: {{ template "podinfo.chart" . }} + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} +spec: + strategy: + type: RollingUpdate + rollingUpdate: + maxUnavailable: 1 + selector: + matchLabels: + app: {{ template "podinfo.fullname" . }} + template: + metadata: + labels: + app: {{ template "podinfo.fullname" . }} + annotations: + prometheus.io/scrape: 'true' + spec: + terminationGracePeriodSeconds: 30 + containers: + - name: {{ .Chart.Name }} + image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}" + imagePullPolicy: {{ .Values.image.pullPolicy }} + command: + - ./podinfo + - --port={{ .Values.service.port }} + - --level={{ .Values.logLevel }} + - --random-delay={{ .Values.faults.delay }} + - --random-error={{ .Values.faults.error }} + env: + {{- if .Values.message }} + - name: PODINFO_UI_MESSAGE + value: {{ .Values.message }} + {{- end }} + {{- if .Values.backend }} + - name: PODINFO_BACKEND_URL + value: {{ .Values.backend }} + {{- end }} + ports: + - name: http + containerPort: {{ .Values.service.port }} + protocol: TCP + livenessProbe: + exec: + command: + - podcli + - check + - http + - localhost:{{ .Values.service.port }}/healthz + initialDelaySeconds: 5 + timeoutSeconds: 5 + readinessProbe: + exec: + command: + - podcli + - check + - http + - localhost:{{ .Values.service.port }}/readyz + initialDelaySeconds: 5 + timeoutSeconds: 5 + volumeMounts: + - name: data + mountPath: /data + resources: +{{ toYaml .Values.resources | indent 12 }} + {{- with .Values.nodeSelector }} + nodeSelector: +{{ toYaml . | indent 8 }} + {{- end }} + {{- with .Values.affinity }} + affinity: +{{ toYaml . | indent 8 }} + {{- end }} + {{- with .Values.tolerations }} + tolerations: +{{ toYaml . | indent 8 }} + {{- end }} + volumes: + - name: data + emptyDir: {} diff --git a/charts/podinfo/templates/hpa.yaml b/charts/podinfo/templates/hpa.yaml new file mode 100644 index 00000000..dcb36d3a --- /dev/null +++ b/charts/podinfo/templates/hpa.yaml @@ -0,0 +1,32 @@ +{{- if .Values.hpa.enabled -}} +apiVersion: autoscaling/v2beta1 +kind: HorizontalPodAutoscaler +metadata: + name: {{ template "podinfo.fullname" . }} +spec: + scaleTargetRef: + apiVersion: apps/v1beta2 + kind: Deployment + name: {{ template "podinfo.fullname" . }} + minReplicas: {{ .Values.hpa.minReplicas }} + maxReplicas: {{ .Values.hpa.maxReplicas }} + metrics: + {{- if .Values.hpa.cpu }} + - type: Resource + resource: + name: cpu + targetAverageUtilization: {{ .Values.hpa.cpu }} + {{- end }} + {{- if .Values.hpa.memory }} + - type: Resource + resource: + name: memory + targetAverageValue: {{ .Values.hpa.memory }} + {{- end }} + {{- if .Values.hpa.requests }} + - type: Pod + pods: + metricName: http_requests + targetAverageValue: {{ .Values.hpa.requests }} + {{- end }} +{{- end }} diff --git a/charts/podinfo/templates/service.yaml b/charts/podinfo/templates/service.yaml new file mode 100644 index 00000000..82b9451c --- /dev/null +++ b/charts/podinfo/templates/service.yaml @@ -0,0 +1,20 @@ +{{- if not .Values.canary.enabled }} +apiVersion: v1 +kind: Service +metadata: + name: {{ template "podinfo.fullname" . }} + labels: + app: {{ template "podinfo.name" . }} + chart: {{ template "podinfo.chart" . }} + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} +spec: + type: {{ .Values.service.type }} + ports: + - port: {{ .Values.service.port }} + targetPort: http + protocol: TCP + name: http + selector: + app: {{ template "podinfo.fullname" . }} +{{- end }} \ No newline at end of file diff --git a/charts/podinfo/templates/tests/test-config.yaml b/charts/podinfo/templates/tests/test-config.yaml new file mode 100755 index 00000000..49a95554 --- /dev/null +++ b/charts/podinfo/templates/tests/test-config.yaml @@ -0,0 +1,22 @@ +{{- $url := printf "%s%s.%s:%v" (include "podinfo.fullname" .) (include "podinfo.suffix" .) .Release.Namespace .Values.service.port -}} +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ template "podinfo.fullname" . }}-tests + labels: + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + chart: {{ .Chart.Name }}-{{ .Chart.Version }} + app: {{ template "podinfo.name" . }} +data: + run.sh: |- + @test "HTTP POST /echo" { + run curl --retry 3 --connect-timeout 2 -sSX POST -d 'test' {{ $url }}/echo + [ $output = "test" ] + } + @test "HTTP POST /store" { + curl --retry 3 --connect-timeout 2 -sSX POST -d 'test' {{ $url }}/store + } + @test "HTTP GET /" { + curl --retry 3 --connect-timeout 2 -sS {{ $url }} | grep hostname + } diff --git a/charts/podinfo/templates/tests/test-pod.yaml b/charts/podinfo/templates/tests/test-pod.yaml new file mode 100755 index 00000000..5105ef4b --- /dev/null +++ b/charts/podinfo/templates/tests/test-pod.yaml @@ -0,0 +1,43 @@ +apiVersion: v1 +kind: Pod +metadata: + name: {{ template "podinfo.fullname" . }}-tests-{{ randAlphaNum 5 | lower }} + annotations: + "helm.sh/hook": test-success + sidecar.istio.io/inject: "false" + labels: + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + chart: {{ .Chart.Name }}-{{ .Chart.Version }} + app: {{ template "podinfo.name" . }} +spec: + initContainers: + - name: "test-framework" + image: "dduportal/bats:0.4.0" + command: + - "bash" + - "-c" + - | + set -ex + # copy bats to tools dir + cp -R /usr/local/libexec/ /tools/bats/ + volumeMounts: + - mountPath: /tools + name: tools + containers: + - name: {{ .Release.Name }}-ui-test + image: dduportal/bats:0.4.0 + command: ["/tools/bats/bats", "-t", "/tests/run.sh"] + volumeMounts: + - mountPath: /tests + name: tests + readOnly: true + - mountPath: /tools + name: tools + volumes: + - name: tests + configMap: + name: {{ template "podinfo.fullname" . }}-tests + - name: tools + emptyDir: {} + restartPolicy: Never diff --git a/charts/podinfo/values.yaml b/charts/podinfo/values.yaml new file mode 100644 index 00000000..3768d618 --- /dev/null +++ b/charts/podinfo/values.yaml @@ -0,0 +1,70 @@ +# Default values for podinfo. +image: + repository: quay.io/stefanprodan/podinfo + tag: 1.4.0 + pullPolicy: IfNotPresent + +service: + type: ClusterIP + port: 9898 + +hpa: + enabled: true + minReplicas: 2 + maxReplicas: 4 + cpu: 80 + memory: 512Mi + +canary: + enabled: false + istioIngress: + enabled: false + # Istio ingress gateway name + gateway: public-gateway.istio-system.svc.cluster.local + # external host name eg. podinfo.example.com + host: + analysis: + # schedule interval (default 60s) + interval: 15s + # max number of failed metric checks before rollback + threshold: 10 + # max traffic percentage routed to canary + # percentage (0-100) + maxWeight: 50 + # canary increment step + # percentage (0-100) + stepWeight: 2 + thresholds: + # minimum req success rate (non 5xx responses) + # percentage (0-100) + successRate: 99 + # maximum req duration P99 + # milliseconds + latency: 500 + loadtest: + enabled: true + # load tester address + url: http://flagger-loadtester.test/ + +resources: + limits: + requests: + cpu: 100m + memory: 32Mi + +nodeSelector: {} + +tolerations: [] + +affinity: {} + +nameOverride: "" +fullnameOverride: "" + +logLevel: info +backend: #http://backend-podinfo:9898/echo +message: #UI greetings + +faults: + delay: false + error: false \ No newline at end of file diff --git a/docs/gitbook/install/flagger-install-on-google-cloud.md b/docs/gitbook/install/flagger-install-on-google-cloud.md index ed3604ba..52631a4d 100644 --- a/docs/gitbook/install/flagger-install-on-google-cloud.md +++ b/docs/gitbook/install/flagger-install-on-google-cloud.md @@ -54,6 +54,7 @@ gcloud beta container clusters create istio \ --machine-type=n1-highcpu-4 \ --preemptible \ --no-enable-cloud-logging \ +--no-enable-cloud-monitoring \ --disk-size=30 \ --enable-autorepair \ --addons=HorizontalPodAutoscaling,Istio \ diff --git a/docs/screens/flagger-grafana-dashboard.png b/docs/screens/flagger-grafana-dashboard.png new file mode 100644 index 00000000..3d65a7d7 Binary files /dev/null and b/docs/screens/flagger-grafana-dashboard.png differ diff --git a/docs/screens/grafana-canary-analysis.png b/docs/screens/grafana-canary-analysis.png index 6055b334..a40de5c3 100644 Binary files a/docs/screens/grafana-canary-analysis.png and b/docs/screens/grafana-canary-analysis.png differ