From 7e238c129ab477d4200ff1e08fb5acbce5475e99 Mon Sep 17 00:00:00 2001 From: weaveworksbot Date: Mon, 7 Dec 2020 10:14:31 +0000 Subject: [PATCH] Publish website --- 404.html | 6 +++--- assets/js/{25.914acd1d.js => 25.355f5783.js} | 2 +- assets/js/{26.b2d4f402.js => 26.b2d05e06.js} | 2 +- assets/js/{29.ce4011f4.js => 29.17c547e6.js} | 2 +- assets/js/{30.edda19d9.js => 30.38bd809e.js} | 2 +- assets/js/5.0c32bdf9.js | 1 - assets/js/5.f0e8e1f0.js | 1 + assets/js/{8.8b72a8d3.js => 8.e3907a66.js} | 2 +- assets/js/{9.7919fc1d.js => 9.12e0bca8.js} | 2 +- assets/js/{app.9b46ae46.js => app.cd972522.js} | 6 +++--- dev/dev-guide.html | 4 ++-- dev/index.html | 4 ++-- dev/release-guide.html | 4 ++-- dev/upgrade-guide.html | 4 ++-- index.html | 12 ++++++------ install/flagger-install-on-eks-appmesh.html | 4 ++-- install/flagger-install-on-google-cloud.html | 4 ++-- install/flagger-install-on-kubernetes.html | 4 ++-- install/index.html | 4 ++-- intro/faq.html | 4 ++-- intro/index.html | 4 ++-- tutorials/appmesh-progressive-delivery.html | 4 ++-- tutorials/canary-helm-gitops.html | 4 ++-- tutorials/contour-progressive-delivery.html | 4 ++-- tutorials/crossover-progressive-delivery.html | 4 ++-- tutorials/flagger-smi-istio.html | 4 ++-- tutorials/gloo-progressive-delivery.html | 4 ++-- tutorials/index.html | 4 ++-- tutorials/istio-ab-testing.html | 4 ++-- tutorials/istio-progressive-delivery.html | 4 ++-- tutorials/kubernetes-blue-green.html | 4 ++-- tutorials/linkerd-progressive-delivery.html | 4 ++-- tutorials/nginx-progressive-delivery.html | 4 ++-- tutorials/zero-downtime-deployments.html | 4 ++-- usage/alerting.html | 4 ++-- usage/deployment-strategies.html | 4 ++-- usage/how-it-works.html | 4 ++-- usage/index.html | 4 ++-- usage/metrics.html | 4 ++-- usage/monitoring.html | 4 ++-- usage/webhooks.html | 4 ++-- 41 files changed, 79 insertions(+), 79 deletions(-) rename assets/js/{25.914acd1d.js => 25.355f5783.js} (99%) rename assets/js/{26.b2d4f402.js => 26.b2d05e06.js} (99%) rename assets/js/{29.ce4011f4.js => 29.17c547e6.js} (79%) rename assets/js/{30.edda19d9.js => 30.38bd809e.js} (99%) delete mode 100644 assets/js/5.0c32bdf9.js create mode 100644 assets/js/5.f0e8e1f0.js rename assets/js/{8.8b72a8d3.js => 8.e3907a66.js} (97%) rename assets/js/{9.7919fc1d.js => 9.12e0bca8.js} (98%) rename assets/js/{app.9b46ae46.js => app.cd972522.js} (80%) diff --git a/404.html b/404.html index 4fbd0a98..42ebf3d4 100644 --- a/404.html +++ b/404.html @@ -13,11 +13,11 @@ - + -

404

Looks like we've got some broken links.
Take me home.
- +

404

How did we get here?
Take me home.
+ diff --git a/assets/js/25.914acd1d.js b/assets/js/25.355f5783.js similarity index 99% rename from assets/js/25.914acd1d.js rename to assets/js/25.355f5783.js index 49297f5a..e77a325a 100644 --- a/assets/js/25.914acd1d.js +++ b/assets/js/25.355f5783.js @@ -1 +1 @@ -(window.webpackJsonp=window.webpackJsonp||[]).push([[25],{285:function(t,a,e){"use strict";e.r(a);var s=e(37),n=Object(s.a)({},function(){var t=this,a=t.$createElement,e=t._self._c||a;return e("ContentSlotsDistributor",{attrs:{"slot-key":t.$parent.slotKey}},[e("h1",{attrs:{id:"blue-green-deployments"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#blue-green-deployments","aria-hidden":"true"}},[t._v("#")]),t._v(" Blue/Green Deployments")]),t._v(" "),e("p",[t._v("This guide shows you how to automate Blue/Green deployments with Flagger and Kubernetes.")]),t._v(" "),e("p",[t._v("For applications that are not deployed on a service mesh, Flagger can orchestrate Blue/Green style deployments\nwith Kubernetes L4 networking. When using a service mesh blue/green can be used as\nspecified "),e("router-link",{attrs:{to:"/usage/deployment-strategies.html"}},[t._v("here")]),t._v(".")],1),t._v(" "),e("p",[e("img",{attrs:{src:"https://raw.githubusercontent.com/weaveworks/flagger/master/docs/diagrams/flagger-bluegreen-steps.png",alt:"Flagger Blue/Green Stages"}})]),t._v(" "),e("h2",{attrs:{id:"prerequisites"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#prerequisites","aria-hidden":"true"}},[t._v("#")]),t._v(" Prerequisites")]),t._v(" "),e("p",[t._v("Flagger requires a Kubernetes cluster "),e("strong",[t._v("v1.11")]),t._v(" or newer.")]),t._v(" "),e("p",[t._v("Install Flagger and the Prometheus add-on:")]),t._v(" "),e("div",{staticClass:"language-bash extra-class"},[e("pre",{pre:!0,attrs:{class:"language-bash"}},[e("code",[t._v("helm repo "),e("span",{pre:!0,attrs:{class:"token function"}},[t._v("add")]),t._v(" flagger https://flagger.app\n\nhelm upgrade -i flagger flagger/flagger "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("\\")]),t._v("\n--namespace flagger "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("\\")]),t._v("\n--set prometheus.install"),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v("true "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("\\")]),t._v("\n--set "),e("span",{pre:!0,attrs:{class:"token assign-left variable"}},[t._v("meshProvider")]),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v("kubernetes\n")])])]),e("p",[t._v("If you already have a Prometheus instance running in your cluster, you can point Flagger to the ClusterIP service with:")]),t._v(" "),e("div",{staticClass:"language-bash extra-class"},[e("pre",{pre:!0,attrs:{class:"language-bash"}},[e("code",[t._v("helm upgrade -i flagger flagger/flagger "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("\\")]),t._v("\n--namespace flagger "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("\\")]),t._v("\n--set "),e("span",{pre:!0,attrs:{class:"token assign-left variable"}},[t._v("metricsServer")]),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v("http://prometheus.monitoring:9090\n")])])]),e("p",[t._v("Optionally you can enable Slack notifications:")]),t._v(" "),e("div",{staticClass:"language-bash extra-class"},[e("pre",{pre:!0,attrs:{class:"language-bash"}},[e("code",[t._v("helm upgrade -i flagger flagger/flagger "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("\\")]),t._v("\n--reuse-values "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("\\")]),t._v("\n--namespace flagger "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("\\")]),t._v("\n--set slack.url"),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v("https://hooks.slack.com/services/YOUR/SLACK/WEBHOOK "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("\\")]),t._v("\n--set slack.channel"),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v("general "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("\\")]),t._v("\n--set slack.user"),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v("flagger\n")])])]),e("h2",{attrs:{id:"bootstrap"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#bootstrap","aria-hidden":"true"}},[t._v("#")]),t._v(" Bootstrap")]),t._v(" "),e("p",[t._v("Flagger takes a Kubernetes deployment and optionally a horizontal pod autoscaler (HPA),\nthen creates a series of objects (Kubernetes deployment and ClusterIP services).\nThese objects expose the application inside the cluster and drive the canary analysis and Blue/Green promotion.")]),t._v(" "),e("p",[t._v("Create a test namespace:")]),t._v(" "),e("div",{staticClass:"language-bash extra-class"},[e("pre",{pre:!0,attrs:{class:"language-bash"}},[e("code",[t._v("kubectl create ns "),e("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v("test")]),t._v("\n")])])]),e("p",[t._v("Create a deployment and a horizontal pod autoscaler:")]),t._v(" "),e("div",{staticClass:"language-bash extra-class"},[e("pre",{pre:!0,attrs:{class:"language-bash"}},[e("code",[t._v("kubectl apply -k github.com/weaveworks/flagger//kustomize/podinfo\n")])])]),e("p",[t._v("Deploy the load testing service to generate traffic during the analysis:")]),t._v(" "),e("div",{staticClass:"language-bash extra-class"},[e("pre",{pre:!0,attrs:{class:"language-bash"}},[e("code",[t._v("kubectl apply -k github.com/weaveworks/flagger//kustomize/tester\n")])])]),e("p",[t._v("Create a canary custom resource:")]),t._v(" "),e("div",{staticClass:"language-yaml extra-class"},[e("pre",{pre:!0,attrs:{class:"language-yaml"}},[e("code",[e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("apiVersion")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" flagger.app/v1beta1\n"),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("kind")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" Canary\n"),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("metadata")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("name")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" podinfo\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("namespace")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" test\n"),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("spec")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# service mesh provider can be: kubernetes, istio, appmesh, nginx, gloo")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("provider")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" kubernetes\n "),e("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# deployment reference")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("targetRef")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("apiVersion")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" apps/v1\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("kind")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" Deployment\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("name")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" podinfo\n "),e("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# the maximum time in seconds for the canary deployment")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# to make progress before rollback (default 600s)")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("progressDeadlineSeconds")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token number"}},[t._v("60")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# HPA reference (optional)")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("autoscalerRef")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("apiVersion")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" autoscaling/v2beta1\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("kind")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" HorizontalPodAutoscaler\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("name")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" podinfo\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("service")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("port")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token number"}},[t._v("9898")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("portDiscovery")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token boolean important"}},[t._v("true")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("analysis")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# schedule interval (default 60s)")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("interval")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" 30s\n "),e("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# max number of failed checks before rollback")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("threshold")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token number"}},[t._v("2")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# number of checks to run before rollback")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("iterations")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token number"}},[t._v("10")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# Prometheus checks based on ")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# http_request_duration_seconds histogram")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("metrics")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("-")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("name")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" request"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("-")]),t._v("success"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("-")]),t._v("rate\n "),e("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# minimum req success rate (non 5xx responses)")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# percentage (0-100)")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("thresholdRange")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("min")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token number"}},[t._v("99")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("interval")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" 1m\n "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("-")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("name")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" request"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("-")]),t._v("duration\n "),e("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# maximum req duration P99")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# milliseconds")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("thresholdRange")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("max")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token number"}},[t._v("500")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("interval")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" 30s\n "),e("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# acceptance/load testing hooks")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("webhooks")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("-")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("name")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" smoke"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("-")]),t._v("test\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("type")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" pre"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("-")]),t._v("rollout\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("url")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" http"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("//flagger"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("-")]),t._v("loadtester.test/\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("timeout")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" 15s\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("metadata")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("type")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" bash\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("cmd")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token string"}},[t._v("\"curl -sd 'anon' http://podinfo-canary.test:9898/token | grep token\"")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("-")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("name")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" load"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("-")]),t._v("test\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("url")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" http"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("//flagger"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("-")]),t._v("loadtester.test/\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("timeout")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" 5s\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("metadata")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("type")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" cmd\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("cmd")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token string"}},[t._v('"hey -z 1m -q 10 -c 2 http://podinfo-canary.test:9898/"')]),t._v("\n")])])]),e("p",[t._v("The above configuration will run an analysis for five minutes.")]),t._v(" "),e("p",[t._v("Save the above resource as podinfo-canary.yaml and then apply it:")]),t._v(" "),e("div",{staticClass:"language-bash extra-class"},[e("pre",{pre:!0,attrs:{class:"language-bash"}},[e("code",[t._v("kubectl apply -f ./podinfo-canary.yaml\n")])])]),e("p",[t._v("After a couple of seconds Flagger will create the canary objects:")]),t._v(" "),e("div",{staticClass:"language-bash extra-class"},[e("pre",{pre:!0,attrs:{class:"language-bash"}},[e("code",[e("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# applied ")]),t._v("\ndeployment.apps/podinfo\nhorizontalpodautoscaler.autoscaling/podinfo\ncanary.flagger.app/podinfo\n\n"),e("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# generated ")]),t._v("\ndeployment.apps/podinfo-primary\nhorizontalpodautoscaler.autoscaling/podinfo-primary\nservice/podinfo\nservice/podinfo-canary\nservice/podinfo-primary\n")])])]),e("p",[t._v("Blue/Green scenario:")]),t._v(" "),e("ul",[e("li",[t._v("on bootstrap, Flagger will create three ClusterIP services ("),e("code",[t._v("app-primary")]),t._v(","),e("code",[t._v("app-canary")]),t._v(", "),e("code",[t._v("app")]),t._v(")\nand a shadow deployment named "),e("code",[t._v("app-primary")]),t._v(" that represents the blue version")]),t._v(" "),e("li",[t._v("when a new version is detected, Flagger would scale up the green version and run the conformance tests\n(the tests should target the "),e("code",[t._v("app-canary")]),t._v(" ClusterIP service to reach the green version)")]),t._v(" "),e("li",[t._v("if the conformance tests are passing, Flagger would start the load tests and validate them with custom Prometheus queries")]),t._v(" "),e("li",[t._v("if the load test analysis is successful, Flagger will promote the new version to "),e("code",[t._v("app-primary")]),t._v(" and scale down the green version")])]),t._v(" "),e("h2",{attrs:{id:"automated-blue-green-promotion"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#automated-blue-green-promotion","aria-hidden":"true"}},[t._v("#")]),t._v(" Automated Blue/Green promotion")]),t._v(" "),e("p",[t._v("Trigger a deployment by updating the container image:")]),t._v(" "),e("div",{staticClass:"language-bash extra-class"},[e("pre",{pre:!0,attrs:{class:"language-bash"}},[e("code",[t._v("kubectl -n "),e("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v("test")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v("set")]),t._v(" image deployment/podinfo "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("\\")]),t._v("\n"),e("span",{pre:!0,attrs:{class:"token assign-left variable"}},[t._v("podinfod")]),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v("stefanprodan/podinfo:3.1.1\n")])])]),e("p",[t._v("Flagger detects that the deployment revision changed and starts a new rollout:")]),t._v(" "),e("div",{staticClass:"language-text extra-class"},[e("pre",{pre:!0,attrs:{class:"language-text"}},[e("code",[t._v("kubectl -n test describe canary/podinfo\n\nEvents:\n\nNew revision detected podinfo.test\nWaiting for podinfo.test rollout to finish: 0 of 1 updated replicas are available\nPre-rollout check acceptance-test passed\nAdvance podinfo.test canary iteration 1/10\nAdvance podinfo.test canary iteration 2/10\nAdvance podinfo.test canary iteration 3/10\nAdvance podinfo.test canary iteration 4/10\nAdvance podinfo.test canary iteration 5/10\nAdvance podinfo.test canary iteration 6/10\nAdvance podinfo.test canary iteration 7/10\nAdvance podinfo.test canary iteration 8/10\nAdvance podinfo.test canary iteration 9/10\nAdvance podinfo.test canary iteration 10/10\nCopying podinfo.test template spec to podinfo-primary.test\nWaiting for podinfo-primary.test rollout to finish: 1 of 2 updated replicas are available\nPromotion completed! Scaling down podinfo.test\n")])])]),e("p",[e("strong",[t._v("Note")]),t._v(" that if you apply new changes to the deployment during the canary analysis, Flagger will restart the analysis.")]),t._v(" "),e("p",[t._v("You can monitor all canaries with:")]),t._v(" "),e("div",{staticClass:"language-bash extra-class"},[e("pre",{pre:!0,attrs:{class:"language-bash"}},[e("code",[e("span",{pre:!0,attrs:{class:"token function"}},[t._v("watch")]),t._v(" kubectl get canaries --all-namespaces\n\nNAMESPACE NAME STATUS WEIGHT LASTTRANSITIONTIME\n"),e("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v("test")]),t._v(" podinfo Progressing "),e("span",{pre:!0,attrs:{class:"token number"}},[t._v("100")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token number"}},[t._v("2019")]),t._v("-06-16T14:05:07Z\nprod frontend Succeeded "),e("span",{pre:!0,attrs:{class:"token number"}},[t._v("0")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token number"}},[t._v("2019")]),t._v("-06-15T16:15:07Z\nprod backend Failed "),e("span",{pre:!0,attrs:{class:"token number"}},[t._v("0")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token number"}},[t._v("2019")]),t._v("-06-14T17:05:07Z\n")])])]),e("h2",{attrs:{id:"automated-rollback"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#automated-rollback","aria-hidden":"true"}},[t._v("#")]),t._v(" Automated rollback")]),t._v(" "),e("p",[t._v("During the analysis you can generate HTTP 500 errors and high latency to test Flagger's rollback.")]),t._v(" "),e("p",[t._v("Exec into the load tester pod with:")]),t._v(" "),e("div",{staticClass:"language-bash extra-class"},[e("pre",{pre:!0,attrs:{class:"language-bash"}},[e("code",[t._v("kubectl -n "),e("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v("test")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v("exec")]),t._v(" -it flagger-loadtester-xx-xx "),e("span",{pre:!0,attrs:{class:"token function"}},[t._v("sh")]),t._v("\n")])])]),e("p",[t._v("Generate HTTP 500 errors:")]),t._v(" "),e("div",{staticClass:"language-bash extra-class"},[e("pre",{pre:!0,attrs:{class:"language-bash"}},[e("code",[e("span",{pre:!0,attrs:{class:"token function"}},[t._v("watch")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token function"}},[t._v("curl")]),t._v(" http://podinfo-canary.test:9898/status/500\n")])])]),e("p",[t._v("Generate latency:")]),t._v(" "),e("div",{staticClass:"language-bash extra-class"},[e("pre",{pre:!0,attrs:{class:"language-bash"}},[e("code",[e("span",{pre:!0,attrs:{class:"token function"}},[t._v("watch")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token function"}},[t._v("curl")]),t._v(" http://podinfo-canary.test:9898/delay/1\n")])])]),e("p",[t._v("When the number of failed checks reaches the analysis threshold, the green version is scaled to zero and the rollout is marked as failed.")]),t._v(" "),e("div",{staticClass:"language-text extra-class"},[e("pre",{pre:!0,attrs:{class:"language-text"}},[e("code",[t._v("kubectl -n test describe canary/podinfo\n\nStatus:\n Failed Checks: 2\n Phase: Failed\nEvents:\n Type Reason Age From Message\n ---- ------ ---- ---- -------\n Normal Synced 3m flagger New revision detected podinfo.test\n Normal Synced 3m flagger Advance podinfo.test canary iteration 1/10\n Normal Synced 3m flagger Advance podinfo.test canary iteration 2/10\n Normal Synced 3m flagger Advance podinfo.test canary iteration 3/10\n Normal Synced 3m flagger Halt podinfo.test advancement success rate 69.17% < 99%\n Normal Synced 2m flagger Halt podinfo.test advancement success rate 61.39% < 99%\n Warning Synced 2m flagger Rolling back podinfo.test failed checks threshold reached 2\n Warning Synced 1m flagger Canary failed! Scaling down podinfo.test\n")])])]),e("h2",{attrs:{id:"custom-metrics"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#custom-metrics","aria-hidden":"true"}},[t._v("#")]),t._v(" Custom metrics")]),t._v(" "),e("p",[t._v("The analysis can be extended with Prometheus queries. The demo app is instrumented with Prometheus so you can\ncreate a custom check that will use the HTTP request duration histogram to validate the canary (green version).")]),t._v(" "),e("p",[t._v("Create a metric template and apply it on the cluster:")]),t._v(" "),e("div",{staticClass:"language-yaml extra-class"},[e("pre",{pre:!0,attrs:{class:"language-yaml"}},[e("code",[e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("apiVersion")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" flagger.app/v1beta1\n"),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("kind")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" MetricTemplate\n"),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("metadata")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("name")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" not"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("-")]),t._v("found"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("-")]),t._v("percentage\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("namespace")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" test\n"),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("spec")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("provider")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("type")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" prometheus\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("address")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" http"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("//flagger"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("-")]),t._v("promethues.flagger"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),e("span",{pre:!0,attrs:{class:"token number"}},[t._v("9090")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("query")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("|")]),e("span",{pre:!0,attrs:{class:"token scalar string"}},[t._v('\n 100 - sum(\n rate(\n http_request_duration_seconds_count{\n kubernetes_namespace="{{ namespace }}",\n kubernetes_pod_name=~"{{ target }}-[0-9a-zA-Z]+(-[0-9a-zA-Z]+)"\n status!="{{ interval }}"\n }[1m]\n )\n )\n /\n sum(\n rate(\n http_request_duration_seconds_count{\n kubernetes_namespace="{{ namespace }}",\n kubernetes_pod_name=~"{{ target }}-[0-9a-zA-Z]+(-[0-9a-zA-Z]+)"\n }[{{ interval }}]\n )\n ) * 100')]),t._v("\n")])])]),e("p",[t._v("Edit the canary analysis and add the following metric:")]),t._v(" "),e("div",{staticClass:"language-yaml extra-class"},[e("pre",{pre:!0,attrs:{class:"language-yaml"}},[e("code",[t._v(" "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("analysis")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("metrics")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("-")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("name")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token string"}},[t._v('"404s percentage"')]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("templateRef")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("name")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" not"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("-")]),t._v("found"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("-")]),t._v("percentage\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("thresholdRange")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("max")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token number"}},[t._v("5")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("interval")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" 1m\n")])])]),e("p",[t._v("The above configuration validates the canary (green version) by checking if the HTTP 404 req/sec percentage is\nbelow 5 percent of the total traffic. If the 404s rate reaches the 5% threshold, then the rollout is rolled back.")]),t._v(" "),e("p",[t._v("Trigger a deployment by updating the container image:")]),t._v(" "),e("div",{staticClass:"language-bash extra-class"},[e("pre",{pre:!0,attrs:{class:"language-bash"}},[e("code",[t._v("kubectl -n "),e("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v("test")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v("set")]),t._v(" image deployment/podinfo "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("\\")]),t._v("\n"),e("span",{pre:!0,attrs:{class:"token assign-left variable"}},[t._v("podinfod")]),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v("stefanprodan/podinfo:3.1.3\n")])])]),e("p",[t._v("Generate 404s:")]),t._v(" "),e("div",{staticClass:"language-bash extra-class"},[e("pre",{pre:!0,attrs:{class:"language-bash"}},[e("code",[e("span",{pre:!0,attrs:{class:"token function"}},[t._v("watch")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token function"}},[t._v("curl")]),t._v(" http://podinfo-canary.test:9898/status/400\n")])])]),e("p",[t._v("Watch Flagger logs:")]),t._v(" "),e("div",{staticClass:"language-text extra-class"},[e("pre",{pre:!0,attrs:{class:"language-text"}},[e("code",[t._v("kubectl -n flagger logs deployment/flagger -f | jq .msg\n\nNew revision detected podinfo.test\nScaling up podinfo.test\nAdvance podinfo.test canary iteration 1/10\nHalt podinfo.test advancement 404s percentage 6.20 > 5\nHalt podinfo.test advancement 404s percentage 6.45 > 5\nRolling back podinfo.test failed checks threshold reached 2\nCanary failed! Scaling down podinfo.test\n")])])]),e("p",[t._v("If you have "),e("router-link",{attrs:{to:"/usage/alerting.html"}},[t._v("alerting")]),t._v(" configured,\nFlagger will send a notification with the reason why the canary failed.")],1),t._v(" "),e("h2",{attrs:{id:"conformance-testing-with-helm"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#conformance-testing-with-helm","aria-hidden":"true"}},[t._v("#")]),t._v(" Conformance Testing with Helm")]),t._v(" "),e("p",[t._v("Flagger comes with a testing service that can run Helm tests when configured as a pre-rollout webhook.")]),t._v(" "),e("p",[t._v("Deploy the Helm test runner in the "),e("code",[t._v("kube-system")]),t._v(" namespace using the "),e("code",[t._v("tiller")]),t._v(" service account:")]),t._v(" "),e("div",{staticClass:"language-bash extra-class"},[e("pre",{pre:!0,attrs:{class:"language-bash"}},[e("code",[t._v("helm repo "),e("span",{pre:!0,attrs:{class:"token function"}},[t._v("add")]),t._v(" flagger https://flagger.app\n\nhelm upgrade -i flagger-helmtester flagger/loadtester "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("\\")]),t._v("\n--namespace"),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v("kube-system "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("\\")]),t._v("\n--set "),e("span",{pre:!0,attrs:{class:"token assign-left variable"}},[t._v("serviceAccountName")]),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v("tiller\n")])])]),e("p",[t._v("When deployed the Helm tester API will be available at "),e("code",[t._v("http://flagger-helmtester.kube-system/")]),t._v(".")]),t._v(" "),e("p",[t._v("Add a helm test pre-rollout hook to your chart:")]),t._v(" "),e("div",{staticClass:"language-yaml extra-class"},[e("pre",{pre:!0,attrs:{class:"language-yaml"}},[e("code",[t._v(" "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("analysis")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("webhooks")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("-")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("name")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token string"}},[t._v('"conformance testing"')]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("type")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" pre"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("-")]),t._v("rollout\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("url")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" http"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("//flagger"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("-")]),t._v("helmtester.kube"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("-")]),t._v("system/\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("timeout")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" 3m\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("metadata")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("type")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token string"}},[t._v('"helm"')]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("cmd")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token string"}},[t._v('"test {{ .Release.Name }} --cleanup"')]),t._v("\n")])])]),e("p",[t._v("When the canary analysis starts, Flagger will call the pre-rollout webhooks.\nIf the helm test fails, Flagger will retry until the analysis threshold is reached and the canary is rolled back.")]),t._v(" "),e("p",[t._v("For an in-depth look at the analysis process read the "),e("router-link",{attrs:{to:"/usage/how-it-works.html"}},[t._v("usage docs")]),t._v(".")],1)])},[],!1,null,null,null);a.default=n.exports}}]); \ No newline at end of file +(window.webpackJsonp=window.webpackJsonp||[]).push([[25],{286:function(t,a,e){"use strict";e.r(a);var s=e(37),n=Object(s.a)({},function(){var t=this,a=t.$createElement,e=t._self._c||a;return e("ContentSlotsDistributor",{attrs:{"slot-key":t.$parent.slotKey}},[e("h1",{attrs:{id:"blue-green-deployments"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#blue-green-deployments","aria-hidden":"true"}},[t._v("#")]),t._v(" Blue/Green Deployments")]),t._v(" "),e("p",[t._v("This guide shows you how to automate Blue/Green deployments with Flagger and Kubernetes.")]),t._v(" "),e("p",[t._v("For applications that are not deployed on a service mesh, Flagger can orchestrate Blue/Green style deployments\nwith Kubernetes L4 networking. When using a service mesh blue/green can be used as\nspecified "),e("router-link",{attrs:{to:"/usage/deployment-strategies.html"}},[t._v("here")]),t._v(".")],1),t._v(" "),e("p",[e("img",{attrs:{src:"https://raw.githubusercontent.com/weaveworks/flagger/master/docs/diagrams/flagger-bluegreen-steps.png",alt:"Flagger Blue/Green Stages"}})]),t._v(" "),e("h2",{attrs:{id:"prerequisites"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#prerequisites","aria-hidden":"true"}},[t._v("#")]),t._v(" Prerequisites")]),t._v(" "),e("p",[t._v("Flagger requires a Kubernetes cluster "),e("strong",[t._v("v1.11")]),t._v(" or newer.")]),t._v(" "),e("p",[t._v("Install Flagger and the Prometheus add-on:")]),t._v(" "),e("div",{staticClass:"language-bash extra-class"},[e("pre",{pre:!0,attrs:{class:"language-bash"}},[e("code",[t._v("helm repo "),e("span",{pre:!0,attrs:{class:"token function"}},[t._v("add")]),t._v(" flagger https://flagger.app\n\nhelm upgrade -i flagger flagger/flagger "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("\\")]),t._v("\n--namespace flagger "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("\\")]),t._v("\n--set prometheus.install"),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v("true "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("\\")]),t._v("\n--set "),e("span",{pre:!0,attrs:{class:"token assign-left variable"}},[t._v("meshProvider")]),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v("kubernetes\n")])])]),e("p",[t._v("If you already have a Prometheus instance running in your cluster, you can point Flagger to the ClusterIP service with:")]),t._v(" "),e("div",{staticClass:"language-bash extra-class"},[e("pre",{pre:!0,attrs:{class:"language-bash"}},[e("code",[t._v("helm upgrade -i flagger flagger/flagger "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("\\")]),t._v("\n--namespace flagger "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("\\")]),t._v("\n--set "),e("span",{pre:!0,attrs:{class:"token assign-left variable"}},[t._v("metricsServer")]),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v("http://prometheus.monitoring:9090\n")])])]),e("p",[t._v("Optionally you can enable Slack notifications:")]),t._v(" "),e("div",{staticClass:"language-bash extra-class"},[e("pre",{pre:!0,attrs:{class:"language-bash"}},[e("code",[t._v("helm upgrade -i flagger flagger/flagger "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("\\")]),t._v("\n--reuse-values "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("\\")]),t._v("\n--namespace flagger "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("\\")]),t._v("\n--set slack.url"),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v("https://hooks.slack.com/services/YOUR/SLACK/WEBHOOK "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("\\")]),t._v("\n--set slack.channel"),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v("general "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("\\")]),t._v("\n--set slack.user"),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v("flagger\n")])])]),e("h2",{attrs:{id:"bootstrap"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#bootstrap","aria-hidden":"true"}},[t._v("#")]),t._v(" Bootstrap")]),t._v(" "),e("p",[t._v("Flagger takes a Kubernetes deployment and optionally a horizontal pod autoscaler (HPA),\nthen creates a series of objects (Kubernetes deployment and ClusterIP services).\nThese objects expose the application inside the cluster and drive the canary analysis and Blue/Green promotion.")]),t._v(" "),e("p",[t._v("Create a test namespace:")]),t._v(" "),e("div",{staticClass:"language-bash extra-class"},[e("pre",{pre:!0,attrs:{class:"language-bash"}},[e("code",[t._v("kubectl create ns "),e("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v("test")]),t._v("\n")])])]),e("p",[t._v("Create a deployment and a horizontal pod autoscaler:")]),t._v(" "),e("div",{staticClass:"language-bash extra-class"},[e("pre",{pre:!0,attrs:{class:"language-bash"}},[e("code",[t._v("kubectl apply -k github.com/weaveworks/flagger//kustomize/podinfo\n")])])]),e("p",[t._v("Deploy the load testing service to generate traffic during the analysis:")]),t._v(" "),e("div",{staticClass:"language-bash extra-class"},[e("pre",{pre:!0,attrs:{class:"language-bash"}},[e("code",[t._v("kubectl apply -k github.com/weaveworks/flagger//kustomize/tester\n")])])]),e("p",[t._v("Create a canary custom resource:")]),t._v(" "),e("div",{staticClass:"language-yaml extra-class"},[e("pre",{pre:!0,attrs:{class:"language-yaml"}},[e("code",[e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("apiVersion")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" flagger.app/v1beta1\n"),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("kind")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" Canary\n"),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("metadata")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("name")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" podinfo\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("namespace")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" test\n"),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("spec")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# service mesh provider can be: kubernetes, istio, appmesh, nginx, gloo")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("provider")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" kubernetes\n "),e("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# deployment reference")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("targetRef")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("apiVersion")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" apps/v1\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("kind")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" Deployment\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("name")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" podinfo\n "),e("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# the maximum time in seconds for the canary deployment")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# to make progress before rollback (default 600s)")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("progressDeadlineSeconds")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token number"}},[t._v("60")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# HPA reference (optional)")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("autoscalerRef")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("apiVersion")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" autoscaling/v2beta1\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("kind")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" HorizontalPodAutoscaler\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("name")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" podinfo\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("service")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("port")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token number"}},[t._v("9898")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("portDiscovery")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token boolean important"}},[t._v("true")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("analysis")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# schedule interval (default 60s)")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("interval")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" 30s\n "),e("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# max number of failed checks before rollback")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("threshold")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token number"}},[t._v("2")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# number of checks to run before rollback")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("iterations")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token number"}},[t._v("10")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# Prometheus checks based on ")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# http_request_duration_seconds histogram")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("metrics")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("-")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("name")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" request"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("-")]),t._v("success"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("-")]),t._v("rate\n "),e("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# minimum req success rate (non 5xx responses)")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# percentage (0-100)")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("thresholdRange")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("min")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token number"}},[t._v("99")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("interval")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" 1m\n "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("-")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("name")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" request"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("-")]),t._v("duration\n "),e("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# maximum req duration P99")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# milliseconds")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("thresholdRange")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("max")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token number"}},[t._v("500")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("interval")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" 30s\n "),e("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# acceptance/load testing hooks")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("webhooks")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("-")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("name")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" smoke"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("-")]),t._v("test\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("type")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" pre"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("-")]),t._v("rollout\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("url")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" http"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("//flagger"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("-")]),t._v("loadtester.test/\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("timeout")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" 15s\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("metadata")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("type")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" bash\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("cmd")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token string"}},[t._v("\"curl -sd 'anon' http://podinfo-canary.test:9898/token | grep token\"")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("-")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("name")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" load"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("-")]),t._v("test\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("url")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" http"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("//flagger"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("-")]),t._v("loadtester.test/\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("timeout")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" 5s\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("metadata")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("type")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" cmd\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("cmd")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token string"}},[t._v('"hey -z 1m -q 10 -c 2 http://podinfo-canary.test:9898/"')]),t._v("\n")])])]),e("p",[t._v("The above configuration will run an analysis for five minutes.")]),t._v(" "),e("p",[t._v("Save the above resource as podinfo-canary.yaml and then apply it:")]),t._v(" "),e("div",{staticClass:"language-bash extra-class"},[e("pre",{pre:!0,attrs:{class:"language-bash"}},[e("code",[t._v("kubectl apply -f ./podinfo-canary.yaml\n")])])]),e("p",[t._v("After a couple of seconds Flagger will create the canary objects:")]),t._v(" "),e("div",{staticClass:"language-bash extra-class"},[e("pre",{pre:!0,attrs:{class:"language-bash"}},[e("code",[e("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# applied ")]),t._v("\ndeployment.apps/podinfo\nhorizontalpodautoscaler.autoscaling/podinfo\ncanary.flagger.app/podinfo\n\n"),e("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# generated ")]),t._v("\ndeployment.apps/podinfo-primary\nhorizontalpodautoscaler.autoscaling/podinfo-primary\nservice/podinfo\nservice/podinfo-canary\nservice/podinfo-primary\n")])])]),e("p",[t._v("Blue/Green scenario:")]),t._v(" "),e("ul",[e("li",[t._v("on bootstrap, Flagger will create three ClusterIP services ("),e("code",[t._v("app-primary")]),t._v(","),e("code",[t._v("app-canary")]),t._v(", "),e("code",[t._v("app")]),t._v(")\nand a shadow deployment named "),e("code",[t._v("app-primary")]),t._v(" that represents the blue version")]),t._v(" "),e("li",[t._v("when a new version is detected, Flagger would scale up the green version and run the conformance tests\n(the tests should target the "),e("code",[t._v("app-canary")]),t._v(" ClusterIP service to reach the green version)")]),t._v(" "),e("li",[t._v("if the conformance tests are passing, Flagger would start the load tests and validate them with custom Prometheus queries")]),t._v(" "),e("li",[t._v("if the load test analysis is successful, Flagger will promote the new version to "),e("code",[t._v("app-primary")]),t._v(" and scale down the green version")])]),t._v(" "),e("h2",{attrs:{id:"automated-blue-green-promotion"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#automated-blue-green-promotion","aria-hidden":"true"}},[t._v("#")]),t._v(" Automated Blue/Green promotion")]),t._v(" "),e("p",[t._v("Trigger a deployment by updating the container image:")]),t._v(" "),e("div",{staticClass:"language-bash extra-class"},[e("pre",{pre:!0,attrs:{class:"language-bash"}},[e("code",[t._v("kubectl -n "),e("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v("test")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v("set")]),t._v(" image deployment/podinfo "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("\\")]),t._v("\n"),e("span",{pre:!0,attrs:{class:"token assign-left variable"}},[t._v("podinfod")]),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v("stefanprodan/podinfo:3.1.1\n")])])]),e("p",[t._v("Flagger detects that the deployment revision changed and starts a new rollout:")]),t._v(" "),e("div",{staticClass:"language-text extra-class"},[e("pre",{pre:!0,attrs:{class:"language-text"}},[e("code",[t._v("kubectl -n test describe canary/podinfo\n\nEvents:\n\nNew revision detected podinfo.test\nWaiting for podinfo.test rollout to finish: 0 of 1 updated replicas are available\nPre-rollout check acceptance-test passed\nAdvance podinfo.test canary iteration 1/10\nAdvance podinfo.test canary iteration 2/10\nAdvance podinfo.test canary iteration 3/10\nAdvance podinfo.test canary iteration 4/10\nAdvance podinfo.test canary iteration 5/10\nAdvance podinfo.test canary iteration 6/10\nAdvance podinfo.test canary iteration 7/10\nAdvance podinfo.test canary iteration 8/10\nAdvance podinfo.test canary iteration 9/10\nAdvance podinfo.test canary iteration 10/10\nCopying podinfo.test template spec to podinfo-primary.test\nWaiting for podinfo-primary.test rollout to finish: 1 of 2 updated replicas are available\nPromotion completed! Scaling down podinfo.test\n")])])]),e("p",[e("strong",[t._v("Note")]),t._v(" that if you apply new changes to the deployment during the canary analysis, Flagger will restart the analysis.")]),t._v(" "),e("p",[t._v("You can monitor all canaries with:")]),t._v(" "),e("div",{staticClass:"language-bash extra-class"},[e("pre",{pre:!0,attrs:{class:"language-bash"}},[e("code",[e("span",{pre:!0,attrs:{class:"token function"}},[t._v("watch")]),t._v(" kubectl get canaries --all-namespaces\n\nNAMESPACE NAME STATUS WEIGHT LASTTRANSITIONTIME\n"),e("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v("test")]),t._v(" podinfo Progressing "),e("span",{pre:!0,attrs:{class:"token number"}},[t._v("100")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token number"}},[t._v("2019")]),t._v("-06-16T14:05:07Z\nprod frontend Succeeded "),e("span",{pre:!0,attrs:{class:"token number"}},[t._v("0")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token number"}},[t._v("2019")]),t._v("-06-15T16:15:07Z\nprod backend Failed "),e("span",{pre:!0,attrs:{class:"token number"}},[t._v("0")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token number"}},[t._v("2019")]),t._v("-06-14T17:05:07Z\n")])])]),e("h2",{attrs:{id:"automated-rollback"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#automated-rollback","aria-hidden":"true"}},[t._v("#")]),t._v(" Automated rollback")]),t._v(" "),e("p",[t._v("During the analysis you can generate HTTP 500 errors and high latency to test Flagger's rollback.")]),t._v(" "),e("p",[t._v("Exec into the load tester pod with:")]),t._v(" "),e("div",{staticClass:"language-bash extra-class"},[e("pre",{pre:!0,attrs:{class:"language-bash"}},[e("code",[t._v("kubectl -n "),e("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v("test")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v("exec")]),t._v(" -it flagger-loadtester-xx-xx "),e("span",{pre:!0,attrs:{class:"token function"}},[t._v("sh")]),t._v("\n")])])]),e("p",[t._v("Generate HTTP 500 errors:")]),t._v(" "),e("div",{staticClass:"language-bash extra-class"},[e("pre",{pre:!0,attrs:{class:"language-bash"}},[e("code",[e("span",{pre:!0,attrs:{class:"token function"}},[t._v("watch")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token function"}},[t._v("curl")]),t._v(" http://podinfo-canary.test:9898/status/500\n")])])]),e("p",[t._v("Generate latency:")]),t._v(" "),e("div",{staticClass:"language-bash extra-class"},[e("pre",{pre:!0,attrs:{class:"language-bash"}},[e("code",[e("span",{pre:!0,attrs:{class:"token function"}},[t._v("watch")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token function"}},[t._v("curl")]),t._v(" http://podinfo-canary.test:9898/delay/1\n")])])]),e("p",[t._v("When the number of failed checks reaches the analysis threshold, the green version is scaled to zero and the rollout is marked as failed.")]),t._v(" "),e("div",{staticClass:"language-text extra-class"},[e("pre",{pre:!0,attrs:{class:"language-text"}},[e("code",[t._v("kubectl -n test describe canary/podinfo\n\nStatus:\n Failed Checks: 2\n Phase: Failed\nEvents:\n Type Reason Age From Message\n ---- ------ ---- ---- -------\n Normal Synced 3m flagger New revision detected podinfo.test\n Normal Synced 3m flagger Advance podinfo.test canary iteration 1/10\n Normal Synced 3m flagger Advance podinfo.test canary iteration 2/10\n Normal Synced 3m flagger Advance podinfo.test canary iteration 3/10\n Normal Synced 3m flagger Halt podinfo.test advancement success rate 69.17% < 99%\n Normal Synced 2m flagger Halt podinfo.test advancement success rate 61.39% < 99%\n Warning Synced 2m flagger Rolling back podinfo.test failed checks threshold reached 2\n Warning Synced 1m flagger Canary failed! Scaling down podinfo.test\n")])])]),e("h2",{attrs:{id:"custom-metrics"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#custom-metrics","aria-hidden":"true"}},[t._v("#")]),t._v(" Custom metrics")]),t._v(" "),e("p",[t._v("The analysis can be extended with Prometheus queries. The demo app is instrumented with Prometheus so you can\ncreate a custom check that will use the HTTP request duration histogram to validate the canary (green version).")]),t._v(" "),e("p",[t._v("Create a metric template and apply it on the cluster:")]),t._v(" "),e("div",{staticClass:"language-yaml extra-class"},[e("pre",{pre:!0,attrs:{class:"language-yaml"}},[e("code",[e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("apiVersion")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" flagger.app/v1beta1\n"),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("kind")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" MetricTemplate\n"),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("metadata")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("name")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" not"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("-")]),t._v("found"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("-")]),t._v("percentage\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("namespace")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" test\n"),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("spec")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("provider")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("type")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" prometheus\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("address")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" http"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("//flagger"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("-")]),t._v("promethues.flagger"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),e("span",{pre:!0,attrs:{class:"token number"}},[t._v("9090")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("query")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("|")]),e("span",{pre:!0,attrs:{class:"token scalar string"}},[t._v('\n 100 - sum(\n rate(\n http_request_duration_seconds_count{\n kubernetes_namespace="{{ namespace }}",\n kubernetes_pod_name=~"{{ target }}-[0-9a-zA-Z]+(-[0-9a-zA-Z]+)"\n status!="{{ interval }}"\n }[1m]\n )\n )\n /\n sum(\n rate(\n http_request_duration_seconds_count{\n kubernetes_namespace="{{ namespace }}",\n kubernetes_pod_name=~"{{ target }}-[0-9a-zA-Z]+(-[0-9a-zA-Z]+)"\n }[{{ interval }}]\n )\n ) * 100')]),t._v("\n")])])]),e("p",[t._v("Edit the canary analysis and add the following metric:")]),t._v(" "),e("div",{staticClass:"language-yaml extra-class"},[e("pre",{pre:!0,attrs:{class:"language-yaml"}},[e("code",[t._v(" "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("analysis")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("metrics")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("-")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("name")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token string"}},[t._v('"404s percentage"')]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("templateRef")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("name")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" not"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("-")]),t._v("found"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("-")]),t._v("percentage\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("thresholdRange")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("max")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token number"}},[t._v("5")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("interval")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" 1m\n")])])]),e("p",[t._v("The above configuration validates the canary (green version) by checking if the HTTP 404 req/sec percentage is\nbelow 5 percent of the total traffic. If the 404s rate reaches the 5% threshold, then the rollout is rolled back.")]),t._v(" "),e("p",[t._v("Trigger a deployment by updating the container image:")]),t._v(" "),e("div",{staticClass:"language-bash extra-class"},[e("pre",{pre:!0,attrs:{class:"language-bash"}},[e("code",[t._v("kubectl -n "),e("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v("test")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v("set")]),t._v(" image deployment/podinfo "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("\\")]),t._v("\n"),e("span",{pre:!0,attrs:{class:"token assign-left variable"}},[t._v("podinfod")]),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v("stefanprodan/podinfo:3.1.3\n")])])]),e("p",[t._v("Generate 404s:")]),t._v(" "),e("div",{staticClass:"language-bash extra-class"},[e("pre",{pre:!0,attrs:{class:"language-bash"}},[e("code",[e("span",{pre:!0,attrs:{class:"token function"}},[t._v("watch")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token function"}},[t._v("curl")]),t._v(" http://podinfo-canary.test:9898/status/400\n")])])]),e("p",[t._v("Watch Flagger logs:")]),t._v(" "),e("div",{staticClass:"language-text extra-class"},[e("pre",{pre:!0,attrs:{class:"language-text"}},[e("code",[t._v("kubectl -n flagger logs deployment/flagger -f | jq .msg\n\nNew revision detected podinfo.test\nScaling up podinfo.test\nAdvance podinfo.test canary iteration 1/10\nHalt podinfo.test advancement 404s percentage 6.20 > 5\nHalt podinfo.test advancement 404s percentage 6.45 > 5\nRolling back podinfo.test failed checks threshold reached 2\nCanary failed! Scaling down podinfo.test\n")])])]),e("p",[t._v("If you have "),e("router-link",{attrs:{to:"/usage/alerting.html"}},[t._v("alerting")]),t._v(" configured,\nFlagger will send a notification with the reason why the canary failed.")],1),t._v(" "),e("h2",{attrs:{id:"conformance-testing-with-helm"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#conformance-testing-with-helm","aria-hidden":"true"}},[t._v("#")]),t._v(" Conformance Testing with Helm")]),t._v(" "),e("p",[t._v("Flagger comes with a testing service that can run Helm tests when configured as a pre-rollout webhook.")]),t._v(" "),e("p",[t._v("Deploy the Helm test runner in the "),e("code",[t._v("kube-system")]),t._v(" namespace using the "),e("code",[t._v("tiller")]),t._v(" service account:")]),t._v(" "),e("div",{staticClass:"language-bash extra-class"},[e("pre",{pre:!0,attrs:{class:"language-bash"}},[e("code",[t._v("helm repo "),e("span",{pre:!0,attrs:{class:"token function"}},[t._v("add")]),t._v(" flagger https://flagger.app\n\nhelm upgrade -i flagger-helmtester flagger/loadtester "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("\\")]),t._v("\n--namespace"),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v("kube-system "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("\\")]),t._v("\n--set "),e("span",{pre:!0,attrs:{class:"token assign-left variable"}},[t._v("serviceAccountName")]),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v("tiller\n")])])]),e("p",[t._v("When deployed the Helm tester API will be available at "),e("code",[t._v("http://flagger-helmtester.kube-system/")]),t._v(".")]),t._v(" "),e("p",[t._v("Add a helm test pre-rollout hook to your chart:")]),t._v(" "),e("div",{staticClass:"language-yaml extra-class"},[e("pre",{pre:!0,attrs:{class:"language-yaml"}},[e("code",[t._v(" "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("analysis")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("webhooks")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("-")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("name")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token string"}},[t._v('"conformance testing"')]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("type")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" pre"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("-")]),t._v("rollout\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("url")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" http"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("//flagger"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("-")]),t._v("helmtester.kube"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("-")]),t._v("system/\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("timeout")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" 3m\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("metadata")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("type")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token string"}},[t._v('"helm"')]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("cmd")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token string"}},[t._v('"test {{ .Release.Name }} --cleanup"')]),t._v("\n")])])]),e("p",[t._v("When the canary analysis starts, Flagger will call the pre-rollout webhooks.\nIf the helm test fails, Flagger will retry until the analysis threshold is reached and the canary is rolled back.")]),t._v(" "),e("p",[t._v("For an in-depth look at the analysis process read the "),e("router-link",{attrs:{to:"/usage/how-it-works.html"}},[t._v("usage docs")]),t._v(".")],1)])},[],!1,null,null,null);a.default=n.exports}}]); \ No newline at end of file diff --git a/assets/js/26.b2d4f402.js b/assets/js/26.b2d05e06.js similarity index 99% rename from assets/js/26.b2d4f402.js rename to assets/js/26.b2d05e06.js index 54f82443..ceb585c3 100644 --- a/assets/js/26.b2d4f402.js +++ b/assets/js/26.b2d05e06.js @@ -1 +1 @@ -(window.webpackJsonp=window.webpackJsonp||[]).push([[26],{286:function(t,a,e){"use strict";e.r(a);var s=e(37),n=Object(s.a)({},function(){var t=this,a=t.$createElement,e=t._self._c||a;return e("ContentSlotsDistributor",{attrs:{"slot-key":t.$parent.slotKey}},[e("h1",{attrs:{id:"linkerd-canary-deployments"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#linkerd-canary-deployments","aria-hidden":"true"}},[t._v("#")]),t._v(" Linkerd Canary Deployments")]),t._v(" "),e("p",[t._v("This guide shows you how to use Linkerd and Flagger to automate canary deployments.")]),t._v(" "),e("p",[e("img",{attrs:{src:"https://raw.githubusercontent.com/weaveworks/flagger/master/docs/diagrams/flagger-linkerd-traffic-split.png",alt:"Flagger Linkerd Traffic Split"}})]),t._v(" "),e("h2",{attrs:{id:"prerequisites"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#prerequisites","aria-hidden":"true"}},[t._v("#")]),t._v(" Prerequisites")]),t._v(" "),e("p",[t._v("Flagger requires a Kubernetes cluster "),e("strong",[t._v("v1.11")]),t._v(" or newer and Linkerd "),e("strong",[t._v("2.4")]),t._v(" or newer.")]),t._v(" "),e("p",[t._v("Install Flagger in the linkerd namespace:")]),t._v(" "),e("div",{staticClass:"language-bash extra-class"},[e("pre",{pre:!0,attrs:{class:"language-bash"}},[e("code",[t._v("kubectl apply -k github.com/weaveworks/flagger//kustomize/linkerd\n")])])]),e("p",[t._v("Note that you'll need kubectl 1.14 or newer to run the above command.")]),t._v(" "),e("h2",{attrs:{id:"bootstrap"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#bootstrap","aria-hidden":"true"}},[t._v("#")]),t._v(" Bootstrap")]),t._v(" "),e("p",[t._v("Flagger takes a Kubernetes deployment and optionally a horizontal pod autoscaler (HPA),\nthen creates a series of objects (Kubernetes deployments, ClusterIP services and SMI traffic split).\nThese objects expose the application inside the mesh and drive the canary analysis and promotion.")]),t._v(" "),e("p",[t._v("Create a test namespace and enable Linkerd proxy injection:")]),t._v(" "),e("div",{staticClass:"language-bash extra-class"},[e("pre",{pre:!0,attrs:{class:"language-bash"}},[e("code",[t._v("kubectl create ns "),e("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v("test")]),t._v("\nkubectl annotate namespace "),e("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v("test")]),t._v(" linkerd.io/inject"),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v("enabled\n")])])]),e("p",[t._v("Install the load testing service to generate traffic during the canary analysis:")]),t._v(" "),e("div",{staticClass:"language-bash extra-class"},[e("pre",{pre:!0,attrs:{class:"language-bash"}},[e("code",[t._v("kubectl apply -k github.com/weaveworks/flagger//kustomize/tester\n")])])]),e("p",[t._v("Create a deployment and a horizontal pod autoscaler:")]),t._v(" "),e("div",{staticClass:"language-bash extra-class"},[e("pre",{pre:!0,attrs:{class:"language-bash"}},[e("code",[t._v("kubectl apply -k github.com/weaveworks/flagger//kustomize/podinfo\n")])])]),e("p",[t._v("Create a canary custom resource for the podinfo deployment:")]),t._v(" "),e("div",{staticClass:"language-yaml extra-class"},[e("pre",{pre:!0,attrs:{class:"language-yaml"}},[e("code",[e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("apiVersion")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" flagger.app/v1beta1\n"),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("kind")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" Canary\n"),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("metadata")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("name")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" podinfo\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("namespace")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" test\n"),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("spec")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# deployment reference")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("targetRef")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("apiVersion")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" apps/v1\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("kind")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" Deployment\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("name")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" podinfo\n "),e("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# HPA reference (optional)")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("autoscalerRef")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("apiVersion")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" autoscaling/v2beta1\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("kind")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" HorizontalPodAutoscaler\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("name")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" podinfo\n "),e("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# the maximum time in seconds for the canary deployment")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# to make progress before it is rollback (default 600s)")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("progressDeadlineSeconds")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token number"}},[t._v("60")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("service")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# ClusterIP port number")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("port")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token number"}},[t._v("9898")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# container port number or name (optional)")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("targetPort")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token number"}},[t._v("9898")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("analysis")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# schedule interval (default 60s)")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("interval")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" 30s\n "),e("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# max number of failed metric checks before rollback")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("threshold")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token number"}},[t._v("5")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# max traffic percentage routed to canary")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# percentage (0-100)")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("maxWeight")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token number"}},[t._v("50")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# canary increment step")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# percentage (0-100)")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("stepWeight")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token number"}},[t._v("5")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# Linkerd Prometheus checks")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("metrics")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("-")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("name")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" request"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("-")]),t._v("success"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("-")]),t._v("rate\n "),e("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# minimum req success rate (non 5xx responses)")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# percentage (0-100)")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("thresholdRange")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("min")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token number"}},[t._v("99")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("interval")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" 1m\n "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("-")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("name")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" request"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("-")]),t._v("duration\n "),e("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# maximum req duration P99")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# milliseconds")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("thresholdRange")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("max")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token number"}},[t._v("500")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("interval")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" 30s\n "),e("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# testing (optional)")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("webhooks")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("-")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("name")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" acceptance"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("-")]),t._v("test\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("type")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" pre"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("-")]),t._v("rollout\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("url")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" http"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("//flagger"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("-")]),t._v("loadtester.test/\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("timeout")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" 30s\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("metadata")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("type")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" bash\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("cmd")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token string"}},[t._v("\"curl -sd 'test' http://podinfo-canary.test:9898/token | grep token\"")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("-")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("name")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" load"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("-")]),t._v("test\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("type")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" rollout\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("url")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" http"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("//flagger"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("-")]),t._v("loadtester.test/\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("metadata")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("cmd")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token string"}},[t._v('"hey -z 2m -q 10 -c 2 http://podinfo-canary.test:9898/"')]),t._v("\n")])])]),e("p",[t._v("Save the above resource as podinfo-canary.yaml and then apply it:")]),t._v(" "),e("div",{staticClass:"language-bash extra-class"},[e("pre",{pre:!0,attrs:{class:"language-bash"}},[e("code",[t._v("kubectl apply -f ./podinfo-canary.yaml\n")])])]),e("p",[t._v("When the canary analysis starts, Flagger will call the pre-rollout webhooks before routing traffic to the canary.\nThe canary analysis will run for five minutes while validating the HTTP metrics and rollout hooks every half a minute.")]),t._v(" "),e("p",[t._v("After a couple of seconds Flagger will create the canary objects:")]),t._v(" "),e("div",{staticClass:"language-bash extra-class"},[e("pre",{pre:!0,attrs:{class:"language-bash"}},[e("code",[e("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# applied")]),t._v("\ndeployment.apps/podinfo\nhorizontalpodautoscaler.autoscaling/podinfo\ningresses.extensions/podinfo\ncanary.flagger.app/podinfo\n\n"),e("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# generated")]),t._v("\ndeployment.apps/podinfo-primary\nhorizontalpodautoscaler.autoscaling/podinfo-primary\nservice/podinfo\nservice/podinfo-canary\nservice/podinfo-primary\ntrafficsplits.split.smi-spec.io/podinfo\n")])])]),e("p",[t._v("After the boostrap, the podinfo deployment will be scaled to zero and the traffic to "),e("code",[t._v("podinfo.test")]),t._v("\nwill be routed to the primary pods.\nDuring the canary analysis, the "),e("code",[t._v("podinfo-canary.test")]),t._v(" address can be used to target directly the canary pods.")]),t._v(" "),e("h2",{attrs:{id:"automated-canary-promotion"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#automated-canary-promotion","aria-hidden":"true"}},[t._v("#")]),t._v(" Automated canary promotion")]),t._v(" "),e("p",[t._v("Flagger implements a control loop that gradually shifts traffic to the canary while measuring\nkey performance indicators like HTTP requests success rate, requests average duration and pod health.\nBased on analysis of the KPIs a canary is promoted or aborted, and the analysis result is published to Slack.")]),t._v(" "),e("p",[e("img",{attrs:{src:"https://raw.githubusercontent.com/weaveworks/flagger/master/docs/diagrams/flagger-canary-steps.png",alt:"Flagger Canary Stages"}})]),t._v(" "),e("p",[t._v("Trigger a canary deployment by updating the container image:")]),t._v(" "),e("div",{staticClass:"language-bash extra-class"},[e("pre",{pre:!0,attrs:{class:"language-bash"}},[e("code",[t._v("kubectl -n "),e("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v("test")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v("set")]),t._v(" image deployment/podinfo "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("\\")]),t._v("\n"),e("span",{pre:!0,attrs:{class:"token assign-left variable"}},[t._v("podinfod")]),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v("stefanprodan/podinfo:3.1.1\n")])])]),e("p",[t._v("Flagger detects that the deployment revision changed and starts a new rollout:")]),t._v(" "),e("div",{staticClass:"language-text extra-class"},[e("pre",{pre:!0,attrs:{class:"language-text"}},[e("code",[t._v("kubectl -n test describe canary/podinfo\n\nStatus:\n Canary Weight: 0\n Failed Checks: 0\n Phase: Succeeded\nEvents:\n New revision detected! Scaling up podinfo.test\n Waiting for podinfo.test rollout to finish: 0 of 1 updated replicas are available\n Pre-rollout check acceptance-test passed\n Advance podinfo.test canary weight 5\n Advance podinfo.test canary weight 10\n Advance podinfo.test canary weight 15\n Advance podinfo.test canary weight 20\n Advance podinfo.test canary weight 25\n Waiting for podinfo.test rollout to finish: 1 of 2 updated replicas are available\n Advance podinfo.test canary weight 30\n Advance podinfo.test canary weight 35\n Advance podinfo.test canary weight 40\n Advance podinfo.test canary weight 45\n Advance podinfo.test canary weight 50\n Copying podinfo.test template spec to podinfo-primary.test\n Waiting for podinfo-primary.test rollout to finish: 1 of 2 updated replicas are available\n Promotion completed! Scaling down podinfo.test\n")])])]),e("p",[e("strong",[t._v("Note")]),t._v(" that if you apply new changes to the deployment during the canary analysis, Flagger will restart the analysis.")]),t._v(" "),e("p",[t._v("A canary deployment is triggered by changes in any of the following objects:")]),t._v(" "),e("ul",[e("li",[t._v("Deployment PodSpec (container image, command, ports, env, resources, etc)")]),t._v(" "),e("li",[t._v("ConfigMaps mounted as volumes or mapped to environment variables")]),t._v(" "),e("li",[t._v("Secrets mounted as volumes or mapped to environment variables")])]),t._v(" "),e("p",[t._v("You can monitor all canaries with:")]),t._v(" "),e("div",{staticClass:"language-bash extra-class"},[e("pre",{pre:!0,attrs:{class:"language-bash"}},[e("code",[e("span",{pre:!0,attrs:{class:"token function"}},[t._v("watch")]),t._v(" kubectl get canaries --all-namespaces\n\nNAMESPACE NAME STATUS WEIGHT LASTTRANSITIONTIME\n"),e("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v("test")]),t._v(" podinfo Progressing "),e("span",{pre:!0,attrs:{class:"token number"}},[t._v("15")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token number"}},[t._v("2019")]),t._v("-06-30T14:05:07Z\nprod frontend Succeeded "),e("span",{pre:!0,attrs:{class:"token number"}},[t._v("0")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token number"}},[t._v("2019")]),t._v("-06-30T16:15:07Z\nprod backend Failed "),e("span",{pre:!0,attrs:{class:"token number"}},[t._v("0")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token number"}},[t._v("2019")]),t._v("-06-30T17:05:07Z\n")])])]),e("h2",{attrs:{id:"automated-rollback"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#automated-rollback","aria-hidden":"true"}},[t._v("#")]),t._v(" Automated rollback")]),t._v(" "),e("p",[t._v("During the canary analysis you can generate HTTP 500 errors and high latency to\ntest if Flagger pauses and rolls back the faulted version.")]),t._v(" "),e("p",[t._v("Trigger another canary deployment:")]),t._v(" "),e("div",{staticClass:"language-bash extra-class"},[e("pre",{pre:!0,attrs:{class:"language-bash"}},[e("code",[t._v("kubectl -n "),e("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v("test")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v("set")]),t._v(" image deployment/podinfo "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("\\")]),t._v("\n"),e("span",{pre:!0,attrs:{class:"token assign-left variable"}},[t._v("podinfod")]),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v("stefanprodan/podinfo:3.1.2\n")])])]),e("p",[t._v("Exec into the load tester pod with:")]),t._v(" "),e("div",{staticClass:"language-bash extra-class"},[e("pre",{pre:!0,attrs:{class:"language-bash"}},[e("code",[t._v("kubectl -n "),e("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v("test")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v("exec")]),t._v(" -it flagger-loadtester-xx-xx "),e("span",{pre:!0,attrs:{class:"token function"}},[t._v("sh")]),t._v("\n")])])]),e("p",[t._v("Generate HTTP 500 errors:")]),t._v(" "),e("div",{staticClass:"language-bash extra-class"},[e("pre",{pre:!0,attrs:{class:"language-bash"}},[e("code",[e("span",{pre:!0,attrs:{class:"token function"}},[t._v("watch")]),t._v(" -n "),e("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token function"}},[t._v("curl")]),t._v(" http://podinfo-canary.test:9898/status/500\n")])])]),e("p",[t._v("Generate latency:")]),t._v(" "),e("div",{staticClass:"language-bash extra-class"},[e("pre",{pre:!0,attrs:{class:"language-bash"}},[e("code",[e("span",{pre:!0,attrs:{class:"token function"}},[t._v("watch")]),t._v(" -n "),e("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token function"}},[t._v("curl")]),t._v(" http://podinfo-canary.test:9898/delay/1\n")])])]),e("p",[t._v("When the number of failed checks reaches the canary analysis threshold, the traffic is routed back to the primary,\nthe canary is scaled to zero and the rollout is marked as failed.")]),t._v(" "),e("div",{staticClass:"language-text extra-class"},[e("pre",{pre:!0,attrs:{class:"language-text"}},[e("code",[t._v("kubectl -n test describe canary/podinfo\n\nStatus:\n Canary Weight: 0\n Failed Checks: 10\n Phase: Failed\nEvents:\n Starting canary analysis for podinfo.test\n Pre-rollout check acceptance-test passed\n Advance podinfo.test canary weight 5\n Advance podinfo.test canary weight 10\n Advance podinfo.test canary weight 15\n Halt podinfo.test advancement success rate 69.17% < 99%\n Halt podinfo.test advancement success rate 61.39% < 99%\n Halt podinfo.test advancement success rate 55.06% < 99%\n Halt podinfo.test advancement request duration 1.20s > 0.5s\n Halt podinfo.test advancement request duration 1.45s > 0.5s\n Rolling back podinfo.test failed checks threshold reached 5\n Canary failed! Scaling down podinfo.test\n")])])]),e("h2",{attrs:{id:"custom-metrics"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#custom-metrics","aria-hidden":"true"}},[t._v("#")]),t._v(" Custom metrics")]),t._v(" "),e("p",[t._v("The canary analysis can be extended with Prometheus queries.")]),t._v(" "),e("p",[t._v("Let's a define a check for not found errors. Edit the canary analysis and add the following metric:")]),t._v(" "),e("div",{staticClass:"language-yaml extra-class"},[e("pre",{pre:!0,attrs:{class:"language-yaml"}},[e("code",[t._v(" "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("analysis")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("metrics")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("-")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("name")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token string"}},[t._v('"404s percentage"')]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("threshold")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token number"}},[t._v("3")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("query")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("|")]),e("span",{pre:!0,attrs:{class:"token scalar string"}},[t._v('\n 100 - sum(\n rate(\n response_total{\n namespace="test",\n deployment="podinfo",\n status_code!="404",\n direction="inbound"\n }[1m]\n )\n )\n /\n sum(\n rate(\n response_total{\n namespace="test",\n deployment="podinfo",\n direction="inbound"\n }[1m]\n )\n )\n * 100')]),t._v("\n")])])]),e("p",[t._v("The above configuration validates the canary version by checking if the HTTP 404 req/sec percentage\nis below three percent of the total traffic.\nIf the 404s rate reaches the 3% threshold, then the analysis is aborted and the canary is marked as failed.")]),t._v(" "),e("p",[t._v("Trigger a canary deployment by updating the container image:")]),t._v(" "),e("div",{staticClass:"language-bash extra-class"},[e("pre",{pre:!0,attrs:{class:"language-bash"}},[e("code",[t._v("kubectl -n "),e("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v("test")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v("set")]),t._v(" image deployment/podinfo "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("\\")]),t._v("\n"),e("span",{pre:!0,attrs:{class:"token assign-left variable"}},[t._v("podinfod")]),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v("stefanprodan/podinfo:3.1.3\n")])])]),e("p",[t._v("Generate 404s:")]),t._v(" "),e("div",{staticClass:"language-bash extra-class"},[e("pre",{pre:!0,attrs:{class:"language-bash"}},[e("code",[e("span",{pre:!0,attrs:{class:"token function"}},[t._v("watch")]),t._v(" -n "),e("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token function"}},[t._v("curl")]),t._v(" http://podinfo-canary:9898/status/404\n")])])]),e("p",[t._v("Watch Flagger logs:")]),t._v(" "),e("div",{staticClass:"language-text extra-class"},[e("pre",{pre:!0,attrs:{class:"language-text"}},[e("code",[t._v("kubectl -n linkerd logs deployment/flagger -f | jq .msg\n\nStarting canary deployment for podinfo.test\nPre-rollout check acceptance-test passed\nAdvance podinfo.test canary weight 5\nHalt podinfo.test advancement 404s percentage 6.20 > 3\nHalt podinfo.test advancement 404s percentage 6.45 > 3\nHalt podinfo.test advancement 404s percentage 7.22 > 3\nHalt podinfo.test advancement 404s percentage 6.50 > 3\nHalt podinfo.test advancement 404s percentage 6.34 > 3\nRolling back podinfo.test failed checks threshold reached 5\nCanary failed! Scaling down podinfo.test\n")])])]),e("p",[t._v("If you have Slack configured, Flagger will send a notification with the reason why the canary failed.")]),t._v(" "),e("h2",{attrs:{id:"linkerd-ingress"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#linkerd-ingress","aria-hidden":"true"}},[t._v("#")]),t._v(" Linkerd Ingress")]),t._v(" "),e("p",[t._v("There are two ingress controllers that are compatible with both Flagger and Linkerd: NGINX and Gloo.")]),t._v(" "),e("p",[t._v("Install NGINX:")]),t._v(" "),e("div",{staticClass:"language-bash extra-class"},[e("pre",{pre:!0,attrs:{class:"language-bash"}},[e("code",[t._v("helm upgrade -i nginx-ingress stable/nginx-ingress "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("\\")]),t._v("\n--namespace ingress-nginx\n")])])]),e("p",[t._v("Create an ingress definition for podinfo that rewrites the incoming header\nto the internal service name (required by Linkerd):")]),t._v(" "),e("div",{staticClass:"language-yaml extra-class"},[e("pre",{pre:!0,attrs:{class:"language-yaml"}},[e("code",[e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("apiVersion")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" extensions/v1beta1\n"),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("kind")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" Ingress\n"),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("metadata")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("name")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" podinfo\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("namespace")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" test\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("labels")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("app")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" podinfo\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("annotations")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("kubernetes.io/ingress.class")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token string"}},[t._v('"nginx"')]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("nginx.ingress.kubernetes.io/configuration-snippet")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("|")]),e("span",{pre:!0,attrs:{class:"token scalar string"}},[t._v("\n proxy_set_header l5d-dst-override $service_name.$namespace.svc.cluster.local:9898;\n proxy_hide_header l5d-remote-ip;\n proxy_hide_header l5d-server-id;")]),t._v("\n"),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("spec")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("rules")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("-")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("host")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" app.example.com\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("http")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("paths")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("-")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("backend")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("serviceName")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" podinfo\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("servicePort")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token number"}},[t._v("9898")]),t._v("\n")])])]),e("p",[t._v("When using an ingress controller, the Linkerd traffic split does not apply to incoming traffic\nsince NGINX in running outside of the mesh. In order to run a canary analysis for a frontend app,\nFlagger creates a shadow ingress and sets the NGINX specific annotations.")]),t._v(" "),e("h2",{attrs:{id:"a-b-testing"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#a-b-testing","aria-hidden":"true"}},[t._v("#")]),t._v(" A/B Testing")]),t._v(" "),e("p",[t._v("Besides weighted routing, Flagger can be configured to route traffic to the canary based on HTTP match conditions.\nIn an A/B testing scenario, you'll be using HTTP headers or cookies to target a certain segment of your users.\nThis is particularly useful for frontend applications that require session affinity.")]),t._v(" "),e("p",[e("img",{attrs:{src:"https://raw.githubusercontent.com/weaveworks/flagger/master/docs/diagrams/flagger-nginx-linkerd.png",alt:"Flagger Linkerd Ingress"}})]),t._v(" "),e("p",[t._v("Edit podinfo canary analysis, set the provider to "),e("code",[t._v("nginx")]),t._v(", add the ingress reference,\nremove the max/step weight and add the match conditions and iterations:")]),t._v(" "),e("div",{staticClass:"language-yaml extra-class"},[e("pre",{pre:!0,attrs:{class:"language-yaml"}},[e("code",[e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("apiVersion")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" flagger.app/v1beta1\n"),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("kind")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" Canary\n"),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("metadata")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("name")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" podinfo\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("namespace")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" test\n"),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("spec")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# ingress reference")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("provider")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" nginx\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("ingressRef")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("apiVersion")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" extensions/v1beta1\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("kind")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" Ingress\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("name")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" podinfo\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("targetRef")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("apiVersion")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" apps/v1\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("kind")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" Deployment\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("name")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" podinfo\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("autoscalerRef")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("apiVersion")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" autoscaling/v2beta1\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("kind")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" HorizontalPodAutoscaler\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("name")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" podinfo\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("service")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# container port")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("port")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token number"}},[t._v("9898")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("analysis")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("interval")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" 1m\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("threshold")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token number"}},[t._v("10")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("iterations")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token number"}},[t._v("10")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("match")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# curl -H 'X-Canary: always' http://app.example.com")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("-")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("headers")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("x-canary")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("exact")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token string"}},[t._v('"always"')]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# curl -b 'canary=always' http://app.example.com")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("-")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("headers")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("cookie")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("exact")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token string"}},[t._v('"canary"')]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# Linkerd Prometheus checks")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("metrics")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("-")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("name")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" request"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("-")]),t._v("success"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("-")]),t._v("rate\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("thresholdRange")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("min")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token number"}},[t._v("99")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("interval")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" 1m\n "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("-")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("name")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" request"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("-")]),t._v("duration\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("thresholdRange")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("max")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token number"}},[t._v("500")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("interval")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" 30s\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("webhooks")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("-")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("name")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" acceptance"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("-")]),t._v("test\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("type")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" pre"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("-")]),t._v("rollout\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("url")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" http"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("//flagger"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("-")]),t._v("loadtester.test/\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("timeout")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" 30s\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("metadata")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("type")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" bash\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("cmd")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token string"}},[t._v("\"curl -sd 'test' http://podinfo-canary:9898/token | grep token\"")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("-")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("name")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" load"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("-")]),t._v("test\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("type")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" rollout\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("url")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" http"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("//flagger"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("-")]),t._v("loadtester.test/\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("metadata")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("cmd")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token string"}},[t._v("\"hey -z 2m -q 10 -c 2 -H 'Cookie: canary=always' http://app.example.com\"")]),t._v("\n")])])]),e("p",[t._v("The above configuration will run an analysis for ten minutes targeting users that have\na "),e("code",[t._v("canary")]),t._v(" cookie set to "),e("code",[t._v("always")]),t._v(" or those that call the service using the "),e("code",[t._v("X-Canary: always")]),t._v(" header.")]),t._v(" "),e("p",[e("strong",[t._v("Note")]),t._v(" that the load test now targets the external address and uses the canary cookie.")]),t._v(" "),e("p",[t._v("Trigger a canary deployment by updating the container image:")]),t._v(" "),e("div",{staticClass:"language-bash extra-class"},[e("pre",{pre:!0,attrs:{class:"language-bash"}},[e("code",[t._v("kubectl -n "),e("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v("test")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v("set")]),t._v(" image deployment/podinfo "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("\\")]),t._v("\n"),e("span",{pre:!0,attrs:{class:"token assign-left variable"}},[t._v("podinfod")]),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v("stefanprodan/podinfo:3.1.4\n")])])]),e("p",[t._v("Flagger detects that the deployment revision changed and starts the A/B testing:")]),t._v(" "),e("div",{staticClass:"language-text extra-class"},[e("pre",{pre:!0,attrs:{class:"language-text"}},[e("code",[t._v("kubectl -n test describe canary/podinfo\n\nEvents:\n Starting canary deployment for podinfo.test\n Pre-rollout check acceptance-test passed\n Advance podinfo.test canary iteration 1/10\n Advance podinfo.test canary iteration 2/10\n Advance podinfo.test canary iteration 3/10\n Advance podinfo.test canary iteration 4/10\n Advance podinfo.test canary iteration 5/10\n Advance podinfo.test canary iteration 6/10\n Advance podinfo.test canary iteration 7/10\n Advance podinfo.test canary iteration 8/10\n Advance podinfo.test canary iteration 9/10\n Advance podinfo.test canary iteration 10/10\n Copying podinfo.test template spec to podinfo-primary.test\n Waiting for podinfo-primary.test rollout to finish: 1 of 2 updated replicas are available\n Promotion completed! Scaling down podinfo.test\n")])])]),e("p",[t._v("The above procedure can be extended with "),e("router-link",{attrs:{to:"/usage/metrics.html"}},[t._v("custom metrics")]),t._v(" checks,\n"),e("router-link",{attrs:{to:"/usage/webhooks.html"}},[t._v("webhooks")]),t._v(",\n"),e("router-link",{attrs:{to:"/usage/webhooks.html#manual-gating"}},[t._v("manual promotion")]),t._v(" approval and\n"),e("router-link",{attrs:{to:"/usage/alerting.html"}},[t._v("Slack or MS Teams")]),t._v(" notifications.")],1)])},[],!1,null,null,null);a.default=n.exports}}]); \ No newline at end of file +(window.webpackJsonp=window.webpackJsonp||[]).push([[26],{285:function(t,a,e){"use strict";e.r(a);var s=e(37),n=Object(s.a)({},function(){var t=this,a=t.$createElement,e=t._self._c||a;return e("ContentSlotsDistributor",{attrs:{"slot-key":t.$parent.slotKey}},[e("h1",{attrs:{id:"linkerd-canary-deployments"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#linkerd-canary-deployments","aria-hidden":"true"}},[t._v("#")]),t._v(" Linkerd Canary Deployments")]),t._v(" "),e("p",[t._v("This guide shows you how to use Linkerd and Flagger to automate canary deployments.")]),t._v(" "),e("p",[e("img",{attrs:{src:"https://raw.githubusercontent.com/weaveworks/flagger/master/docs/diagrams/flagger-linkerd-traffic-split.png",alt:"Flagger Linkerd Traffic Split"}})]),t._v(" "),e("h2",{attrs:{id:"prerequisites"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#prerequisites","aria-hidden":"true"}},[t._v("#")]),t._v(" Prerequisites")]),t._v(" "),e("p",[t._v("Flagger requires a Kubernetes cluster "),e("strong",[t._v("v1.11")]),t._v(" or newer and Linkerd "),e("strong",[t._v("2.4")]),t._v(" or newer.")]),t._v(" "),e("p",[t._v("Install Flagger in the linkerd namespace:")]),t._v(" "),e("div",{staticClass:"language-bash extra-class"},[e("pre",{pre:!0,attrs:{class:"language-bash"}},[e("code",[t._v("kubectl apply -k github.com/weaveworks/flagger//kustomize/linkerd\n")])])]),e("p",[t._v("Note that you'll need kubectl 1.14 or newer to run the above command.")]),t._v(" "),e("h2",{attrs:{id:"bootstrap"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#bootstrap","aria-hidden":"true"}},[t._v("#")]),t._v(" Bootstrap")]),t._v(" "),e("p",[t._v("Flagger takes a Kubernetes deployment and optionally a horizontal pod autoscaler (HPA),\nthen creates a series of objects (Kubernetes deployments, ClusterIP services and SMI traffic split).\nThese objects expose the application inside the mesh and drive the canary analysis and promotion.")]),t._v(" "),e("p",[t._v("Create a test namespace and enable Linkerd proxy injection:")]),t._v(" "),e("div",{staticClass:"language-bash extra-class"},[e("pre",{pre:!0,attrs:{class:"language-bash"}},[e("code",[t._v("kubectl create ns "),e("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v("test")]),t._v("\nkubectl annotate namespace "),e("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v("test")]),t._v(" linkerd.io/inject"),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v("enabled\n")])])]),e("p",[t._v("Install the load testing service to generate traffic during the canary analysis:")]),t._v(" "),e("div",{staticClass:"language-bash extra-class"},[e("pre",{pre:!0,attrs:{class:"language-bash"}},[e("code",[t._v("kubectl apply -k github.com/weaveworks/flagger//kustomize/tester\n")])])]),e("p",[t._v("Create a deployment and a horizontal pod autoscaler:")]),t._v(" "),e("div",{staticClass:"language-bash extra-class"},[e("pre",{pre:!0,attrs:{class:"language-bash"}},[e("code",[t._v("kubectl apply -k github.com/weaveworks/flagger//kustomize/podinfo\n")])])]),e("p",[t._v("Create a canary custom resource for the podinfo deployment:")]),t._v(" "),e("div",{staticClass:"language-yaml extra-class"},[e("pre",{pre:!0,attrs:{class:"language-yaml"}},[e("code",[e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("apiVersion")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" flagger.app/v1beta1\n"),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("kind")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" Canary\n"),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("metadata")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("name")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" podinfo\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("namespace")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" test\n"),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("spec")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# deployment reference")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("targetRef")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("apiVersion")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" apps/v1\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("kind")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" Deployment\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("name")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" podinfo\n "),e("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# HPA reference (optional)")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("autoscalerRef")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("apiVersion")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" autoscaling/v2beta1\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("kind")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" HorizontalPodAutoscaler\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("name")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" podinfo\n "),e("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# the maximum time in seconds for the canary deployment")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# to make progress before it is rollback (default 600s)")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("progressDeadlineSeconds")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token number"}},[t._v("60")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("service")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# ClusterIP port number")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("port")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token number"}},[t._v("9898")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# container port number or name (optional)")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("targetPort")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token number"}},[t._v("9898")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("analysis")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# schedule interval (default 60s)")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("interval")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" 30s\n "),e("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# max number of failed metric checks before rollback")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("threshold")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token number"}},[t._v("5")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# max traffic percentage routed to canary")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# percentage (0-100)")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("maxWeight")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token number"}},[t._v("50")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# canary increment step")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# percentage (0-100)")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("stepWeight")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token number"}},[t._v("5")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# Linkerd Prometheus checks")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("metrics")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("-")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("name")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" request"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("-")]),t._v("success"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("-")]),t._v("rate\n "),e("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# minimum req success rate (non 5xx responses)")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# percentage (0-100)")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("thresholdRange")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("min")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token number"}},[t._v("99")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("interval")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" 1m\n "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("-")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("name")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" request"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("-")]),t._v("duration\n "),e("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# maximum req duration P99")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# milliseconds")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("thresholdRange")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("max")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token number"}},[t._v("500")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("interval")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" 30s\n "),e("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# testing (optional)")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("webhooks")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("-")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("name")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" acceptance"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("-")]),t._v("test\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("type")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" pre"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("-")]),t._v("rollout\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("url")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" http"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("//flagger"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("-")]),t._v("loadtester.test/\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("timeout")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" 30s\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("metadata")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("type")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" bash\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("cmd")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token string"}},[t._v("\"curl -sd 'test' http://podinfo-canary.test:9898/token | grep token\"")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("-")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("name")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" load"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("-")]),t._v("test\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("type")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" rollout\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("url")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" http"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("//flagger"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("-")]),t._v("loadtester.test/\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("metadata")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("cmd")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token string"}},[t._v('"hey -z 2m -q 10 -c 2 http://podinfo-canary.test:9898/"')]),t._v("\n")])])]),e("p",[t._v("Save the above resource as podinfo-canary.yaml and then apply it:")]),t._v(" "),e("div",{staticClass:"language-bash extra-class"},[e("pre",{pre:!0,attrs:{class:"language-bash"}},[e("code",[t._v("kubectl apply -f ./podinfo-canary.yaml\n")])])]),e("p",[t._v("When the canary analysis starts, Flagger will call the pre-rollout webhooks before routing traffic to the canary.\nThe canary analysis will run for five minutes while validating the HTTP metrics and rollout hooks every half a minute.")]),t._v(" "),e("p",[t._v("After a couple of seconds Flagger will create the canary objects:")]),t._v(" "),e("div",{staticClass:"language-bash extra-class"},[e("pre",{pre:!0,attrs:{class:"language-bash"}},[e("code",[e("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# applied")]),t._v("\ndeployment.apps/podinfo\nhorizontalpodautoscaler.autoscaling/podinfo\ningresses.extensions/podinfo\ncanary.flagger.app/podinfo\n\n"),e("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# generated")]),t._v("\ndeployment.apps/podinfo-primary\nhorizontalpodautoscaler.autoscaling/podinfo-primary\nservice/podinfo\nservice/podinfo-canary\nservice/podinfo-primary\ntrafficsplits.split.smi-spec.io/podinfo\n")])])]),e("p",[t._v("After the boostrap, the podinfo deployment will be scaled to zero and the traffic to "),e("code",[t._v("podinfo.test")]),t._v("\nwill be routed to the primary pods.\nDuring the canary analysis, the "),e("code",[t._v("podinfo-canary.test")]),t._v(" address can be used to target directly the canary pods.")]),t._v(" "),e("h2",{attrs:{id:"automated-canary-promotion"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#automated-canary-promotion","aria-hidden":"true"}},[t._v("#")]),t._v(" Automated canary promotion")]),t._v(" "),e("p",[t._v("Flagger implements a control loop that gradually shifts traffic to the canary while measuring\nkey performance indicators like HTTP requests success rate, requests average duration and pod health.\nBased on analysis of the KPIs a canary is promoted or aborted, and the analysis result is published to Slack.")]),t._v(" "),e("p",[e("img",{attrs:{src:"https://raw.githubusercontent.com/weaveworks/flagger/master/docs/diagrams/flagger-canary-steps.png",alt:"Flagger Canary Stages"}})]),t._v(" "),e("p",[t._v("Trigger a canary deployment by updating the container image:")]),t._v(" "),e("div",{staticClass:"language-bash extra-class"},[e("pre",{pre:!0,attrs:{class:"language-bash"}},[e("code",[t._v("kubectl -n "),e("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v("test")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v("set")]),t._v(" image deployment/podinfo "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("\\")]),t._v("\n"),e("span",{pre:!0,attrs:{class:"token assign-left variable"}},[t._v("podinfod")]),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v("stefanprodan/podinfo:3.1.1\n")])])]),e("p",[t._v("Flagger detects that the deployment revision changed and starts a new rollout:")]),t._v(" "),e("div",{staticClass:"language-text extra-class"},[e("pre",{pre:!0,attrs:{class:"language-text"}},[e("code",[t._v("kubectl -n test describe canary/podinfo\n\nStatus:\n Canary Weight: 0\n Failed Checks: 0\n Phase: Succeeded\nEvents:\n New revision detected! Scaling up podinfo.test\n Waiting for podinfo.test rollout to finish: 0 of 1 updated replicas are available\n Pre-rollout check acceptance-test passed\n Advance podinfo.test canary weight 5\n Advance podinfo.test canary weight 10\n Advance podinfo.test canary weight 15\n Advance podinfo.test canary weight 20\n Advance podinfo.test canary weight 25\n Waiting for podinfo.test rollout to finish: 1 of 2 updated replicas are available\n Advance podinfo.test canary weight 30\n Advance podinfo.test canary weight 35\n Advance podinfo.test canary weight 40\n Advance podinfo.test canary weight 45\n Advance podinfo.test canary weight 50\n Copying podinfo.test template spec to podinfo-primary.test\n Waiting for podinfo-primary.test rollout to finish: 1 of 2 updated replicas are available\n Promotion completed! Scaling down podinfo.test\n")])])]),e("p",[e("strong",[t._v("Note")]),t._v(" that if you apply new changes to the deployment during the canary analysis, Flagger will restart the analysis.")]),t._v(" "),e("p",[t._v("A canary deployment is triggered by changes in any of the following objects:")]),t._v(" "),e("ul",[e("li",[t._v("Deployment PodSpec (container image, command, ports, env, resources, etc)")]),t._v(" "),e("li",[t._v("ConfigMaps mounted as volumes or mapped to environment variables")]),t._v(" "),e("li",[t._v("Secrets mounted as volumes or mapped to environment variables")])]),t._v(" "),e("p",[t._v("You can monitor all canaries with:")]),t._v(" "),e("div",{staticClass:"language-bash extra-class"},[e("pre",{pre:!0,attrs:{class:"language-bash"}},[e("code",[e("span",{pre:!0,attrs:{class:"token function"}},[t._v("watch")]),t._v(" kubectl get canaries --all-namespaces\n\nNAMESPACE NAME STATUS WEIGHT LASTTRANSITIONTIME\n"),e("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v("test")]),t._v(" podinfo Progressing "),e("span",{pre:!0,attrs:{class:"token number"}},[t._v("15")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token number"}},[t._v("2019")]),t._v("-06-30T14:05:07Z\nprod frontend Succeeded "),e("span",{pre:!0,attrs:{class:"token number"}},[t._v("0")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token number"}},[t._v("2019")]),t._v("-06-30T16:15:07Z\nprod backend Failed "),e("span",{pre:!0,attrs:{class:"token number"}},[t._v("0")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token number"}},[t._v("2019")]),t._v("-06-30T17:05:07Z\n")])])]),e("h2",{attrs:{id:"automated-rollback"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#automated-rollback","aria-hidden":"true"}},[t._v("#")]),t._v(" Automated rollback")]),t._v(" "),e("p",[t._v("During the canary analysis you can generate HTTP 500 errors and high latency to\ntest if Flagger pauses and rolls back the faulted version.")]),t._v(" "),e("p",[t._v("Trigger another canary deployment:")]),t._v(" "),e("div",{staticClass:"language-bash extra-class"},[e("pre",{pre:!0,attrs:{class:"language-bash"}},[e("code",[t._v("kubectl -n "),e("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v("test")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v("set")]),t._v(" image deployment/podinfo "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("\\")]),t._v("\n"),e("span",{pre:!0,attrs:{class:"token assign-left variable"}},[t._v("podinfod")]),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v("stefanprodan/podinfo:3.1.2\n")])])]),e("p",[t._v("Exec into the load tester pod with:")]),t._v(" "),e("div",{staticClass:"language-bash extra-class"},[e("pre",{pre:!0,attrs:{class:"language-bash"}},[e("code",[t._v("kubectl -n "),e("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v("test")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v("exec")]),t._v(" -it flagger-loadtester-xx-xx "),e("span",{pre:!0,attrs:{class:"token function"}},[t._v("sh")]),t._v("\n")])])]),e("p",[t._v("Generate HTTP 500 errors:")]),t._v(" "),e("div",{staticClass:"language-bash extra-class"},[e("pre",{pre:!0,attrs:{class:"language-bash"}},[e("code",[e("span",{pre:!0,attrs:{class:"token function"}},[t._v("watch")]),t._v(" -n "),e("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token function"}},[t._v("curl")]),t._v(" http://podinfo-canary.test:9898/status/500\n")])])]),e("p",[t._v("Generate latency:")]),t._v(" "),e("div",{staticClass:"language-bash extra-class"},[e("pre",{pre:!0,attrs:{class:"language-bash"}},[e("code",[e("span",{pre:!0,attrs:{class:"token function"}},[t._v("watch")]),t._v(" -n "),e("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token function"}},[t._v("curl")]),t._v(" http://podinfo-canary.test:9898/delay/1\n")])])]),e("p",[t._v("When the number of failed checks reaches the canary analysis threshold, the traffic is routed back to the primary,\nthe canary is scaled to zero and the rollout is marked as failed.")]),t._v(" "),e("div",{staticClass:"language-text extra-class"},[e("pre",{pre:!0,attrs:{class:"language-text"}},[e("code",[t._v("kubectl -n test describe canary/podinfo\n\nStatus:\n Canary Weight: 0\n Failed Checks: 10\n Phase: Failed\nEvents:\n Starting canary analysis for podinfo.test\n Pre-rollout check acceptance-test passed\n Advance podinfo.test canary weight 5\n Advance podinfo.test canary weight 10\n Advance podinfo.test canary weight 15\n Halt podinfo.test advancement success rate 69.17% < 99%\n Halt podinfo.test advancement success rate 61.39% < 99%\n Halt podinfo.test advancement success rate 55.06% < 99%\n Halt podinfo.test advancement request duration 1.20s > 0.5s\n Halt podinfo.test advancement request duration 1.45s > 0.5s\n Rolling back podinfo.test failed checks threshold reached 5\n Canary failed! Scaling down podinfo.test\n")])])]),e("h2",{attrs:{id:"custom-metrics"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#custom-metrics","aria-hidden":"true"}},[t._v("#")]),t._v(" Custom metrics")]),t._v(" "),e("p",[t._v("The canary analysis can be extended with Prometheus queries.")]),t._v(" "),e("p",[t._v("Let's a define a check for not found errors. Edit the canary analysis and add the following metric:")]),t._v(" "),e("div",{staticClass:"language-yaml extra-class"},[e("pre",{pre:!0,attrs:{class:"language-yaml"}},[e("code",[t._v(" "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("analysis")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("metrics")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("-")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("name")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token string"}},[t._v('"404s percentage"')]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("threshold")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token number"}},[t._v("3")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("query")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("|")]),e("span",{pre:!0,attrs:{class:"token scalar string"}},[t._v('\n 100 - sum(\n rate(\n response_total{\n namespace="test",\n deployment="podinfo",\n status_code!="404",\n direction="inbound"\n }[1m]\n )\n )\n /\n sum(\n rate(\n response_total{\n namespace="test",\n deployment="podinfo",\n direction="inbound"\n }[1m]\n )\n )\n * 100')]),t._v("\n")])])]),e("p",[t._v("The above configuration validates the canary version by checking if the HTTP 404 req/sec percentage\nis below three percent of the total traffic.\nIf the 404s rate reaches the 3% threshold, then the analysis is aborted and the canary is marked as failed.")]),t._v(" "),e("p",[t._v("Trigger a canary deployment by updating the container image:")]),t._v(" "),e("div",{staticClass:"language-bash extra-class"},[e("pre",{pre:!0,attrs:{class:"language-bash"}},[e("code",[t._v("kubectl -n "),e("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v("test")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v("set")]),t._v(" image deployment/podinfo "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("\\")]),t._v("\n"),e("span",{pre:!0,attrs:{class:"token assign-left variable"}},[t._v("podinfod")]),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v("stefanprodan/podinfo:3.1.3\n")])])]),e("p",[t._v("Generate 404s:")]),t._v(" "),e("div",{staticClass:"language-bash extra-class"},[e("pre",{pre:!0,attrs:{class:"language-bash"}},[e("code",[e("span",{pre:!0,attrs:{class:"token function"}},[t._v("watch")]),t._v(" -n "),e("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token function"}},[t._v("curl")]),t._v(" http://podinfo-canary:9898/status/404\n")])])]),e("p",[t._v("Watch Flagger logs:")]),t._v(" "),e("div",{staticClass:"language-text extra-class"},[e("pre",{pre:!0,attrs:{class:"language-text"}},[e("code",[t._v("kubectl -n linkerd logs deployment/flagger -f | jq .msg\n\nStarting canary deployment for podinfo.test\nPre-rollout check acceptance-test passed\nAdvance podinfo.test canary weight 5\nHalt podinfo.test advancement 404s percentage 6.20 > 3\nHalt podinfo.test advancement 404s percentage 6.45 > 3\nHalt podinfo.test advancement 404s percentage 7.22 > 3\nHalt podinfo.test advancement 404s percentage 6.50 > 3\nHalt podinfo.test advancement 404s percentage 6.34 > 3\nRolling back podinfo.test failed checks threshold reached 5\nCanary failed! Scaling down podinfo.test\n")])])]),e("p",[t._v("If you have Slack configured, Flagger will send a notification with the reason why the canary failed.")]),t._v(" "),e("h2",{attrs:{id:"linkerd-ingress"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#linkerd-ingress","aria-hidden":"true"}},[t._v("#")]),t._v(" Linkerd Ingress")]),t._v(" "),e("p",[t._v("There are two ingress controllers that are compatible with both Flagger and Linkerd: NGINX and Gloo.")]),t._v(" "),e("p",[t._v("Install NGINX:")]),t._v(" "),e("div",{staticClass:"language-bash extra-class"},[e("pre",{pre:!0,attrs:{class:"language-bash"}},[e("code",[t._v("helm upgrade -i nginx-ingress stable/nginx-ingress "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("\\")]),t._v("\n--namespace ingress-nginx\n")])])]),e("p",[t._v("Create an ingress definition for podinfo that rewrites the incoming header\nto the internal service name (required by Linkerd):")]),t._v(" "),e("div",{staticClass:"language-yaml extra-class"},[e("pre",{pre:!0,attrs:{class:"language-yaml"}},[e("code",[e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("apiVersion")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" extensions/v1beta1\n"),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("kind")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" Ingress\n"),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("metadata")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("name")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" podinfo\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("namespace")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" test\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("labels")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("app")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" podinfo\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("annotations")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("kubernetes.io/ingress.class")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token string"}},[t._v('"nginx"')]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("nginx.ingress.kubernetes.io/configuration-snippet")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("|")]),e("span",{pre:!0,attrs:{class:"token scalar string"}},[t._v("\n proxy_set_header l5d-dst-override $service_name.$namespace.svc.cluster.local:9898;\n proxy_hide_header l5d-remote-ip;\n proxy_hide_header l5d-server-id;")]),t._v("\n"),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("spec")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("rules")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("-")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("host")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" app.example.com\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("http")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("paths")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("-")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("backend")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("serviceName")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" podinfo\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("servicePort")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token number"}},[t._v("9898")]),t._v("\n")])])]),e("p",[t._v("When using an ingress controller, the Linkerd traffic split does not apply to incoming traffic\nsince NGINX in running outside of the mesh. In order to run a canary analysis for a frontend app,\nFlagger creates a shadow ingress and sets the NGINX specific annotations.")]),t._v(" "),e("h2",{attrs:{id:"a-b-testing"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#a-b-testing","aria-hidden":"true"}},[t._v("#")]),t._v(" A/B Testing")]),t._v(" "),e("p",[t._v("Besides weighted routing, Flagger can be configured to route traffic to the canary based on HTTP match conditions.\nIn an A/B testing scenario, you'll be using HTTP headers or cookies to target a certain segment of your users.\nThis is particularly useful for frontend applications that require session affinity.")]),t._v(" "),e("p",[e("img",{attrs:{src:"https://raw.githubusercontent.com/weaveworks/flagger/master/docs/diagrams/flagger-nginx-linkerd.png",alt:"Flagger Linkerd Ingress"}})]),t._v(" "),e("p",[t._v("Edit podinfo canary analysis, set the provider to "),e("code",[t._v("nginx")]),t._v(", add the ingress reference,\nremove the max/step weight and add the match conditions and iterations:")]),t._v(" "),e("div",{staticClass:"language-yaml extra-class"},[e("pre",{pre:!0,attrs:{class:"language-yaml"}},[e("code",[e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("apiVersion")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" flagger.app/v1beta1\n"),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("kind")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" Canary\n"),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("metadata")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("name")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" podinfo\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("namespace")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" test\n"),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("spec")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# ingress reference")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("provider")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" nginx\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("ingressRef")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("apiVersion")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" extensions/v1beta1\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("kind")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" Ingress\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("name")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" podinfo\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("targetRef")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("apiVersion")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" apps/v1\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("kind")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" Deployment\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("name")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" podinfo\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("autoscalerRef")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("apiVersion")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" autoscaling/v2beta1\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("kind")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" HorizontalPodAutoscaler\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("name")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" podinfo\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("service")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# container port")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("port")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token number"}},[t._v("9898")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("analysis")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("interval")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" 1m\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("threshold")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token number"}},[t._v("10")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("iterations")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token number"}},[t._v("10")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("match")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# curl -H 'X-Canary: always' http://app.example.com")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("-")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("headers")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("x-canary")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("exact")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token string"}},[t._v('"always"')]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# curl -b 'canary=always' http://app.example.com")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("-")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("headers")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("cookie")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("exact")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token string"}},[t._v('"canary"')]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# Linkerd Prometheus checks")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("metrics")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("-")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("name")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" request"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("-")]),t._v("success"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("-")]),t._v("rate\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("thresholdRange")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("min")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token number"}},[t._v("99")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("interval")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" 1m\n "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("-")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("name")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" request"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("-")]),t._v("duration\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("thresholdRange")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("max")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token number"}},[t._v("500")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("interval")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" 30s\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("webhooks")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("-")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("name")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" acceptance"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("-")]),t._v("test\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("type")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" pre"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("-")]),t._v("rollout\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("url")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" http"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("//flagger"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("-")]),t._v("loadtester.test/\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("timeout")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" 30s\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("metadata")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("type")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" bash\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("cmd")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token string"}},[t._v("\"curl -sd 'test' http://podinfo-canary:9898/token | grep token\"")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("-")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("name")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" load"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("-")]),t._v("test\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("type")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" rollout\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("url")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" http"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("//flagger"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("-")]),t._v("loadtester.test/\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("metadata")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("cmd")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token string"}},[t._v("\"hey -z 2m -q 10 -c 2 -H 'Cookie: canary=always' http://app.example.com\"")]),t._v("\n")])])]),e("p",[t._v("The above configuration will run an analysis for ten minutes targeting users that have\na "),e("code",[t._v("canary")]),t._v(" cookie set to "),e("code",[t._v("always")]),t._v(" or those that call the service using the "),e("code",[t._v("X-Canary: always")]),t._v(" header.")]),t._v(" "),e("p",[e("strong",[t._v("Note")]),t._v(" that the load test now targets the external address and uses the canary cookie.")]),t._v(" "),e("p",[t._v("Trigger a canary deployment by updating the container image:")]),t._v(" "),e("div",{staticClass:"language-bash extra-class"},[e("pre",{pre:!0,attrs:{class:"language-bash"}},[e("code",[t._v("kubectl -n "),e("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v("test")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v("set")]),t._v(" image deployment/podinfo "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("\\")]),t._v("\n"),e("span",{pre:!0,attrs:{class:"token assign-left variable"}},[t._v("podinfod")]),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v("stefanprodan/podinfo:3.1.4\n")])])]),e("p",[t._v("Flagger detects that the deployment revision changed and starts the A/B testing:")]),t._v(" "),e("div",{staticClass:"language-text extra-class"},[e("pre",{pre:!0,attrs:{class:"language-text"}},[e("code",[t._v("kubectl -n test describe canary/podinfo\n\nEvents:\n Starting canary deployment for podinfo.test\n Pre-rollout check acceptance-test passed\n Advance podinfo.test canary iteration 1/10\n Advance podinfo.test canary iteration 2/10\n Advance podinfo.test canary iteration 3/10\n Advance podinfo.test canary iteration 4/10\n Advance podinfo.test canary iteration 5/10\n Advance podinfo.test canary iteration 6/10\n Advance podinfo.test canary iteration 7/10\n Advance podinfo.test canary iteration 8/10\n Advance podinfo.test canary iteration 9/10\n Advance podinfo.test canary iteration 10/10\n Copying podinfo.test template spec to podinfo-primary.test\n Waiting for podinfo-primary.test rollout to finish: 1 of 2 updated replicas are available\n Promotion completed! Scaling down podinfo.test\n")])])]),e("p",[t._v("The above procedure can be extended with "),e("router-link",{attrs:{to:"/usage/metrics.html"}},[t._v("custom metrics")]),t._v(" checks,\n"),e("router-link",{attrs:{to:"/usage/webhooks.html"}},[t._v("webhooks")]),t._v(",\n"),e("router-link",{attrs:{to:"/usage/webhooks.html#manual-gating"}},[t._v("manual promotion")]),t._v(" approval and\n"),e("router-link",{attrs:{to:"/usage/alerting.html"}},[t._v("Slack or MS Teams")]),t._v(" notifications.")],1)])},[],!1,null,null,null);a.default=n.exports}}]); \ No newline at end of file diff --git a/assets/js/29.ce4011f4.js b/assets/js/29.17c547e6.js similarity index 79% rename from assets/js/29.ce4011f4.js rename to assets/js/29.17c547e6.js index 7ef8af3f..4755337d 100644 --- a/assets/js/29.ce4011f4.js +++ b/assets/js/29.17c547e6.js @@ -1 +1 @@ -(window.webpackJsonp=window.webpackJsonp||[]).push([[29],{290:function(t,e,n){"use strict";n.r(e);var s=n(37),l=Object(s.a)({},function(){var t=this.$createElement;return(this._self._c||t)("ContentSlotsDistributor",{attrs:{"slot-key":this.$parent.slotKey}})},[],!1,null,null,null);e.default=l.exports}}]); \ No newline at end of file +(window.webpackJsonp=window.webpackJsonp||[]).push([[29],{289:function(t,e,n){"use strict";n.r(e);var s=n(37),l=Object(s.a)({},function(){var t=this.$createElement;return(this._self._c||t)("ContentSlotsDistributor",{attrs:{"slot-key":this.$parent.slotKey}})},[],!1,null,null,null);e.default=l.exports}}]); \ No newline at end of file diff --git a/assets/js/30.edda19d9.js b/assets/js/30.38bd809e.js similarity index 99% rename from assets/js/30.edda19d9.js rename to assets/js/30.38bd809e.js index ec1e64a3..321599ef 100644 --- a/assets/js/30.edda19d9.js +++ b/assets/js/30.38bd809e.js @@ -1 +1 @@ -(window.webpackJsonp=window.webpackJsonp||[]).push([[30],{289:function(a,t,e){"use strict";e.r(t);var s=e(37),n=Object(s.a)({},function(){var a=this,t=a.$createElement,e=a._self._c||t;return e("ContentSlotsDistributor",{attrs:{"slot-key":a.$parent.slotKey}},[e("h1",{attrs:{id:"alerting"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#alerting","aria-hidden":"true"}},[a._v("#")]),a._v(" Alerting")]),a._v(" "),e("p",[a._v("Flagger can be configured to send alerts to various chat platforms. You can define a global alert provider at\ninstall time or configure alerts on a per canary basis.")]),a._v(" "),e("h2",{attrs:{id:"global-configuration"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#global-configuration","aria-hidden":"true"}},[a._v("#")]),a._v(" Global configuration")]),a._v(" "),e("p",[a._v("Flagger can be configured to send Slack notifications:")]),a._v(" "),e("div",{staticClass:"language-bash extra-class"},[e("pre",{pre:!0,attrs:{class:"language-bash"}},[e("code",[a._v("helm upgrade -i flagger flagger/flagger "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[a._v("\\")]),a._v("\n--set slack.url"),e("span",{pre:!0,attrs:{class:"token operator"}},[a._v("=")]),a._v("https://hooks.slack.com/services/YOUR/SLACK/WEBHOOK "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[a._v("\\")]),a._v("\n--set slack.channel"),e("span",{pre:!0,attrs:{class:"token operator"}},[a._v("=")]),a._v("general "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[a._v("\\")]),a._v("\n--set slack.user"),e("span",{pre:!0,attrs:{class:"token operator"}},[a._v("=")]),a._v("flagger\n")])])]),e("p",[a._v("Once configured with a Slack incoming "),e("strong",[a._v("webhook")]),a._v(", Flagger will post messages when a canary deployment\nhas been initialised, when a new revision has been detected and if the canary analysis failed or succeeded.")]),a._v(" "),e("p",[e("img",{attrs:{src:"https://raw.githubusercontent.com/weaveworks/flagger/master/docs/screens/slack-canary-notifications.png",alt:"Slack Notifications"}})]),a._v(" "),e("p",[a._v("A canary deployment will be rolled back if the progress deadline exceeded or if the analysis reached the\nmaximum number of failed checks:")]),a._v(" "),e("p",[e("img",{attrs:{src:"https://raw.githubusercontent.com/weaveworks/flagger/master/docs/screens/slack-canary-failed.png",alt:"Slack Notifications"}})]),a._v(" "),e("p",[a._v("Flagger can be configured to send notifications to Microsoft Teams:")]),a._v(" "),e("div",{staticClass:"language-bash extra-class"},[e("pre",{pre:!0,attrs:{class:"language-bash"}},[e("code",[a._v("helm upgrade -i flagger flagger/flagger "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[a._v("\\")]),a._v("\n--set msteams.url"),e("span",{pre:!0,attrs:{class:"token operator"}},[a._v("=")]),a._v("https://outlook.office.com/webhook/YOUR/TEAMS/WEBHOOK\n")])])]),e("p",[a._v("Similar to Slack, Flagger alerts on canary analysis events:")]),a._v(" "),e("p",[e("img",{attrs:{src:"https://raw.githubusercontent.com/weaveworks/flagger/master/docs/screens/flagger-ms-teams-notifications.png",alt:"MS Teams Notifications"}})]),a._v(" "),e("p",[e("img",{attrs:{src:"https://raw.githubusercontent.com/weaveworks/flagger/master/docs/screens/flagger-ms-teams-failed.png",alt:"MS Teams Notifications"}})]),a._v(" "),e("h2",{attrs:{id:"canary-configuration"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#canary-configuration","aria-hidden":"true"}},[a._v("#")]),a._v(" Canary configuration")]),a._v(" "),e("p",[a._v("Configuring alerting globally has several limitations as it's not possible to specify different channels\nor configure the verbosity on a per canary basis.\nTo make the alerting move flexible, the canary analysis can be extended\nwith a list of alerts that reference an alert provider.\nFor each alert, users can configure the severity level.\nThe alerts section overrides the global setting.")]),a._v(" "),e("p",[a._v("Slack example:")]),a._v(" "),e("div",{staticClass:"language-yaml extra-class"},[e("pre",{pre:!0,attrs:{class:"language-yaml"}},[e("code",[e("span",{pre:!0,attrs:{class:"token key atrule"}},[a._v("apiVersion")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[a._v(":")]),a._v(" flagger.app/v1beta1\n"),e("span",{pre:!0,attrs:{class:"token key atrule"}},[a._v("kind")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[a._v(":")]),a._v(" AlertProvider\n"),e("span",{pre:!0,attrs:{class:"token key atrule"}},[a._v("metadata")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[a._v(":")]),a._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[a._v("name")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[a._v(":")]),a._v(" on"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[a._v("-")]),a._v("call\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[a._v("namespace")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[a._v(":")]),a._v(" flagger\n"),e("span",{pre:!0,attrs:{class:"token key atrule"}},[a._v("spec")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[a._v(":")]),a._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[a._v("type")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[a._v(":")]),a._v(" slack\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[a._v("channel")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[a._v(":")]),a._v(" on"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[a._v("-")]),a._v("call"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[a._v("-")]),a._v("alerts\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[a._v("username")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[a._v(":")]),a._v(" flagger\n "),e("span",{pre:!0,attrs:{class:"token comment"}},[a._v("# webhook address (ignored if secretRef is specified)")]),a._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[a._v("address")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[a._v(":")]),a._v(" https"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[a._v(":")]),a._v("//hooks.slack.com/services/YOUR/SLACK/WEBHOOK\n "),e("span",{pre:!0,attrs:{class:"token comment"}},[a._v("# secret containing the webhook address (optional)")]),a._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[a._v("secretRef")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[a._v(":")]),a._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[a._v("name")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[a._v(":")]),a._v(" on"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[a._v("-")]),a._v("call"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[a._v("-")]),a._v("url\n"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[a._v("---")]),a._v("\n"),e("span",{pre:!0,attrs:{class:"token key atrule"}},[a._v("apiVersion")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[a._v(":")]),a._v(" v1\n"),e("span",{pre:!0,attrs:{class:"token key atrule"}},[a._v("kind")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[a._v(":")]),a._v(" Secret\n"),e("span",{pre:!0,attrs:{class:"token key atrule"}},[a._v("metadata")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[a._v(":")]),a._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[a._v("name")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[a._v(":")]),a._v(" on"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[a._v("-")]),a._v("call"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[a._v("-")]),a._v("url\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[a._v("namespace")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[a._v(":")]),a._v(" flagger\n"),e("span",{pre:!0,attrs:{class:"token key atrule"}},[a._v("data")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[a._v(":")]),a._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[a._v("address")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[a._v(":")]),a._v(" ")]),a._v("\n")])])]),e("p",[a._v("The alert provider "),e("strong",[a._v("type")]),a._v(" can be: "),e("code",[a._v("slack")]),a._v(", "),e("code",[a._v("msteams")]),a._v(", "),e("code",[a._v("rocket")]),a._v(" or "),e("code",[a._v("discord")]),a._v(". When set to "),e("code",[a._v("discord")]),a._v(",\nFlagger will use "),e("a",{attrs:{href:"https://birdie0.github.io/discord-webhooks-guide/other/slack_formatting.html",target:"_blank",rel:"noopener noreferrer"}},[a._v("Slack formatting"),e("OutboundLink")],1),a._v("\nand will append "),e("code",[a._v("/slack")]),a._v(" to the Discord address.")]),a._v(" "),e("p",[a._v("When not specified, "),e("strong",[a._v("channel")]),a._v(" defaults to "),e("code",[a._v("general")]),a._v(" and "),e("strong",[a._v("username")]),a._v(" defaults to "),e("code",[a._v("flagger")]),a._v(".")]),a._v(" "),e("p",[a._v("When "),e("strong",[a._v("secretRef")]),a._v(" is specified, the Kubernetes secret must contain a data field named "),e("code",[a._v("address")]),a._v(",\nthe address in the secret will take precedence over the "),e("strong",[a._v("address")]),a._v(" field in the provider spec.")]),a._v(" "),e("p",[a._v("The canary analysis can have a list of alerts, each alert referencing an alert provider:")]),a._v(" "),e("div",{staticClass:"language-yaml extra-class"},[e("pre",{pre:!0,attrs:{class:"language-yaml"}},[e("code",[a._v(" "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[a._v("analysis")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[a._v(":")]),a._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[a._v("alerts")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[a._v(":")]),a._v("\n "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[a._v("-")]),a._v(" "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[a._v("name")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[a._v(":")]),a._v(" "),e("span",{pre:!0,attrs:{class:"token string"}},[a._v('"on-call Slack"')]),a._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[a._v("severity")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[a._v(":")]),a._v(" error\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[a._v("providerRef")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[a._v(":")]),a._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[a._v("name")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[a._v(":")]),a._v(" on"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[a._v("-")]),a._v("call\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[a._v("namespace")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[a._v(":")]),a._v(" flagger\n "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[a._v("-")]),a._v(" "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[a._v("name")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[a._v(":")]),a._v(" "),e("span",{pre:!0,attrs:{class:"token string"}},[a._v('"qa Discord"')]),a._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[a._v("severity")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[a._v(":")]),a._v(" warn\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[a._v("providerRef")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[a._v(":")]),a._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[a._v("name")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[a._v(":")]),a._v(" qa"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[a._v("-")]),a._v("discord\n "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[a._v("-")]),a._v(" "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[a._v("name")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[a._v(":")]),a._v(" "),e("span",{pre:!0,attrs:{class:"token string"}},[a._v('"dev MS Teams"')]),a._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[a._v("severity")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[a._v(":")]),a._v(" info\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[a._v("providerRef")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[a._v(":")]),a._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[a._v("name")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[a._v(":")]),a._v(" dev"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[a._v("-")]),a._v("msteams\n")])])]),e("p",[a._v("Alert fields:")]),a._v(" "),e("ul",[e("li",[e("strong",[a._v("name")]),a._v(" (required)")]),a._v(" "),e("li",[e("strong",[a._v("severity")]),a._v(" levels: "),e("code",[a._v("info")]),a._v(", "),e("code",[a._v("warn")]),a._v(", "),e("code",[a._v("error")]),a._v(" (default info)")]),a._v(" "),e("li",[e("strong",[a._v("providerRef.name")]),a._v(" alert provider name (required)")]),a._v(" "),e("li",[e("strong",[a._v("providerRef.namespace")]),a._v(" alert provider namespace (defaults to the canary namespace)")])]),a._v(" "),e("p",[a._v("When the severity is set to "),e("code",[a._v("warn")]),a._v(", Flagger will alert when waiting on manual confirmation or if the analysis fails.\nWhen the severity is set to "),e("code",[a._v("error")]),a._v(", Flagger will alert only if the canary analysis fails.")]),a._v(" "),e("h2",{attrs:{id:"prometheus-alert-manager"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#prometheus-alert-manager","aria-hidden":"true"}},[a._v("#")]),a._v(" Prometheus Alert Manager")]),a._v(" "),e("p",[a._v("You can use Alertmanager to trigger alerts when a canary deployment failed:")]),a._v(" "),e("div",{staticClass:"language-yaml extra-class"},[e("pre",{pre:!0,attrs:{class:"language-yaml"}},[e("code",[a._v(" "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[a._v("-")]),a._v(" "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[a._v("alert")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[a._v(":")]),a._v(" canary_rollback\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[a._v("expr")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[a._v(":")]),a._v(" flagger_canary_status "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[a._v(">")]),a._v(" 1\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[a._v("for")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[a._v(":")]),a._v(" 1m\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[a._v("labels")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[a._v(":")]),a._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[a._v("severity")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[a._v(":")]),a._v(" warning\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[a._v("annotations")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[a._v(":")]),a._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[a._v("summary")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[a._v(":")]),a._v(" "),e("span",{pre:!0,attrs:{class:"token string"}},[a._v('"Canary failed"')]),a._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[a._v("description")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[a._v(":")]),a._v(" "),e("span",{pre:!0,attrs:{class:"token string"}},[a._v('"Workload {{ $labels.name }} namespace {{ $labels.namespace }}"')]),a._v("\n")])])])])},[],!1,null,null,null);t.default=n.exports}}]); \ No newline at end of file +(window.webpackJsonp=window.webpackJsonp||[]).push([[30],{290:function(a,t,e){"use strict";e.r(t);var s=e(37),n=Object(s.a)({},function(){var a=this,t=a.$createElement,e=a._self._c||t;return e("ContentSlotsDistributor",{attrs:{"slot-key":a.$parent.slotKey}},[e("h1",{attrs:{id:"alerting"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#alerting","aria-hidden":"true"}},[a._v("#")]),a._v(" Alerting")]),a._v(" "),e("p",[a._v("Flagger can be configured to send alerts to various chat platforms. You can define a global alert provider at\ninstall time or configure alerts on a per canary basis.")]),a._v(" "),e("h2",{attrs:{id:"global-configuration"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#global-configuration","aria-hidden":"true"}},[a._v("#")]),a._v(" Global configuration")]),a._v(" "),e("p",[a._v("Flagger can be configured to send Slack notifications:")]),a._v(" "),e("div",{staticClass:"language-bash extra-class"},[e("pre",{pre:!0,attrs:{class:"language-bash"}},[e("code",[a._v("helm upgrade -i flagger flagger/flagger "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[a._v("\\")]),a._v("\n--set slack.url"),e("span",{pre:!0,attrs:{class:"token operator"}},[a._v("=")]),a._v("https://hooks.slack.com/services/YOUR/SLACK/WEBHOOK "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[a._v("\\")]),a._v("\n--set slack.channel"),e("span",{pre:!0,attrs:{class:"token operator"}},[a._v("=")]),a._v("general "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[a._v("\\")]),a._v("\n--set slack.user"),e("span",{pre:!0,attrs:{class:"token operator"}},[a._v("=")]),a._v("flagger\n")])])]),e("p",[a._v("Once configured with a Slack incoming "),e("strong",[a._v("webhook")]),a._v(", Flagger will post messages when a canary deployment\nhas been initialised, when a new revision has been detected and if the canary analysis failed or succeeded.")]),a._v(" "),e("p",[e("img",{attrs:{src:"https://raw.githubusercontent.com/weaveworks/flagger/master/docs/screens/slack-canary-notifications.png",alt:"Slack Notifications"}})]),a._v(" "),e("p",[a._v("A canary deployment will be rolled back if the progress deadline exceeded or if the analysis reached the\nmaximum number of failed checks:")]),a._v(" "),e("p",[e("img",{attrs:{src:"https://raw.githubusercontent.com/weaveworks/flagger/master/docs/screens/slack-canary-failed.png",alt:"Slack Notifications"}})]),a._v(" "),e("p",[a._v("Flagger can be configured to send notifications to Microsoft Teams:")]),a._v(" "),e("div",{staticClass:"language-bash extra-class"},[e("pre",{pre:!0,attrs:{class:"language-bash"}},[e("code",[a._v("helm upgrade -i flagger flagger/flagger "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[a._v("\\")]),a._v("\n--set msteams.url"),e("span",{pre:!0,attrs:{class:"token operator"}},[a._v("=")]),a._v("https://outlook.office.com/webhook/YOUR/TEAMS/WEBHOOK\n")])])]),e("p",[a._v("Similar to Slack, Flagger alerts on canary analysis events:")]),a._v(" "),e("p",[e("img",{attrs:{src:"https://raw.githubusercontent.com/weaveworks/flagger/master/docs/screens/flagger-ms-teams-notifications.png",alt:"MS Teams Notifications"}})]),a._v(" "),e("p",[e("img",{attrs:{src:"https://raw.githubusercontent.com/weaveworks/flagger/master/docs/screens/flagger-ms-teams-failed.png",alt:"MS Teams Notifications"}})]),a._v(" "),e("h2",{attrs:{id:"canary-configuration"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#canary-configuration","aria-hidden":"true"}},[a._v("#")]),a._v(" Canary configuration")]),a._v(" "),e("p",[a._v("Configuring alerting globally has several limitations as it's not possible to specify different channels\nor configure the verbosity on a per canary basis.\nTo make the alerting move flexible, the canary analysis can be extended\nwith a list of alerts that reference an alert provider.\nFor each alert, users can configure the severity level.\nThe alerts section overrides the global setting.")]),a._v(" "),e("p",[a._v("Slack example:")]),a._v(" "),e("div",{staticClass:"language-yaml extra-class"},[e("pre",{pre:!0,attrs:{class:"language-yaml"}},[e("code",[e("span",{pre:!0,attrs:{class:"token key atrule"}},[a._v("apiVersion")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[a._v(":")]),a._v(" flagger.app/v1beta1\n"),e("span",{pre:!0,attrs:{class:"token key atrule"}},[a._v("kind")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[a._v(":")]),a._v(" AlertProvider\n"),e("span",{pre:!0,attrs:{class:"token key atrule"}},[a._v("metadata")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[a._v(":")]),a._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[a._v("name")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[a._v(":")]),a._v(" on"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[a._v("-")]),a._v("call\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[a._v("namespace")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[a._v(":")]),a._v(" flagger\n"),e("span",{pre:!0,attrs:{class:"token key atrule"}},[a._v("spec")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[a._v(":")]),a._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[a._v("type")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[a._v(":")]),a._v(" slack\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[a._v("channel")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[a._v(":")]),a._v(" on"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[a._v("-")]),a._v("call"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[a._v("-")]),a._v("alerts\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[a._v("username")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[a._v(":")]),a._v(" flagger\n "),e("span",{pre:!0,attrs:{class:"token comment"}},[a._v("# webhook address (ignored if secretRef is specified)")]),a._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[a._v("address")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[a._v(":")]),a._v(" https"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[a._v(":")]),a._v("//hooks.slack.com/services/YOUR/SLACK/WEBHOOK\n "),e("span",{pre:!0,attrs:{class:"token comment"}},[a._v("# secret containing the webhook address (optional)")]),a._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[a._v("secretRef")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[a._v(":")]),a._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[a._v("name")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[a._v(":")]),a._v(" on"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[a._v("-")]),a._v("call"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[a._v("-")]),a._v("url\n"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[a._v("---")]),a._v("\n"),e("span",{pre:!0,attrs:{class:"token key atrule"}},[a._v("apiVersion")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[a._v(":")]),a._v(" v1\n"),e("span",{pre:!0,attrs:{class:"token key atrule"}},[a._v("kind")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[a._v(":")]),a._v(" Secret\n"),e("span",{pre:!0,attrs:{class:"token key atrule"}},[a._v("metadata")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[a._v(":")]),a._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[a._v("name")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[a._v(":")]),a._v(" on"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[a._v("-")]),a._v("call"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[a._v("-")]),a._v("url\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[a._v("namespace")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[a._v(":")]),a._v(" flagger\n"),e("span",{pre:!0,attrs:{class:"token key atrule"}},[a._v("data")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[a._v(":")]),a._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[a._v("address")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[a._v(":")]),a._v(" ")]),a._v("\n")])])]),e("p",[a._v("The alert provider "),e("strong",[a._v("type")]),a._v(" can be: "),e("code",[a._v("slack")]),a._v(", "),e("code",[a._v("msteams")]),a._v(", "),e("code",[a._v("rocket")]),a._v(" or "),e("code",[a._v("discord")]),a._v(". When set to "),e("code",[a._v("discord")]),a._v(",\nFlagger will use "),e("a",{attrs:{href:"https://birdie0.github.io/discord-webhooks-guide/other/slack_formatting.html",target:"_blank",rel:"noopener noreferrer"}},[a._v("Slack formatting"),e("OutboundLink")],1),a._v("\nand will append "),e("code",[a._v("/slack")]),a._v(" to the Discord address.")]),a._v(" "),e("p",[a._v("When not specified, "),e("strong",[a._v("channel")]),a._v(" defaults to "),e("code",[a._v("general")]),a._v(" and "),e("strong",[a._v("username")]),a._v(" defaults to "),e("code",[a._v("flagger")]),a._v(".")]),a._v(" "),e("p",[a._v("When "),e("strong",[a._v("secretRef")]),a._v(" is specified, the Kubernetes secret must contain a data field named "),e("code",[a._v("address")]),a._v(",\nthe address in the secret will take precedence over the "),e("strong",[a._v("address")]),a._v(" field in the provider spec.")]),a._v(" "),e("p",[a._v("The canary analysis can have a list of alerts, each alert referencing an alert provider:")]),a._v(" "),e("div",{staticClass:"language-yaml extra-class"},[e("pre",{pre:!0,attrs:{class:"language-yaml"}},[e("code",[a._v(" "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[a._v("analysis")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[a._v(":")]),a._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[a._v("alerts")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[a._v(":")]),a._v("\n "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[a._v("-")]),a._v(" "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[a._v("name")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[a._v(":")]),a._v(" "),e("span",{pre:!0,attrs:{class:"token string"}},[a._v('"on-call Slack"')]),a._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[a._v("severity")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[a._v(":")]),a._v(" error\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[a._v("providerRef")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[a._v(":")]),a._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[a._v("name")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[a._v(":")]),a._v(" on"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[a._v("-")]),a._v("call\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[a._v("namespace")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[a._v(":")]),a._v(" flagger\n "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[a._v("-")]),a._v(" "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[a._v("name")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[a._v(":")]),a._v(" "),e("span",{pre:!0,attrs:{class:"token string"}},[a._v('"qa Discord"')]),a._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[a._v("severity")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[a._v(":")]),a._v(" warn\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[a._v("providerRef")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[a._v(":")]),a._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[a._v("name")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[a._v(":")]),a._v(" qa"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[a._v("-")]),a._v("discord\n "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[a._v("-")]),a._v(" "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[a._v("name")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[a._v(":")]),a._v(" "),e("span",{pre:!0,attrs:{class:"token string"}},[a._v('"dev MS Teams"')]),a._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[a._v("severity")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[a._v(":")]),a._v(" info\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[a._v("providerRef")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[a._v(":")]),a._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[a._v("name")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[a._v(":")]),a._v(" dev"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[a._v("-")]),a._v("msteams\n")])])]),e("p",[a._v("Alert fields:")]),a._v(" "),e("ul",[e("li",[e("strong",[a._v("name")]),a._v(" (required)")]),a._v(" "),e("li",[e("strong",[a._v("severity")]),a._v(" levels: "),e("code",[a._v("info")]),a._v(", "),e("code",[a._v("warn")]),a._v(", "),e("code",[a._v("error")]),a._v(" (default info)")]),a._v(" "),e("li",[e("strong",[a._v("providerRef.name")]),a._v(" alert provider name (required)")]),a._v(" "),e("li",[e("strong",[a._v("providerRef.namespace")]),a._v(" alert provider namespace (defaults to the canary namespace)")])]),a._v(" "),e("p",[a._v("When the severity is set to "),e("code",[a._v("warn")]),a._v(", Flagger will alert when waiting on manual confirmation or if the analysis fails.\nWhen the severity is set to "),e("code",[a._v("error")]),a._v(", Flagger will alert only if the canary analysis fails.")]),a._v(" "),e("h2",{attrs:{id:"prometheus-alert-manager"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#prometheus-alert-manager","aria-hidden":"true"}},[a._v("#")]),a._v(" Prometheus Alert Manager")]),a._v(" "),e("p",[a._v("You can use Alertmanager to trigger alerts when a canary deployment failed:")]),a._v(" "),e("div",{staticClass:"language-yaml extra-class"},[e("pre",{pre:!0,attrs:{class:"language-yaml"}},[e("code",[a._v(" "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[a._v("-")]),a._v(" "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[a._v("alert")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[a._v(":")]),a._v(" canary_rollback\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[a._v("expr")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[a._v(":")]),a._v(" flagger_canary_status "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[a._v(">")]),a._v(" 1\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[a._v("for")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[a._v(":")]),a._v(" 1m\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[a._v("labels")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[a._v(":")]),a._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[a._v("severity")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[a._v(":")]),a._v(" warning\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[a._v("annotations")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[a._v(":")]),a._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[a._v("summary")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[a._v(":")]),a._v(" "),e("span",{pre:!0,attrs:{class:"token string"}},[a._v('"Canary failed"')]),a._v("\n "),e("span",{pre:!0,attrs:{class:"token key atrule"}},[a._v("description")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[a._v(":")]),a._v(" "),e("span",{pre:!0,attrs:{class:"token string"}},[a._v('"Workload {{ $labels.name }} namespace {{ $labels.namespace }}"')]),a._v("\n")])])])])},[],!1,null,null,null);t.default=n.exports}}]); \ No newline at end of file diff --git a/assets/js/5.0c32bdf9.js b/assets/js/5.0c32bdf9.js deleted file mode 100644 index 44e85859..00000000 --- a/assets/js/5.0c32bdf9.js +++ /dev/null @@ -1 +0,0 @@ -(window.webpackJsonp=window.webpackJsonp||[]).push([[5],{265:function(e,r,t){"use strict";t.r(r);var a=t(37),n=Object(a.a)({},function(){var e=this,r=e.$createElement,t=e._self._c||r;return t("ContentSlotsDistributor",{attrs:{"slot-key":e.$parent.slotKey}},[t("h2",{attrs:{id:"progressive-delivery"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#progressive-delivery","aria-hidden":"true"}},[e._v("#")]),e._v(" Progressive Delivery")]),e._v(" "),t("p",[e._v("Flagger was designed to give developers confidence in automating production releases with progressive delivery techniques.")]),e._v(" "),t("div",{staticClass:"tip custom-block"},[t("p",{staticClass:"custom-block-title"},[e._v("Canary release")]),e._v(" "),t("p",[e._v("A benefit of using canary releases is the ability to do capacity testing of the new version in a production environment\nwith a safe rollback strategy if issues are found. By slowly ramping up the load, you can monitor and capture metrics\nabout how the new version impacts the production environment.")]),e._v(" "),t("p",[t("a",{attrs:{href:"https://martinfowler.com/bliki/CanaryRelease.html",target:"_blank",rel:"noopener noreferrer"}},[e._v("Martin Fowler"),t("OutboundLink")],1)])]),e._v(" "),t("p",[e._v("Flagger can run automated application analysis, testing, promotion and rollback for the following deployment strategies:")]),e._v(" "),t("ul",[t("li",[t("p",[t("strong",[e._v("Canary")]),e._v(" (progressive traffic shifting)")]),e._v(" "),t("ul",[t("li",[t("a",{attrs:{href:"https://docs.flagger.app/tutorials/istio-progressive-delivery",target:"_blank",rel:"noopener noreferrer"}},[e._v("Istio"),t("OutboundLink")],1),e._v(",\n"),t("a",{attrs:{href:"https://docs.flagger.app/tutorials/linkerd-progressive-delivery",target:"_blank",rel:"noopener noreferrer"}},[e._v("Linkerd"),t("OutboundLink")],1),e._v(",\n"),t("a",{attrs:{href:"https://docs.flagger.app/tutorials/appmesh-progressive-delivery",target:"_blank",rel:"noopener noreferrer"}},[e._v("App Mesh"),t("OutboundLink")],1),e._v(",\n"),t("a",{attrs:{href:"https://docs.flagger.app/tutorials/contour-progressive-delivery",target:"_blank",rel:"noopener noreferrer"}},[e._v("Contour"),t("OutboundLink")],1),e._v(",\n"),t("a",{attrs:{href:"https://docs.flagger.app/tutorials/gloo-progressive-delivery",target:"_blank",rel:"noopener noreferrer"}},[e._v("Gloo"),t("OutboundLink")],1),e._v(",\n"),t("a",{attrs:{href:"https://docs.flagger.app/tutorials/nginx-progressive-delivery",target:"_blank",rel:"noopener noreferrer"}},[e._v("NGINX"),t("OutboundLink")],1),e._v(",\n"),t("a",{attrs:{href:"https://docs.flagger.app/tutorials/skipper-progressive-delivery",target:"_blank",rel:"noopener noreferrer"}},[e._v("Skipper"),t("OutboundLink")],1)])])]),e._v(" "),t("li",[t("p",[t("strong",[e._v("A/B Testing")]),e._v(" (HTTP headers and cookies traffic routing)")]),e._v(" "),t("ul",[t("li",[t("a",{attrs:{href:"https://docs.flagger.app/tutorials/istio-ab-testing",target:"_blank",rel:"noopener noreferrer"}},[e._v("Istio"),t("OutboundLink")],1),e._v(",\n"),t("a",{attrs:{href:"https://docs.flagger.app/tutorials/appmesh-progressive-delivery#a-b-testing",target:"_blank",rel:"noopener noreferrer"}},[e._v("App Mesh"),t("OutboundLink")],1),e._v(",\n"),t("a",{attrs:{href:"https://docs.flagger.app/tutorials/contour-progressive-delivery#a-b-testing",target:"_blank",rel:"noopener noreferrer"}},[e._v("Contour"),t("OutboundLink")],1),e._v(",\n"),t("a",{attrs:{href:"https://docs.flagger.app/tutorials/nginx-progressive-delivery#a-b-testing",target:"_blank",rel:"noopener noreferrer"}},[e._v("NGINX"),t("OutboundLink")],1)])])]),e._v(" "),t("li",[t("p",[t("strong",[e._v("Blue/Green")]),e._v(" (traffic switching and mirroring)")]),e._v(" "),t("ul",[t("li",[t("a",{attrs:{href:"https://docs.flagger.app/tutorials/kubernetes-blue-green",target:"_blank",rel:"noopener noreferrer"}},[e._v("Kubernetes CNI"),t("OutboundLink")],1),e._v(",\n"),t("a",{attrs:{href:"https://docs.flagger.app/tutorials/istio-progressive-delivery#traffic-mirroring",target:"_blank",rel:"noopener noreferrer"}},[e._v("Istio"),t("OutboundLink")],1),e._v(",\nLinkerd, App Mesh, Contour, Gloo, NGINX")])])])]),e._v(" "),t("p",[e._v("Flagger can be configured to "),t("a",{attrs:{href:"https://docs.flagger.app/usage/alerting",target:"_blank",rel:"noopener noreferrer"}},[e._v("send notifications"),t("OutboundLink")],1),e._v(" to\nSlack, Microsoft Teams, Discord or Rocket.\nIt will post messages when a deployment has been initialised,\nwhen a new revision has been detected and if the canary analysis failed or succeeded.")]),e._v(" "),t("h2",{attrs:{id:"gitops"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#gitops","aria-hidden":"true"}},[e._v("#")]),e._v(" GitOps")]),e._v(" "),t("p",[t("img",{attrs:{src:"/flagger-gitops.png",alt:"GtiOps with Flagger and FluxCD"}})]),e._v(" "),t("p",[e._v("You can build fully automated GitOps pipelines for canary deployments with Flagger and\n"),t("a",{attrs:{href:"https://github.com/fluxcd/flux",target:"_blank",rel:"noopener noreferrer"}},[e._v("FluxCD"),t("OutboundLink")],1),e._v(" (CNCF sandbox project).")]),e._v(" "),t("div",{staticClass:"tip custom-block"},[t("p",{staticClass:"custom-block-title"},[e._v("GitOps")]),e._v(" "),t("p",[e._v("GitOps is a way to do Kubernetes cluster management and application delivery.\nIt works by using Git as a single source of truth for declarative infrastructure and applications.\nWith Git at the center of your delivery pipelines, developers can make pull requests\nto accelerate and simplify application deployments and operations tasks to Kubernetes.")]),e._v(" "),t("p",[t("a",{attrs:{href:"https://www.weave.works/technologies/gitops/",target:"_blank",rel:"noopener noreferrer"}},[e._v("Weaveworks"),t("OutboundLink")],1)])]),e._v(" "),t("p",[e._v("GitOps tutorials:")]),e._v(" "),t("ul",[t("li",[t("a",{attrs:{href:"https://github.com/stefanprodan/gitops-istio",target:"_blank",rel:"noopener noreferrer"}},[e._v("Progressive Delivery for Istio with Flagger and FluxCD"),t("OutboundLink")],1)]),e._v(" "),t("li",[t("a",{attrs:{href:"https://eks.handson.flagger.dev",target:"_blank",rel:"noopener noreferrer"}},[e._v("Progressive Delivery for AWS App Mesh with Flagger and FluxCD"),t("OutboundLink")],1)]),e._v(" "),t("li",[t("a",{attrs:{href:"https://helm.workshop.flagger.dev",target:"_blank",rel:"noopener noreferrer"}},[e._v("Progressive Delivery for Linkerd with Flagger, FluxCD and Helm v3"),t("OutboundLink")],1)])]),e._v(" "),t("h2",{attrs:{id:"getting-help"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#getting-help","aria-hidden":"true"}},[e._v("#")]),e._v(" Getting Help")]),e._v(" "),t("p",[e._v("If you have any questions about Flagger and progressive delivery:")]),e._v(" "),t("ul",[t("li",[e._v("Read the Flagger "),t("a",{attrs:{href:"https://docs.flagger.app",target:"_blank",rel:"noopener noreferrer"}},[e._v("docs"),t("OutboundLink")],1),e._v(".")]),e._v(" "),t("li",[e._v("Invite yourself to the "),t("a",{attrs:{href:"https://slack.weave.works/",target:"_blank",rel:"noopener noreferrer"}},[e._v("Weave community slack"),t("OutboundLink")],1),e._v("\nand join the "),t("a",{attrs:{href:"https://weave-community.slack.com/messages/flagger/",target:"_blank",rel:"noopener noreferrer"}},[e._v("#flagger"),t("OutboundLink")],1),e._v(" channel.")]),e._v(" "),t("li",[e._v("Join the "),t("a",{attrs:{href:"https://www.meetup.com/pro/Weave/",target:"_blank",rel:"noopener noreferrer"}},[e._v("Weave User Group"),t("OutboundLink")],1),e._v(" and get invited to online talks,\nhands-on training and meetups in your area.")]),e._v(" "),t("li",[e._v("File an "),t("a",{attrs:{href:"https://github.com/weaveworks/flagger/issues/new",target:"_blank",rel:"noopener noreferrer"}},[e._v("issue"),t("OutboundLink")],1),e._v(".")])]),e._v(" "),t("p",[e._v("Your feedback is always welcome!")])])},[],!1,null,null,null);r.default=n.exports}}]); \ No newline at end of file diff --git a/assets/js/5.f0e8e1f0.js b/assets/js/5.f0e8e1f0.js new file mode 100644 index 00000000..866f69f7 --- /dev/null +++ b/assets/js/5.f0e8e1f0.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[5],{265:function(e,r,t){"use strict";t.r(r);var a=t(37),n=Object(a.a)({},function(){var e=this,r=e.$createElement,t=e._self._c||r;return t("ContentSlotsDistributor",{attrs:{"slot-key":e.$parent.slotKey}},[t("h2",{attrs:{id:"progressive-delivery"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#progressive-delivery","aria-hidden":"true"}},[e._v("#")]),e._v(" Progressive Delivery")]),e._v(" "),t("p",[e._v("Flagger was designed to give developers confidence in automating production releases with progressive delivery techniques.")]),e._v(" "),t("div",{staticClass:"tip custom-block"},[t("p",{staticClass:"custom-block-title"},[e._v("Canary release")]),e._v(" "),t("p",[e._v("A benefit of using canary releases is the ability to do capacity testing of the new version in a production environment\nwith a safe rollback strategy if issues are found. By slowly ramping up the load, you can monitor and capture metrics\nabout how the new version impacts the production environment.")]),e._v(" "),t("p",[t("a",{attrs:{href:"https://martinfowler.com/bliki/CanaryRelease.html",target:"_blank",rel:"noopener noreferrer"}},[e._v("Martin Fowler"),t("OutboundLink")],1)])]),e._v(" "),t("p",[e._v("Flagger can run automated application analysis, testing, promotion and rollback for the following deployment strategies:")]),e._v(" "),t("ul",[t("li",[t("p",[t("strong",[e._v("Canary")]),e._v(" (progressive traffic shifting)")]),e._v(" "),t("ul",[t("li",[t("a",{attrs:{href:"https://docs.flagger.app/tutorials/istio-progressive-delivery",target:"_blank",rel:"noopener noreferrer"}},[e._v("Istio"),t("OutboundLink")],1),e._v(",\n"),t("a",{attrs:{href:"https://docs.flagger.app/tutorials/linkerd-progressive-delivery",target:"_blank",rel:"noopener noreferrer"}},[e._v("Linkerd"),t("OutboundLink")],1),e._v(",\n"),t("a",{attrs:{href:"https://docs.flagger.app/tutorials/appmesh-progressive-delivery",target:"_blank",rel:"noopener noreferrer"}},[e._v("App Mesh"),t("OutboundLink")],1),e._v(",\n"),t("a",{attrs:{href:"https://docs.flagger.app/tutorials/contour-progressive-delivery",target:"_blank",rel:"noopener noreferrer"}},[e._v("Contour"),t("OutboundLink")],1),e._v(",\n"),t("a",{attrs:{href:"https://docs.flagger.app/tutorials/gloo-progressive-delivery",target:"_blank",rel:"noopener noreferrer"}},[e._v("Gloo"),t("OutboundLink")],1),e._v(",\n"),t("a",{attrs:{href:"https://docs.flagger.app/tutorials/nginx-progressive-delivery",target:"_blank",rel:"noopener noreferrer"}},[e._v("NGINX"),t("OutboundLink")],1),e._v(",\n"),t("a",{attrs:{href:"https://docs.flagger.app/tutorials/skipper-progressive-delivery",target:"_blank",rel:"noopener noreferrer"}},[e._v("Skipper"),t("OutboundLink")],1),e._v(" "),t("a",{attrs:{href:"https://docs.flagger.app/tutorials/traefik-progressive-delivery",target:"_blank",rel:"noopener noreferrer"}},[e._v("Traefik"),t("OutboundLink")],1)])])]),e._v(" "),t("li",[t("p",[t("strong",[e._v("A/B Testing")]),e._v(" (HTTP headers and cookies traffic routing)")]),e._v(" "),t("ul",[t("li",[t("a",{attrs:{href:"https://docs.flagger.app/tutorials/istio-ab-testing",target:"_blank",rel:"noopener noreferrer"}},[e._v("Istio"),t("OutboundLink")],1),e._v(",\n"),t("a",{attrs:{href:"https://docs.flagger.app/tutorials/appmesh-progressive-delivery#a-b-testing",target:"_blank",rel:"noopener noreferrer"}},[e._v("App Mesh"),t("OutboundLink")],1),e._v(",\n"),t("a",{attrs:{href:"https://docs.flagger.app/tutorials/contour-progressive-delivery#a-b-testing",target:"_blank",rel:"noopener noreferrer"}},[e._v("Contour"),t("OutboundLink")],1),e._v(",\n"),t("a",{attrs:{href:"https://docs.flagger.app/tutorials/nginx-progressive-delivery#a-b-testing",target:"_blank",rel:"noopener noreferrer"}},[e._v("NGINX"),t("OutboundLink")],1)])])]),e._v(" "),t("li",[t("p",[t("strong",[e._v("Blue/Green")]),e._v(" (traffic switching and mirroring)")]),e._v(" "),t("ul",[t("li",[t("a",{attrs:{href:"https://docs.flagger.app/tutorials/kubernetes-blue-green",target:"_blank",rel:"noopener noreferrer"}},[e._v("Kubernetes CNI"),t("OutboundLink")],1),e._v(",\n"),t("a",{attrs:{href:"https://docs.flagger.app/tutorials/istio-progressive-delivery#traffic-mirroring",target:"_blank",rel:"noopener noreferrer"}},[e._v("Istio"),t("OutboundLink")],1),e._v(",\nLinkerd, App Mesh, Contour, Gloo, NGINX, Skipper, Traefik")])])])]),e._v(" "),t("p",[e._v("Flagger can be configured to "),t("a",{attrs:{href:"https://docs.flagger.app/usage/alerting",target:"_blank",rel:"noopener noreferrer"}},[e._v("send notifications"),t("OutboundLink")],1),e._v(" to\nSlack, Microsoft Teams, Discord or Rocket.\nIt will post messages when a deployment has been initialised,\nwhen a new revision has been detected and if the canary analysis failed or succeeded.")]),e._v(" "),t("h2",{attrs:{id:"gitops"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#gitops","aria-hidden":"true"}},[e._v("#")]),e._v(" GitOps")]),e._v(" "),t("p",[t("img",{attrs:{src:"/flagger-gitops.png",alt:"GtiOps with Flagger and FluxCD"}})]),e._v(" "),t("p",[e._v("You can build fully automated GitOps pipelines for canary deployments with Flagger and\n"),t("a",{attrs:{href:"https://github.com/fluxcd/flux",target:"_blank",rel:"noopener noreferrer"}},[e._v("FluxCD"),t("OutboundLink")],1),e._v(" (CNCF sandbox project).")]),e._v(" "),t("div",{staticClass:"tip custom-block"},[t("p",{staticClass:"custom-block-title"},[e._v("GitOps")]),e._v(" "),t("p",[e._v("GitOps is a way to do Kubernetes cluster management and application delivery.\nIt works by using Git as a single source of truth for declarative infrastructure and applications.\nWith Git at the center of your delivery pipelines, developers can make pull requests\nto accelerate and simplify application deployments and operations tasks to Kubernetes.")]),e._v(" "),t("p",[t("a",{attrs:{href:"https://www.weave.works/technologies/gitops/",target:"_blank",rel:"noopener noreferrer"}},[e._v("Weaveworks"),t("OutboundLink")],1)])]),e._v(" "),t("p",[e._v("GitOps tutorials:")]),e._v(" "),t("ul",[t("li",[t("a",{attrs:{href:"https://github.com/stefanprodan/gitops-istio",target:"_blank",rel:"noopener noreferrer"}},[e._v("Progressive Delivery for Istio with Flagger and Flux"),t("OutboundLink")],1)]),e._v(" "),t("li",[t("a",{attrs:{href:"https://eks.handson.flagger.dev",target:"_blank",rel:"noopener noreferrer"}},[e._v("Progressive Delivery for AWS App Mesh with Flagger and Flux"),t("OutboundLink")],1)]),e._v(" "),t("li",[t("a",{attrs:{href:"https://github.com/stefanprodan/gitops-linkerd",target:"_blank",rel:"noopener noreferrer"}},[e._v("Progressive Delivery for Linkerd and Contour with Flagger and Flux"),t("OutboundLink")],1)])]),e._v(" "),t("h2",{attrs:{id:"getting-help"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#getting-help","aria-hidden":"true"}},[e._v("#")]),e._v(" Getting Help")]),e._v(" "),t("p",[e._v("If you have any questions about Flagger and progressive delivery:")]),e._v(" "),t("ul",[t("li",[e._v("Read the Flagger "),t("a",{attrs:{href:"https://docs.flagger.app",target:"_blank",rel:"noopener noreferrer"}},[e._v("docs"),t("OutboundLink")],1),e._v(".")]),e._v(" "),t("li",[e._v("Invite yourself to the "),t("a",{attrs:{href:"https://slack.weave.works/",target:"_blank",rel:"noopener noreferrer"}},[e._v("Weave community slack"),t("OutboundLink")],1),e._v("\nand join the "),t("a",{attrs:{href:"https://weave-community.slack.com/messages/flagger/",target:"_blank",rel:"noopener noreferrer"}},[e._v("#flagger"),t("OutboundLink")],1),e._v(" channel.")]),e._v(" "),t("li",[e._v("Join the "),t("a",{attrs:{href:"https://www.meetup.com/pro/Weave/",target:"_blank",rel:"noopener noreferrer"}},[e._v("Weave User Group"),t("OutboundLink")],1),e._v(" and get invited to online talks,\nhands-on training and meetups in your area.")]),e._v(" "),t("li",[e._v("File an "),t("a",{attrs:{href:"https://github.com/weaveworks/flagger/issues/new",target:"_blank",rel:"noopener noreferrer"}},[e._v("issue"),t("OutboundLink")],1),e._v(".")])]),e._v(" "),t("p",[e._v("Your feedback is always welcome!")])])},[],!1,null,null,null);r.default=n.exports}}]); \ No newline at end of file diff --git a/assets/js/8.8b72a8d3.js b/assets/js/8.e3907a66.js similarity index 97% rename from assets/js/8.8b72a8d3.js rename to assets/js/8.e3907a66.js index c781fe1f..ce74f5cc 100644 --- a/assets/js/8.8b72a8d3.js +++ b/assets/js/8.e3907a66.js @@ -1 +1 @@ -(window.webpackJsonp=window.webpackJsonp||[]).push([[8],{269:function(e,t,a){"use strict";a.r(t);var s=a(37),r=Object(s.a)({},function(){var e=this,t=e.$createElement,a=e._self._c||t;return a("ContentSlotsDistributor",{attrs:{"slot-key":e.$parent.slotKey}},[a("h1",{attrs:{id:"release-guide"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#release-guide","aria-hidden":"true"}},[e._v("#")]),e._v(" Release Guide")]),e._v(" "),a("p",[e._v("This document describes how to release Flagger.")]),e._v(" "),a("h2",{attrs:{id:"release"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#release","aria-hidden":"true"}},[e._v("#")]),e._v(" Release")]),e._v(" "),a("p",[e._v("To release a new Flagger version (e.g. "),a("code",[e._v("2.0.0")]),e._v(") follow these steps:")]),e._v(" "),a("ul",[a("li",[e._v("create a branch "),a("code",[e._v("git checkout -b prep-2.0.0")])]),e._v(" "),a("li",[e._v("set the version in code and manifests "),a("code",[e._v("TAG=2.0.0 make version-set")])]),e._v(" "),a("li",[e._v("commit changes and merge PR")]),e._v(" "),a("li",[e._v("checkout master "),a("code",[e._v("git checkout master && git pull")])]),e._v(" "),a("li",[e._v("tag master "),a("code",[e._v("make release")])])]),e._v(" "),a("h2",{attrs:{id:"ci"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#ci","aria-hidden":"true"}},[e._v("#")]),e._v(" CI")]),e._v(" "),a("p",[e._v("After the tag has been pushed to GitHub, the CI release pipeline does the following:")]),e._v(" "),a("ul",[a("li",[e._v("creates a GitHub release")]),e._v(" "),a("li",[e._v("pushes the Flagger binary and change log to GitHub release")]),e._v(" "),a("li",[e._v("pushes the Flagger container image to Docker Hub")]),e._v(" "),a("li",[e._v("pushes the Helm chart to github-pages branch")]),e._v(" "),a("li",[e._v("GitHub pages publishes the new chart version on the Helm repository")])]),e._v(" "),a("h2",{attrs:{id:"docs"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#docs","aria-hidden":"true"}},[e._v("#")]),e._v(" Docs")]),e._v(" "),a("p",[e._v("The documentation "),a("a",{attrs:{href:"https://docs.flagger.app",target:"_blank",rel:"noopener noreferrer"}},[e._v("website"),a("OutboundLink")],1),e._v(" is built from the "),a("code",[e._v("docs")]),e._v(" branch.")]),e._v(" "),a("p",[e._v("After a Flagger release, publish the docs with:")]),e._v(" "),a("ul",[a("li",[a("code",[e._v("git checkout master && git pull")])]),e._v(" "),a("li",[a("code",[e._v("git checkout docs")])]),e._v(" "),a("li",[a("code",[e._v("git rebase master")])]),e._v(" "),a("li",[a("code",[e._v("git push origin docs")])])])])},[],!1,null,null,null);t.default=r.exports}}]); \ No newline at end of file +(window.webpackJsonp=window.webpackJsonp||[]).push([[8],{268:function(e,t,a){"use strict";a.r(t);var s=a(37),r=Object(s.a)({},function(){var e=this,t=e.$createElement,a=e._self._c||t;return a("ContentSlotsDistributor",{attrs:{"slot-key":e.$parent.slotKey}},[a("h1",{attrs:{id:"release-guide"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#release-guide","aria-hidden":"true"}},[e._v("#")]),e._v(" Release Guide")]),e._v(" "),a("p",[e._v("This document describes how to release Flagger.")]),e._v(" "),a("h2",{attrs:{id:"release"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#release","aria-hidden":"true"}},[e._v("#")]),e._v(" Release")]),e._v(" "),a("p",[e._v("To release a new Flagger version (e.g. "),a("code",[e._v("2.0.0")]),e._v(") follow these steps:")]),e._v(" "),a("ul",[a("li",[e._v("create a branch "),a("code",[e._v("git checkout -b prep-2.0.0")])]),e._v(" "),a("li",[e._v("set the version in code and manifests "),a("code",[e._v("TAG=2.0.0 make version-set")])]),e._v(" "),a("li",[e._v("commit changes and merge PR")]),e._v(" "),a("li",[e._v("checkout master "),a("code",[e._v("git checkout master && git pull")])]),e._v(" "),a("li",[e._v("tag master "),a("code",[e._v("make release")])])]),e._v(" "),a("h2",{attrs:{id:"ci"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#ci","aria-hidden":"true"}},[e._v("#")]),e._v(" CI")]),e._v(" "),a("p",[e._v("After the tag has been pushed to GitHub, the CI release pipeline does the following:")]),e._v(" "),a("ul",[a("li",[e._v("creates a GitHub release")]),e._v(" "),a("li",[e._v("pushes the Flagger binary and change log to GitHub release")]),e._v(" "),a("li",[e._v("pushes the Flagger container image to Docker Hub")]),e._v(" "),a("li",[e._v("pushes the Helm chart to github-pages branch")]),e._v(" "),a("li",[e._v("GitHub pages publishes the new chart version on the Helm repository")])]),e._v(" "),a("h2",{attrs:{id:"docs"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#docs","aria-hidden":"true"}},[e._v("#")]),e._v(" Docs")]),e._v(" "),a("p",[e._v("The documentation "),a("a",{attrs:{href:"https://docs.flagger.app",target:"_blank",rel:"noopener noreferrer"}},[e._v("website"),a("OutboundLink")],1),e._v(" is built from the "),a("code",[e._v("docs")]),e._v(" branch.")]),e._v(" "),a("p",[e._v("After a Flagger release, publish the docs with:")]),e._v(" "),a("ul",[a("li",[a("code",[e._v("git checkout master && git pull")])]),e._v(" "),a("li",[a("code",[e._v("git checkout docs")])]),e._v(" "),a("li",[a("code",[e._v("git rebase master")])]),e._v(" "),a("li",[a("code",[e._v("git push origin docs")])])])])},[],!1,null,null,null);t.default=r.exports}}]); \ No newline at end of file diff --git a/assets/js/9.7919fc1d.js b/assets/js/9.12e0bca8.js similarity index 98% rename from assets/js/9.7919fc1d.js rename to assets/js/9.12e0bca8.js index 3cd69ace..514759f0 100644 --- a/assets/js/9.7919fc1d.js +++ b/assets/js/9.12e0bca8.js @@ -1 +1 @@ -(window.webpackJsonp=window.webpackJsonp||[]).push([[9],{268:function(e,a,t){"use strict";t.r(a);var s=t(37),r=Object(s.a)({},function(){var e=this,a=e.$createElement,t=e._self._c||a;return t("ContentSlotsDistributor",{attrs:{"slot-key":e.$parent.slotKey}},[t("h1",{attrs:{id:"upgrade-guide"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#upgrade-guide","aria-hidden":"true"}},[e._v("#")]),e._v(" Upgrade Guide")]),e._v(" "),t("p",[e._v("This document describes how to upgrade Flagger.")]),e._v(" "),t("h2",{attrs:{id:"upgrade-canaries-v1alpha3-to-v1beta1"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#upgrade-canaries-v1alpha3-to-v1beta1","aria-hidden":"true"}},[e._v("#")]),e._v(" Upgrade canaries v1alpha3 to v1beta1")]),e._v(" "),t("p",[e._v("Canary CRD changes in "),t("code",[e._v("canaries.flagger.app/v1beta1")]),e._v(":")]),e._v(" "),t("ul",[t("li",[e._v("the "),t("code",[e._v("spec.canaryAnalysis")]),e._v(" field has been deprecated and replaced with "),t("code",[e._v("spec.analysis")])]),e._v(" "),t("li",[e._v("the "),t("code",[e._v("spec.analysis.interval")]),e._v(" and "),t("code",[e._v("spec.analysis.threshold")]),e._v(" fields are required")]),e._v(" "),t("li",[e._v("the "),t("code",[e._v("status.lastAppliedSpec")]),e._v(" and "),t("code",[e._v("status.lastPromotedSpec")]),e._v(" hashing algorithm changed to "),t("code",[e._v("hash/fnv")])]),e._v(" "),t("li",[e._v("the "),t("code",[e._v("spec.analysis.alerts")]),e._v(" array can reference "),t("code",[e._v("alertproviders.flagger.app/v1beta1")]),e._v(" resources")]),e._v(" "),t("li",[e._v("the "),t("code",[e._v("spec.analysis.metrics[].templateRef")]),e._v(" can reference a "),t("code",[e._v("metrictemplate.flagger.app/v1beta1")]),e._v(" resource")]),e._v(" "),t("li",[e._v("the "),t("code",[e._v("metric.threshold")]),e._v(" field has been deprecated and replaced with "),t("code",[e._v("metric.thresholdRange")])]),e._v(" "),t("li",[e._v("the "),t("code",[e._v("spec.targetRef")]),e._v(" can reference "),t("code",[e._v("DaemonSet")]),e._v(" kind")])]),e._v(" "),t("p",[e._v("Upgrade procedure:")]),e._v(" "),t("ul",[t("li",[e._v("install the "),t("code",[e._v("v1beta1")]),e._v(" CRDs")]),e._v(" "),t("li",[e._v("update Flagger deployment")]),e._v(" "),t("li",[e._v("replace "),t("code",[e._v("apiVersion: flagger.app/v1alpha3")]),e._v(" with "),t("code",[e._v("apiVersion: flagger.app/v1beta1")]),e._v(" in all canary manifests")]),e._v(" "),t("li",[e._v("replace "),t("code",[e._v("spec.canaryAnalysis")]),e._v(" with "),t("code",[e._v("spec.analysis")]),e._v(" in all canary manifests")]),e._v(" "),t("li",[e._v("update canary manifests in cluster")])]),e._v(" "),t("p",[t("strong",[e._v("Note")]),e._v(" that after upgrading Flagger, all canaries will be triggered as the hash value used for tracking changes\nis computed differently. You can set "),t("code",[e._v("spec.skipAnalysis: true")]),e._v(" in all canary manifests before upgrading Flagger,\ndo the upgrade, wait for Flagger to finish the no-op promotions and finally set "),t("code",[e._v("skipAnalysis")]),e._v(" to "),t("code",[e._v("false")]),e._v(".")]),e._v(" "),t("p",[e._v("Update builtin metrics:")]),e._v(" "),t("ul",[t("li",[e._v("replace "),t("code",[e._v("threshold")]),e._v(" with "),t("code",[e._v("thresholdRange.min")]),e._v(" for request-success-rate")]),e._v(" "),t("li",[e._v("replace "),t("code",[e._v("threshold")]),e._v(" with "),t("code",[e._v("thresholdRange.max")]),e._v(" for request-duration")])]),e._v(" "),t("div",{staticClass:"language-yaml extra-class"},[t("pre",{pre:!0,attrs:{class:"language-yaml"}},[t("code",[t("span",{pre:!0,attrs:{class:"token key atrule"}},[e._v("metrics")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[e._v(":")]),e._v("\n"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[e._v("-")]),e._v(" "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[e._v("name")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[e._v(":")]),e._v(" request"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[e._v("-")]),e._v("success"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[e._v("-")]),e._v("rate\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[e._v("thresholdRange")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[e._v(":")]),e._v("\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[e._v("min")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[e._v(":")]),e._v(" "),t("span",{pre:!0,attrs:{class:"token number"}},[e._v("99")]),e._v("\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[e._v("interval")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[e._v(":")]),e._v(" 1m\n"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[e._v("-")]),e._v(" "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[e._v("name")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[e._v(":")]),e._v(" request"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[e._v("-")]),e._v("duration\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[e._v("thresholdRange")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[e._v(":")]),e._v("\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[e._v("max")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[e._v(":")]),e._v(" "),t("span",{pre:!0,attrs:{class:"token number"}},[e._v("500")]),e._v("\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[e._v("interval")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[e._v(":")]),e._v(" 1m\n")])])])])},[],!1,null,null,null);a.default=r.exports}}]); \ No newline at end of file +(window.webpackJsonp=window.webpackJsonp||[]).push([[9],{269:function(e,a,t){"use strict";t.r(a);var s=t(37),r=Object(s.a)({},function(){var e=this,a=e.$createElement,t=e._self._c||a;return t("ContentSlotsDistributor",{attrs:{"slot-key":e.$parent.slotKey}},[t("h1",{attrs:{id:"upgrade-guide"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#upgrade-guide","aria-hidden":"true"}},[e._v("#")]),e._v(" Upgrade Guide")]),e._v(" "),t("p",[e._v("This document describes how to upgrade Flagger.")]),e._v(" "),t("h2",{attrs:{id:"upgrade-canaries-v1alpha3-to-v1beta1"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#upgrade-canaries-v1alpha3-to-v1beta1","aria-hidden":"true"}},[e._v("#")]),e._v(" Upgrade canaries v1alpha3 to v1beta1")]),e._v(" "),t("p",[e._v("Canary CRD changes in "),t("code",[e._v("canaries.flagger.app/v1beta1")]),e._v(":")]),e._v(" "),t("ul",[t("li",[e._v("the "),t("code",[e._v("spec.canaryAnalysis")]),e._v(" field has been deprecated and replaced with "),t("code",[e._v("spec.analysis")])]),e._v(" "),t("li",[e._v("the "),t("code",[e._v("spec.analysis.interval")]),e._v(" and "),t("code",[e._v("spec.analysis.threshold")]),e._v(" fields are required")]),e._v(" "),t("li",[e._v("the "),t("code",[e._v("status.lastAppliedSpec")]),e._v(" and "),t("code",[e._v("status.lastPromotedSpec")]),e._v(" hashing algorithm changed to "),t("code",[e._v("hash/fnv")])]),e._v(" "),t("li",[e._v("the "),t("code",[e._v("spec.analysis.alerts")]),e._v(" array can reference "),t("code",[e._v("alertproviders.flagger.app/v1beta1")]),e._v(" resources")]),e._v(" "),t("li",[e._v("the "),t("code",[e._v("spec.analysis.metrics[].templateRef")]),e._v(" can reference a "),t("code",[e._v("metrictemplate.flagger.app/v1beta1")]),e._v(" resource")]),e._v(" "),t("li",[e._v("the "),t("code",[e._v("metric.threshold")]),e._v(" field has been deprecated and replaced with "),t("code",[e._v("metric.thresholdRange")])]),e._v(" "),t("li",[e._v("the "),t("code",[e._v("spec.targetRef")]),e._v(" can reference "),t("code",[e._v("DaemonSet")]),e._v(" kind")])]),e._v(" "),t("p",[e._v("Upgrade procedure:")]),e._v(" "),t("ul",[t("li",[e._v("install the "),t("code",[e._v("v1beta1")]),e._v(" CRDs")]),e._v(" "),t("li",[e._v("update Flagger deployment")]),e._v(" "),t("li",[e._v("replace "),t("code",[e._v("apiVersion: flagger.app/v1alpha3")]),e._v(" with "),t("code",[e._v("apiVersion: flagger.app/v1beta1")]),e._v(" in all canary manifests")]),e._v(" "),t("li",[e._v("replace "),t("code",[e._v("spec.canaryAnalysis")]),e._v(" with "),t("code",[e._v("spec.analysis")]),e._v(" in all canary manifests")]),e._v(" "),t("li",[e._v("update canary manifests in cluster")])]),e._v(" "),t("p",[t("strong",[e._v("Note")]),e._v(" that after upgrading Flagger, all canaries will be triggered as the hash value used for tracking changes\nis computed differently. You can set "),t("code",[e._v("spec.skipAnalysis: true")]),e._v(" in all canary manifests before upgrading Flagger,\ndo the upgrade, wait for Flagger to finish the no-op promotions and finally set "),t("code",[e._v("skipAnalysis")]),e._v(" to "),t("code",[e._v("false")]),e._v(".")]),e._v(" "),t("p",[e._v("Update builtin metrics:")]),e._v(" "),t("ul",[t("li",[e._v("replace "),t("code",[e._v("threshold")]),e._v(" with "),t("code",[e._v("thresholdRange.min")]),e._v(" for request-success-rate")]),e._v(" "),t("li",[e._v("replace "),t("code",[e._v("threshold")]),e._v(" with "),t("code",[e._v("thresholdRange.max")]),e._v(" for request-duration")])]),e._v(" "),t("div",{staticClass:"language-yaml extra-class"},[t("pre",{pre:!0,attrs:{class:"language-yaml"}},[t("code",[t("span",{pre:!0,attrs:{class:"token key atrule"}},[e._v("metrics")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[e._v(":")]),e._v("\n"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[e._v("-")]),e._v(" "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[e._v("name")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[e._v(":")]),e._v(" request"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[e._v("-")]),e._v("success"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[e._v("-")]),e._v("rate\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[e._v("thresholdRange")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[e._v(":")]),e._v("\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[e._v("min")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[e._v(":")]),e._v(" "),t("span",{pre:!0,attrs:{class:"token number"}},[e._v("99")]),e._v("\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[e._v("interval")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[e._v(":")]),e._v(" 1m\n"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[e._v("-")]),e._v(" "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[e._v("name")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[e._v(":")]),e._v(" request"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[e._v("-")]),e._v("duration\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[e._v("thresholdRange")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[e._v(":")]),e._v("\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[e._v("max")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[e._v(":")]),e._v(" "),t("span",{pre:!0,attrs:{class:"token number"}},[e._v("500")]),e._v("\n "),t("span",{pre:!0,attrs:{class:"token key atrule"}},[e._v("interval")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[e._v(":")]),e._v(" 1m\n")])])])])},[],!1,null,null,null);a.default=r.exports}}]); \ No newline at end of file diff --git a/assets/js/app.9b46ae46.js b/assets/js/app.cd972522.js similarity index 80% rename from assets/js/app.9b46ae46.js rename to assets/js/app.cd972522.js index dceedae9..4e948b34 100644 --- a/assets/js/app.9b46ae46.js +++ b/assets/js/app.cd972522.js @@ -1,8 +1,8 @@ -(window.webpackJsonp=window.webpackJsonp||[]).push([[0],[]]);!function(t){function e(e){for(var r,a,s=e[0],u=e[1],c=e[2],f=0,p=[];f0?o(r(t),9007199254740991):0}},function(t,e,n){var r=n(7),o=n(17),i=n(22),a=n(41)("src"),s=n(143),u=(""+s).split("toString");n(28).inspectSource=function(t){return s.call(t)},(t.exports=function(t,e,n,s){var c="function"==typeof n;c&&(i(n,"name")||o(n,"name",e)),t[e]!==n&&(c&&(i(n,a)||o(n,a,t[e]?""+t[e]:u.join(String(e)))),t===r?t[e]=n:s?t[e]?t[e]=n:o(t,e,n):(delete t[e],o(t,e,n)))})(Function.prototype,"toString",function(){return"function"==typeof this&&this[a]||s.call(this)})},function(t,e){var n={}.hasOwnProperty;t.exports=function(t,e){return n.call(t,e)}},function(t,e,n){var r=n(45);t.exports=function(t,e,n){if(r(t),void 0===e)return t;switch(n){case 1:return function(n){return t.call(e,n)};case 2:return function(n,r){return t.call(e,n,r)};case 3:return function(n,r,o){return t.call(e,n,r,o)}}return function(){return t.apply(e,arguments)}}},function(t,e){t.exports={}},function(t,e,n){var r=n(165),o=n(68);t.exports=function(t){return r(o(t))}},function(t,e){t.exports=function(t){if(null==t)throw TypeError("Can't call method on "+t);return t}},function(t,e,n){"use strict";var r=n(16);t.exports=function(t,e){return!!t&&r(function(){e?t.call(null,function(){},1):t.call(null)})}},function(t,e){var n=t.exports={version:"2.6.9"};"number"==typeof __e&&(__e=n)},function(t,e,n){var r=n(88);t.exports=function(t,e,n){if(r(t),void 0===e)return t;switch(n){case 1:return function(n){return t.call(e,n)};case 2:return function(n,r){return t.call(e,n,r)};case 3:return function(n,r,o){return t.call(e,n,r,o)}}return function(){return t.apply(e,arguments)}}},function(t,e,n){var r=n(26);t.exports=function(t){return Object(r(t))}},function(t,e){t.exports={}},function(t,e,n){var r=n(58),o=n(26);t.exports=function(t){return r(o(t))}},function(t,e){t.exports=!0},function(t,e){t.exports=function(t){try{return!!t()}catch(t){return!0}}},function(t,e){t.exports=function(t,e){return{enumerable:!(1&t),configurable:!(2&t),writable:!(4&t),value:e}}},function(t,e){var n={}.toString;t.exports=function(t){return n.call(t).slice(8,-1)}},function(t,e,n){"use strict";function r(t,e,n,r,o,i,a,s){var u,c="function"==typeof t?t.options:t;if(e&&(c.render=e,c.staticRenderFns=n,c._compiled=!0),r&&(c.functional=!0),i&&(c._scopeId="data-v-"+i),a?(u=function(t){(t=t||this.$vnode&&this.$vnode.ssrContext||this.parent&&this.parent.$vnode&&this.parent.$vnode.ssrContext)||"undefined"==typeof __VUE_SSR_CONTEXT__||(t=__VUE_SSR_CONTEXT__),o&&o.call(this,t),t&&t._registeredComponents&&t._registeredComponents.add(a)},c._ssrRegister=u):o&&(u=s?function(){o.call(this,this.$root.$options.shadowRoot)}:o),u)if(c.functional){c._injectStyles=u;var l=c.render;c.render=function(t,e){return u.call(e),l(t,e)}}else{var f=c.beforeCreate;c.beforeCreate=f?[].concat(f,u):[u]}return{exports:t,options:c}}n.d(e,"a",function(){return r})},function(t,e,n){for(var r=n(146),o=n(61),i=n(21),a=n(7),s=n(17),u=n(31),c=n(2),l=c("iterator"),f=c("toStringTag"),p=u.Array,h={CSSRuleList:!0,CSSStyleDeclaration:!1,CSSValueList:!1,ClientRectList:!1,DOMRectList:!1,DOMStringList:!1,DOMTokenList:!0,DataTransferItemList:!1,FileList:!1,HTMLAllCollection:!1,HTMLCollection:!1,HTMLFormElement:!1,HTMLSelectElement:!1,MediaList:!0,MimeTypeArray:!1,NamedNodeMap:!1,NodeList:!0,PaintRequestList:!1,Plugin:!1,PluginArray:!1,SVGLengthList:!1,SVGNumberList:!1,SVGPathSegList:!1,SVGPointList:!1,SVGStringList:!1,SVGTransformList:!1,SourceBufferList:!1,StyleSheetList:!0,TextTrackCueList:!1,TextTrackList:!1,TouchList:!1},d=o(h),v=0;v0?r:n)(t)}},function(t,e,n){"use strict";var r=n(162)(!0);n(101)(String,"String",function(t){this._t=String(t),this._i=0},function(){var t,e=this._t,n=this._i;return n>=e.length?{value:void 0,done:!0}:(t=r(e,n),this._i+=t.length,{value:t,done:!1})})},function(t,e){t.exports=function(t){if("function"!=typeof t)throw TypeError(t+" is not a function!");return t}},function(t,e){var n=0,r=Math.random();t.exports=function(t){return"Symbol(".concat(void 0===t?"":t,")_",(++n+r).toString(36))}},function(t,e,n){var r=n(9).f,o=n(19),i=n(4)("toStringTag");t.exports=function(t,e,n){t&&!o(t=n?t:t.prototype,i)&&r(t,i,{configurable:!0,value:e})}},function(t,e,n){var r=n(68);t.exports=function(t){return Object(r(t))}},function(t,e,n){var r=n(12).f,o=Function.prototype,i=/^\s*function ([^ (]*)/;"name"in o||n(13)&&r(o,"name",{configurable:!0,get:function(){try{return(""+this).match(i)[1]}catch(t){return""}}})},function(t,e,n){},function(t,e,n){"use strict";var r=n(127),o=n.n(r);var i=n(128),a=n.n(i),s=n(129),u=n.n(s);function c(t){return function(t){if(o()(t)){for(var e=0,n=new Array(t.length);e0?o(r(t),9007199254740991):0}},function(t,e,n){var r=n(7),o=n(17),i=n(22),a=n(41)("src"),s=n(143),u=(""+s).split("toString");n(28).inspectSource=function(t){return s.call(t)},(t.exports=function(t,e,n,s){var c="function"==typeof n;c&&(i(n,"name")||o(n,"name",e)),t[e]!==n&&(c&&(i(n,a)||o(n,a,t[e]?""+t[e]:u.join(String(e)))),t===r?t[e]=n:s?t[e]?t[e]=n:o(t,e,n):(delete t[e],o(t,e,n)))})(Function.prototype,"toString",function(){return"function"==typeof this&&this[a]||s.call(this)})},function(t,e){var n={}.hasOwnProperty;t.exports=function(t,e){return n.call(t,e)}},function(t,e,n){var r=n(45);t.exports=function(t,e,n){if(r(t),void 0===e)return t;switch(n){case 1:return function(n){return t.call(e,n)};case 2:return function(n,r){return t.call(e,n,r)};case 3:return function(n,r,o){return t.call(e,n,r,o)}}return function(){return t.apply(e,arguments)}}},function(t,e){t.exports={}},function(t,e,n){var r=n(165),o=n(68);t.exports=function(t){return r(o(t))}},function(t,e){t.exports=function(t){if(null==t)throw TypeError("Can't call method on "+t);return t}},function(t,e,n){"use strict";var r=n(16);t.exports=function(t,e){return!!t&&r(function(){e?t.call(null,function(){},1):t.call(null)})}},function(t,e){var n=t.exports={version:"2.6.9"};"number"==typeof __e&&(__e=n)},function(t,e,n){var r=n(88);t.exports=function(t,e,n){if(r(t),void 0===e)return t;switch(n){case 1:return function(n){return t.call(e,n)};case 2:return function(n,r){return t.call(e,n,r)};case 3:return function(n,r,o){return t.call(e,n,r,o)}}return function(){return t.apply(e,arguments)}}},function(t,e,n){var r=n(26);t.exports=function(t){return Object(r(t))}},function(t,e){t.exports={}},function(t,e,n){var r=n(58),o=n(26);t.exports=function(t){return r(o(t))}},function(t,e){t.exports=!0},function(t,e){t.exports=function(t){try{return!!t()}catch(t){return!0}}},function(t,e){t.exports=function(t,e){return{enumerable:!(1&t),configurable:!(2&t),writable:!(4&t),value:e}}},function(t,e){var n={}.toString;t.exports=function(t){return n.call(t).slice(8,-1)}},function(t,e,n){"use strict";function r(t,e,n,r,o,i,a,s){var u,c="function"==typeof t?t.options:t;if(e&&(c.render=e,c.staticRenderFns=n,c._compiled=!0),r&&(c.functional=!0),i&&(c._scopeId="data-v-"+i),a?(u=function(t){(t=t||this.$vnode&&this.$vnode.ssrContext||this.parent&&this.parent.$vnode&&this.parent.$vnode.ssrContext)||"undefined"==typeof __VUE_SSR_CONTEXT__||(t=__VUE_SSR_CONTEXT__),o&&o.call(this,t),t&&t._registeredComponents&&t._registeredComponents.add(a)},c._ssrRegister=u):o&&(u=s?function(){o.call(this,this.$root.$options.shadowRoot)}:o),u)if(c.functional){c._injectStyles=u;var l=c.render;c.render=function(t,e){return u.call(e),l(t,e)}}else{var f=c.beforeCreate;c.beforeCreate=f?[].concat(f,u):[u]}return{exports:t,options:c}}n.d(e,"a",function(){return r})},function(t,e,n){for(var r=n(146),o=n(61),i=n(21),a=n(7),s=n(17),u=n(31),c=n(2),l=c("iterator"),f=c("toStringTag"),p=u.Array,h={CSSRuleList:!0,CSSStyleDeclaration:!1,CSSValueList:!1,ClientRectList:!1,DOMRectList:!1,DOMStringList:!1,DOMTokenList:!0,DataTransferItemList:!1,FileList:!1,HTMLAllCollection:!1,HTMLCollection:!1,HTMLFormElement:!1,HTMLSelectElement:!1,MediaList:!0,MimeTypeArray:!1,NamedNodeMap:!1,NodeList:!0,PaintRequestList:!1,Plugin:!1,PluginArray:!1,SVGLengthList:!1,SVGNumberList:!1,SVGPathSegList:!1,SVGPointList:!1,SVGStringList:!1,SVGTransformList:!1,SourceBufferList:!1,StyleSheetList:!0,TextTrackCueList:!1,TextTrackList:!1,TouchList:!1},v=o(h),d=0;d0?r:n)(t)}},function(t,e,n){"use strict";var r=n(162)(!0);n(101)(String,"String",function(t){this._t=String(t),this._i=0},function(){var t,e=this._t,n=this._i;return n>=e.length?{value:void 0,done:!0}:(t=r(e,n),this._i+=t.length,{value:t,done:!1})})},function(t,e){t.exports=function(t){if("function"!=typeof t)throw TypeError(t+" is not a function!");return t}},function(t,e){var n=0,r=Math.random();t.exports=function(t){return"Symbol(".concat(void 0===t?"":t,")_",(++n+r).toString(36))}},function(t,e,n){var r=n(9).f,o=n(19),i=n(4)("toStringTag");t.exports=function(t,e,n){t&&!o(t=n?t:t.prototype,i)&&r(t,i,{configurable:!0,value:e})}},function(t,e,n){var r=n(68);t.exports=function(t){return Object(r(t))}},function(t,e,n){var r=n(12).f,o=Function.prototype,i=/^\s*function ([^ (]*)/;"name"in o||n(13)&&r(o,"name",{configurable:!0,get:function(){try{return(""+this).match(i)[1]}catch(t){return""}}})},function(t,e,n){},function(t,e,n){"use strict";var r=n(127),o=n.n(r);var i=n(128),a=n.n(i),s=n(129),u=n.n(s);function c(t){return function(t){if(o()(t)){for(var e=0,n=new Array(t.length);e
'};function o(t,e,n){return tn?n:t}function i(t){return 100*(-1+t)}n.configure=function(t){var e,n;for(e in t)void 0!==(n=t[e])&&t.hasOwnProperty(e)&&(r[e]=n);return this},n.status=null,n.set=function(t){var e=n.isStarted();t=o(t,r.minimum,1),n.status=1===t?null:t;var u=n.render(!e),c=u.querySelector(r.barSelector),l=r.speed,f=r.easing;return u.offsetWidth,a(function(e){""===r.positionUsing&&(r.positionUsing=n.getPositioningCSS()),s(c,function(t,e,n){var o;return(o="translate3d"===r.positionUsing?{transform:"translate3d("+i(t)+"%,0,0)"}:"translate"===r.positionUsing?{transform:"translate("+i(t)+"%,0)"}:{"margin-left":i(t)+"%"}).transition="all "+e+"ms "+n,o}(t,l,f)),1===t?(s(u,{transition:"none",opacity:1}),u.offsetWidth,setTimeout(function(){s(u,{transition:"all "+l+"ms linear",opacity:0}),setTimeout(function(){n.remove(),e()},l)},l)):setTimeout(e,l)}),this},n.isStarted=function(){return"number"==typeof n.status},n.start=function(){n.status||n.set(0);var t=function(){setTimeout(function(){n.status&&(n.trickle(),t())},r.trickleSpeed)};return r.trickle&&t(),this},n.done=function(t){return t||n.status?n.inc(.3+.5*Math.random()).set(1):this},n.inc=function(t){var e=n.status;return e?("number"!=typeof t&&(t=(1-e)*o(Math.random()*e,.1,.95)),e=o(e+t,0,.994),n.set(e)):n.start()},n.trickle=function(){return n.inc(Math.random()*r.trickleRate)},t=0,e=0,n.promise=function(r){return r&&"resolved"!==r.state()?(0===e&&n.start(),t++,e++,r.always(function(){0==--e?(t=0,n.done()):n.set((t-e)/t)}),this):this},n.render=function(t){if(n.isRendered())return document.getElementById("nprogress");c(document.documentElement,"nprogress-busy");var e=document.createElement("div");e.id="nprogress",e.innerHTML=r.template;var o,a=e.querySelector(r.barSelector),u=t?"-100":i(n.status||0),l=document.querySelector(r.parent);return s(a,{transition:"all 0 linear",transform:"translate3d("+u+"%,0,0)"}),r.showSpinner||(o=e.querySelector(r.spinnerSelector))&&p(o),l!=document.body&&c(l,"nprogress-custom-parent"),l.appendChild(e),e},n.remove=function(){l(document.documentElement,"nprogress-busy"),l(document.querySelector(r.parent),"nprogress-custom-parent");var t=document.getElementById("nprogress");t&&p(t)},n.isRendered=function(){return!!document.getElementById("nprogress")},n.getPositioningCSS=function(){var t=document.body.style,e="WebkitTransform"in t?"Webkit":"MozTransform"in t?"Moz":"msTransform"in t?"ms":"OTransform"in t?"O":"";return e+"Perspective"in t?"translate3d":e+"Transform"in t?"translate":"margin"};var a=function(){var t=[];function e(){var n=t.shift();n&&n(e)}return function(n){t.push(n),1==t.length&&e()}}(),s=function(){var t=["Webkit","O","Moz","ms"],e={};function n(n){return n=n.replace(/^-ms-/,"ms-").replace(/-([\da-z])/gi,function(t,e){return e.toUpperCase()}),e[n]||(e[n]=function(e){var n=document.body.style;if(e in n)return e;for(var r,o=t.length,i=e.charAt(0).toUpperCase()+e.slice(1);o--;)if((r=t[o]+i)in n)return r;return e}(n))}function r(t,e,r){e=n(e),t.style[e]=r}return function(t,e){var n,o,i=arguments;if(2==i.length)for(n in e)void 0!==(o=e[n])&&e.hasOwnProperty(n)&&r(t,n,o);else r(t,i[1],i[2])}}();function u(t,e){return("string"==typeof t?t:f(t)).indexOf(" "+e+" ")>=0}function c(t,e){var n=f(t),r=n+e;u(n,e)||(t.className=r.substring(1))}function l(t,e){var n,r=f(t);u(t,e)&&(n=r.replace(" "+e+" "," "),t.className=n.substring(1,n.length-1))}function f(t){return(" "+(t.className||"")+" ").replace(/\s+/gi," ")}function p(t){t&&t.parentNode&&t.parentNode.removeChild(t)}return n})?r.call(e,n,e,t):r)||(t.exports=o)},function(t,e,n){"use strict";var r=n(1),o=n(55)(1);r(r.P+r.F*!n(27)([].map,!0),"Array",{map:function(t){return o(this,t,arguments[1])}})},function(t,e,n){var r=n(29),o=n(58),i=n(30),a=n(20),s=n(144);t.exports=function(t,e){var n=1==t,u=2==t,c=3==t,l=4==t,f=6==t,p=5==t||f,h=e||s;return function(e,s,d){for(var v,m,y=i(e),g=o(y),b=r(s,d,3),_=a(g.length),w=0,x=n?h(e,_):u?h(e,0):void 0;_>w;w++)if((p||w in g)&&(m=b(v=g[w],w,y),t))if(n)x[w]=m;else if(m)switch(t){case 3:return!0;case 5:return v;case 6:return w;case 2:x.push(v)}else if(l)return!1;return f?-1:c||l?l:x}}},function(t,e,n){"use strict";var r=n(1),o=n(55)(0),i=n(27)([].forEach,!0);r(r.P+r.F*!i,"Array",{forEach:function(t){return o(this,t,arguments[1])}})},function(t,e,n){var r=n(28),o=n(7),i=o["__core-js_shared__"]||(o["__core-js_shared__"]={});(t.exports=function(t,e){return i[t]||(i[t]=void 0!==e?e:{})})("versions",[]).push({version:r.version,mode:n(94)?"pure":"global",copyright:"© 2019 Denis Pushkarev (zloirock.ru)"})},function(t,e,n){var r=n(42);t.exports=Object("z").propertyIsEnumerable(0)?Object:function(t){return"String"==r(t)?t.split(""):Object(t)}},function(t,e,n){"use strict";var r=n(94),o=n(1),i=n(21),a=n(17),s=n(31),u=n(147),c=n(65),l=n(151),f=n(2)("iterator"),p=!([].keys&&"next"in[].keys()),h=function(){return this};t.exports=function(t,e,n,d,v,m,y){u(n,e,d);var g,b,_,w=function(t){if(!p&&t in S)return S[t];switch(t){case"keys":case"values":return function(){return new n(this,t)}}return function(){return new n(this,t)}},x=e+" Iterator",k="values"==v,O=!1,S=t.prototype,C=S[f]||S["@@iterator"]||v&&S[v],A=C||w(v),E=v?k?w("entries"):A:void 0,$="Array"==e&&S.entries||C;if($&&(_=l($.call(new t)))!==Object.prototype&&_.next&&(c(_,x,!0),r||"function"==typeof _[f]||a(_,f,h)),k&&C&&"values"!==C.name&&(O=!0,A=function(){return C.call(this)}),r&&!y||!p&&!O&&S[f]||a(S,f,A),s[e]=A,s[x]=h,v)if(g={values:k?A:w("values"),keys:m?A:w("keys"),entries:E},y)for(b in g)b in S||i(S,b,g[b]);else o(o.P+o.F*(p||O),e,g);return g}},function(t,e,n){var r=n(8),o=n(148),i=n(64),a=n(63)("IE_PROTO"),s=function(){},u=function(){var t,e=n(92)("iframe"),r=i.length;for(e.style.display="none",n(150).appendChild(e),e.src="javascript:",(t=e.contentWindow.document).open(),t.write(" + diff --git a/dev/index.html b/dev/index.html index eac5d6b3..b7117b3b 100644 --- a/dev/index.html +++ b/dev/index.html @@ -13,7 +13,7 @@ - + @@ -30,6 +30,6 @@ GitHub
- + diff --git a/dev/release-guide.html b/dev/release-guide.html index c06fe2dc..1bb7df2b 100644 --- a/dev/release-guide.html +++ b/dev/release-guide.html @@ -13,7 +13,7 @@ - + @@ -38,6 +38,6 @@ →

- + diff --git a/dev/upgrade-guide.html b/dev/upgrade-guide.html index bd521b55..abe323b9 100644 --- a/dev/upgrade-guide.html +++ b/dev/upgrade-guide.html @@ -13,7 +13,7 @@ - + @@ -45,6 +45,6 @@ do the upgrade, wait for Flagger to finish the no-op promotions and finally set

- + diff --git a/index.html b/index.html index d9c398a3..a2e01bce 100644 --- a/index.html +++ b/index.html @@ -13,7 +13,7 @@ - + @@ -33,7 +33,7 @@ Progressive Delivery Operator for Kubernetes

Get Started → -

Safer Releases

Reduce the risk of introducing a new software version in production by gradually shifting traffic to the new version while measuring metrics like HTTP/gRPC request success rate and latency.

Flexible Traffic Routing

Shift and route traffic between app versions using a service mesh like Istio, Linkerd or AWS App Mesh. Or if a service mesh does not meet your needs, use an Ingress controller like Contour, Gloo, NGINX or Skipper.

Extensible Validation

Besides the builtin metrics checks, you can extend your application analysis with custom Prometheus, Datadog, CloudWatch metrics and webooks for running acceptance tests, load tests, or any other custom validation.

Progressive Delivery

Flagger was designed to give developers confidence in automating production releases with progressive delivery techniques.

Canary release

A benefit of using canary releases is the ability to do capacity testing of the new version in a production environment +

Safer Releases

Reduce the risk of introducing a new software version in production by gradually shifting traffic to the new version while measuring metrics like HTTP/gRPC request success rate and latency.

Flexible Traffic Routing

Shift and route traffic between app versions using a service mesh like Istio, Linkerd or AWS App Mesh. Or if a service mesh does not meet your needs, use an Ingress controller like Contour, Gloo, NGINX, Skipper or Traefik.

Extensible Validation

Besides the builtin metrics checks, you can extend your application analysis with custom Prometheus, Datadog, CloudWatch metrics and webooks for running acceptance tests, load tests, or any other custom validation.

Progressive Delivery

Flagger was designed to give developers confidence in automating production releases with progressive delivery techniques.

Canary release

A benefit of using canary releases is the ability to do capacity testing of the new version in a production environment with a safe rollback strategy if issues are found. By slowly ramping up the load, you can monitor and capture metrics about how the new version impacts the production environment.

Martin Fowler

Flagger can run automated application analysis, testing, promotion and rollback for the following deployment strategies:

Flagger can be configured to send notifications to +Linkerd, App Mesh, Contour, Gloo, NGINX, Skipper, Traefik

Flagger can be configured to send notifications to Slack, Microsoft Teams, Discord or Rocket. It will post messages when a deployment has been initialised, when a new revision has been detected and if the canary analysis failed or succeeded.

GitOps

GtiOps with Flagger and FluxCD

You can build fully automated GitOps pipelines for canary deployments with Flagger and FluxCD (CNCF sandbox project).

GitOps

GitOps is a way to do Kubernetes cluster management and application delivery. It works by using Git as a single source of truth for declarative infrastructure and applications. With Git at the center of your delivery pipelines, developers can make pull requests -to accelerate and simplify application deployments and operations tasks to Kubernetes.

Weaveworks

GitOps tutorials:

Getting Help

If you have any questions about Flagger and progressive delivery:

GitOps tutorials:

Getting Help

If you have any questions about Flagger and progressive delivery:

Your feedback is always welcome!

- + diff --git a/install/flagger-install-on-eks-appmesh.html b/install/flagger-install-on-eks-appmesh.html index a1b7ce00..a76d13f2 100644 --- a/install/flagger-install-on-eks-appmesh.html +++ b/install/flagger-install-on-eks-appmesh.html @@ -13,7 +13,7 @@ - + @@ -92,6 +92,6 @@ Status: →

- + diff --git a/install/flagger-install-on-google-cloud.html b/install/flagger-install-on-google-cloud.html index f02edc6f..2be3d742 100644 --- a/install/flagger-install-on-google-cloud.html +++ b/install/flagger-install-on-google-cloud.html @@ -13,7 +13,7 @@ - + @@ -203,6 +203,6 @@ https://storage.googleapis.com/gke-release/istio/release/1.0.6-gke.3/patches/ins →

- + diff --git a/install/flagger-install-on-kubernetes.html b/install/flagger-install-on-kubernetes.html index b399d9e0..1de47086 100644 --- a/install/flagger-install-on-kubernetes.html +++ b/install/flagger-install-on-kubernetes.html @@ -13,7 +13,7 @@ - + @@ -146,6 +146,6 @@ to https://outlook.office.com/webhook/YOUR/TEAMS/WEBHOOK.

- + diff --git a/install/index.html b/install/index.html index 80145c31..49dbb679 100644 --- a/install/index.html +++ b/install/index.html @@ -13,7 +13,7 @@ - + @@ -30,6 +30,6 @@ GitHub
- + diff --git a/intro/faq.html b/intro/faq.html index a77ed3ae..ec625c12 100644 --- a/intro/faq.html +++ b/intro/faq.html @@ -13,7 +13,7 @@ - + @@ -478,6 +478,6 @@ the two services and the website rule will be moved to the end of the list in th →

- + diff --git a/intro/index.html b/intro/index.html index ad6d29f4..5fbfd0ef 100644 --- a/intro/index.html +++ b/intro/index.html @@ -13,7 +13,7 @@ - + @@ -46,6 +46,6 @@ and install Flagger with →

- + diff --git a/tutorials/appmesh-progressive-delivery.html b/tutorials/appmesh-progressive-delivery.html index 6510f9b3..c31a803a 100644 --- a/tutorials/appmesh-progressive-delivery.html +++ b/tutorials/appmesh-progressive-delivery.html @@ -13,7 +13,7 @@ - + @@ -263,6 +263,6 @@ Promotion completed! Scaling down podinfo.test →

- + diff --git a/tutorials/canary-helm-gitops.html b/tutorials/canary-helm-gitops.html index bea67e63..07d71771 100644 --- a/tutorials/canary-helm-gitops.html +++ b/tutorials/canary-helm-gitops.html @@ -13,7 +13,7 @@ - + @@ -216,6 +216,6 @@ and - + diff --git a/tutorials/contour-progressive-delivery.html b/tutorials/contour-progressive-delivery.html index e11f4767..63ecd158 100644 --- a/tutorials/contour-progressive-delivery.html +++ b/tutorials/contour-progressive-delivery.html @@ -13,7 +13,7 @@ - + @@ -268,6 +268,6 @@ Promotion completed! Scaling down podinfo.test

- + diff --git a/tutorials/crossover-progressive-delivery.html b/tutorials/crossover-progressive-delivery.html index bb3cec91..d44911bf 100644 --- a/tutorials/crossover-progressive-delivery.html +++ b/tutorials/crossover-progressive-delivery.html @@ -13,7 +13,7 @@ - + @@ -210,6 +210,6 @@ Rolling back podinfo.test failed checks threshold reached 5 Canary failed! Scaling down podinfo.test

If you’ve enabled the Slack notifications, you’ll receive a message if the progress deadline is exceeded, or if the analysis reached the maximum number of failed checks:

Flagger Slack Notifications

- + diff --git a/tutorials/flagger-smi-istio.html b/tutorials/flagger-smi-istio.html index 29d89811..c2faba3c 100644 --- a/tutorials/flagger-smi-istio.html +++ b/tutorials/flagger-smi-istio.html @@ -13,7 +13,7 @@ - + @@ -187,6 +187,6 @@ Events: Warning Synced 1m flagger Rolling back podinfo.test failed checks threshold reached 10 Warning Synced 1m flagger Canary failed! Scaling down podinfo.test
- + diff --git a/tutorials/gloo-progressive-delivery.html b/tutorials/gloo-progressive-delivery.html index 4f74d908..00e636df 100644 --- a/tutorials/gloo-progressive-delivery.html +++ b/tutorials/gloo-progressive-delivery.html @@ -13,7 +13,7 @@ - + @@ -266,6 +266,6 @@ Flagger will send a notification with the reason why the canary failed.

F →

- + diff --git a/tutorials/index.html b/tutorials/index.html index 374ee97d..03a8682d 100644 --- a/tutorials/index.html +++ b/tutorials/index.html @@ -13,7 +13,7 @@ - + @@ -30,6 +30,6 @@ GitHub
- + diff --git a/tutorials/istio-ab-testing.html b/tutorials/istio-ab-testing.html index cd37a191..1d2722f8 100644 --- a/tutorials/istio-ab-testing.html +++ b/tutorials/istio-ab-testing.html @@ -13,7 +13,7 @@ - + @@ -198,6 +198,6 @@ Events: →

- + diff --git a/tutorials/istio-progressive-delivery.html b/tutorials/istio-progressive-delivery.html index 44599900..7471ccd4 100644 --- a/tutorials/istio-progressive-delivery.html +++ b/tutorials/istio-progressive-delivery.html @@ -13,7 +13,7 @@ - + @@ -258,6 +258,6 @@ Metrics are collected on both requests so that the deployment will only proceed →

- + diff --git a/tutorials/kubernetes-blue-green.html b/tutorials/kubernetes-blue-green.html index fe9d211c..96ccf3df 100644 --- a/tutorials/kubernetes-blue-green.html +++ b/tutorials/kubernetes-blue-green.html @@ -13,7 +13,7 @@ - + @@ -248,6 +248,6 @@ If the helm test fails, Flagger will retry until the analysis threshold is reach →

- + diff --git a/tutorials/linkerd-progressive-delivery.html b/tutorials/linkerd-progressive-delivery.html index 8df9e8d9..aa6bd8a6 100644 --- a/tutorials/linkerd-progressive-delivery.html +++ b/tutorials/linkerd-progressive-delivery.html @@ -13,7 +13,7 @@ - + @@ -340,6 +340,6 @@ Events: →

- + diff --git a/tutorials/nginx-progressive-delivery.html b/tutorials/nginx-progressive-delivery.html index 7bfdba2f..554f0f17 100644 --- a/tutorials/nginx-progressive-delivery.html +++ b/tutorials/nginx-progressive-delivery.html @@ -13,7 +13,7 @@ - + @@ -319,6 +319,6 @@ Events: →

- + diff --git a/tutorials/zero-downtime-deployments.html b/tutorials/zero-downtime-deployments.html index 19c81913..ad7f08c9 100644 --- a/tutorials/zero-downtime-deployments.html +++ b/tutorials/zero-downtime-deployments.html @@ -13,7 +13,7 @@ - + @@ -135,6 +135,6 @@ perTryTimeout: 5s retryOn: "gateway-error,connect-failure,refused-stream"

When the HPA scales down your app, your users could run into 503 errors. The above configuration will make Envoy retry the HTTP requests that failed due to gateway errors.

- + diff --git a/usage/alerting.html b/usage/alerting.html index 318436a2..f733fe25 100644 --- a/usage/alerting.html +++ b/usage/alerting.html @@ -13,7 +13,7 @@ - + @@ -101,6 +101,6 @@ When the severity is set to error, Flagger will alert only if the c →

- + diff --git a/usage/deployment-strategies.html b/usage/deployment-strategies.html index 05863e4e..7fe5fa7f 100644 --- a/usage/deployment-strategies.html +++ b/usage/deployment-strategies.html @@ -13,7 +13,7 @@ - + @@ -155,6 +155,6 @@ the Kubernetes deployment rollout.