From e3321118e50a6d5013d0df4de4b47fa9b73e6d8d Mon Sep 17 00:00:00 2001 From: stefanprodan Date: Sat, 29 Jun 2019 15:03:36 +0300 Subject: [PATCH 01/12] Fix linkerd success rate query --- pkg/metrics/linkerd.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/metrics/linkerd.go b/pkg/metrics/linkerd.go index 4a9ee294..f4d5707a 100644 --- a/pkg/metrics/linkerd.go +++ b/pkg/metrics/linkerd.go @@ -11,7 +11,7 @@ var linkerdQueries = map[string]string{ response_total{ namespace="{{ .Namespace }}", deployment=~"{{ .Name }}", - classification="failure", + classification!="failure", direction="inbound" }[{{ .Interval }}] ) From bc613905e9b93fc50b0890aa34341a9e7b6903e3 Mon Sep 17 00:00:00 2001 From: stefanprodan Date: Sat, 29 Jun 2019 15:20:35 +0300 Subject: [PATCH 02/12] Add Linkerd edge-19.6.4 e2e testing --- .circleci/config.yml | 15 +++++++ Makefile | 4 ++ test/e2e-linkerd-tests.sh | 93 +++++++++++++++++++++++++++++++++++++++ test/e2e-linkerd.sh | 29 ++++++++++++ 4 files changed, 141 insertions(+) create mode 100755 test/e2e-linkerd-tests.sh create mode 100755 test/e2e-linkerd.sh diff --git a/.circleci/config.yml b/.circleci/config.yml index 478988a2..0497333b 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -122,6 +122,17 @@ jobs: - run: test/e2e-nginx.sh - run: test/e2e-nginx-tests.sh + e2e-linkerd-testing: + machine: true + steps: + - checkout + - attach_workspace: + at: /tmp/bin + - run: test/container-build.sh + - run: test/e2e-kind.sh + - run: test/e2e-linkerd.sh + - run: test/e2e-linkerd-tests.sh + workflows: version: 2 build-test-push: @@ -146,6 +157,9 @@ workflows: - e2e-nginx-testing: requires: - build-binary + - e2e-linkerd-testing: + requires: + - build-binary - push-container: requires: - build-binary @@ -154,6 +168,7 @@ workflows: - e2e-supergloo-testing - e2e-gloo-testing - e2e-nginx-testing + - e2e-linkerd-testing release: jobs: diff --git a/Makefile b/Makefile index 145a9896..89ea050e 100644 --- a/Makefile +++ b/Makefile @@ -30,6 +30,10 @@ run-nop: GO111MODULE=on go run cmd/flagger/* -kubeconfig=$$HOME/.kube/config -log-level=info -mesh-provider=none -namespace=bg \ -metrics-server=https://prometheus.istio.weavedx.com +run-linkerd: + GO111MODULE=on go run cmd/flagger/* -kubeconfig=$$HOME/.kube/config -log-level=info -mesh-provider=smi:linkerd -namespace=demo \ + -metrics-server=https://linkerd-prometheus.istio.weavedx.com + build: GIT_COMMIT=$$(git rev-list -1 HEAD) && GO111MODULE=on CGO_ENABLED=0 GOOS=linux go build -ldflags "-s -w -X github.com/weaveworks/flagger/pkg/version.REVISION=$${GIT_COMMIT}" -a -installsuffix cgo -o ./bin/flagger ./cmd/flagger/* docker build -t weaveworks/flagger:$(TAG) . -f Dockerfile diff --git a/test/e2e-linkerd-tests.sh b/test/e2e-linkerd-tests.sh new file mode 100755 index 00000000..53ad55fb --- /dev/null +++ b/test/e2e-linkerd-tests.sh @@ -0,0 +1,93 @@ +#!/usr/bin/env bash + +# This script runs Linkerd e2e tests for Canary initialization, analysis and promotion + +set -o errexit + +REPO_ROOT=$(git rev-parse --show-toplevel) +export KUBECONFIG="$(kind get kubeconfig-path --name="kind")" + +echo '>>> Creating test namespace' +kubectl create namespace test +kubectl annotate namespace test linkerd.io/inject=enabled + +echo '>>> Installing the load tester' +kubectl -n test apply -f ${REPO_ROOT}/artifacts/loadtester/ +kubectl -n test rollout status deployment/flagger-loadtester + +echo '>>> Initialising canary' +kubectl apply -f ${REPO_ROOT}/test/e2e-workload.yaml + +cat <>> Waiting for primary to be ready' +retries=50 +count=0 +ok=false +until ${ok}; do + kubectl -n test get canary/podinfo | grep 'Initialized' && ok=true || ok=false + sleep 5 + count=$(($count + 1)) + if [[ ${count} -eq ${retries} ]]; then + kubectl -n linkerd logs deployment/flagger + echo "No more retries left" + exit 1 + fi +done + +echo '✔ Canary initialization test passed' + +echo '>>> Triggering canary deployment' +kubectl -n test set image deployment/podinfo podinfod=quay.io/stefanprodan/podinfo:1.4.1 + +echo '>>> Waiting for canary promotion' +retries=50 +count=0 +ok=false +until ${ok}; do + kubectl -n test describe deployment/podinfo-primary | grep '1.4.1' && ok=true || ok=false + sleep 10 + kubectl -n istio-system logs deployment/flagger --tail 1 + count=$(($count + 1)) + if [[ ${count} -eq ${retries} ]]; then + kubectl -n linkerd logs deployment/flagger + echo "No more retries left" + exit 1 + fi +done + +echo '✔ Canary promotion test passed' diff --git a/test/e2e-linkerd.sh b/test/e2e-linkerd.sh new file mode 100755 index 00000000..e7293e42 --- /dev/null +++ b/test/e2e-linkerd.sh @@ -0,0 +1,29 @@ +#!/usr/bin/env bash + +set -o errexit + +LINKERD_VER="edge-19.6.4" +REPO_ROOT=$(git rev-parse --show-toplevel) +export KUBECONFIG="$(kind get kubeconfig-path --name="kind")" + +curl -LO https://github.com/linkerd/linkerd2/releases/download/${LINKERD_VER}/linkerd2-cli-${LINKERD_VER}-linux > ${REPO_ROOT}/bin/linkerd +chmod +x ${REPO_ROOT}/bin/linkerd + +echo ">>> Installing Linkerd ${LINKERD_VER}" +${REPO_ROOT}/bin/linkerd install | kubectl apply -f - +${REPO_ROOT}/bin/linkerd check + +kubectl -n linkerd rollout status deployment/linkerd-controller +kubectl -n linkerd rollout status deployment/linkerd-proxy-injector + +echo '>>> Load Flagger image in Kind' +kind load docker-image test/flagger:latest + +echo '>>> Installing Flagger' +helm upgrade -i flagger ${REPO_ROOT}/charts/flagger \ +--namespace linkerd \ +--set metricsServer=http://linkerd-prometheus:9090 \ +--set meshProvider=smi:linkerd + +kubectl -n istio-system set image deployment/flagger flagger=test/flagger:latest +kubectl -n istio-system rollout status deployment/flagger \ No newline at end of file From d651e8fe48901517b3b88571861d4cd7f9d6d566 Mon Sep 17 00:00:00 2001 From: stefanprodan Date: Sat, 29 Jun 2019 15:23:40 +0300 Subject: [PATCH 03/12] Fix Linkerd metrics test --- pkg/metrics/linkerd_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/metrics/linkerd_test.go b/pkg/metrics/linkerd_test.go index 6dbfef5c..82b62dd5 100644 --- a/pkg/metrics/linkerd_test.go +++ b/pkg/metrics/linkerd_test.go @@ -8,7 +8,7 @@ import ( ) func TestLinkerdObserver_GetRequestSuccessRate(t *testing.T) { - expected := `sum(rate(response_total{namespace="default",deployment=~"podinfo",classification="failure",direction="inbound"}[1m]))/sum(rate(response_total{namespace="default",deployment=~"podinfo",direction="inbound"}[1m]))*100` + expected := `sum(rate(response_total{namespace="default",deployment=~"podinfo",classification!="failure",direction="inbound"}[1m]))/sum(rate(response_total{namespace="default",deployment=~"podinfo",direction="inbound"}[1m]))*100` ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { promql := r.URL.Query()["query"][0] From ff90c42fa7460e9b961ab837e5935f9505992f5c Mon Sep 17 00:00:00 2001 From: stefanprodan Date: Sat, 29 Jun 2019 15:41:28 +0300 Subject: [PATCH 04/12] Fix Linkerd CLI install --- test/e2e-linkerd-tests.sh | 2 +- test/e2e-linkerd.sh | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/test/e2e-linkerd-tests.sh b/test/e2e-linkerd-tests.sh index 53ad55fb..48462e3d 100755 --- a/test/e2e-linkerd-tests.sh +++ b/test/e2e-linkerd-tests.sh @@ -81,7 +81,7 @@ ok=false until ${ok}; do kubectl -n test describe deployment/podinfo-primary | grep '1.4.1' && ok=true || ok=false sleep 10 - kubectl -n istio-system logs deployment/flagger --tail 1 + kubectl -n linkerd logs deployment/flagger --tail 1 count=$(($count + 1)) if [[ ${count} -eq ${retries} ]]; then kubectl -n linkerd logs deployment/flagger diff --git a/test/e2e-linkerd.sh b/test/e2e-linkerd.sh index e7293e42..4545c64c 100755 --- a/test/e2e-linkerd.sh +++ b/test/e2e-linkerd.sh @@ -6,7 +6,7 @@ LINKERD_VER="edge-19.6.4" REPO_ROOT=$(git rev-parse --show-toplevel) export KUBECONFIG="$(kind get kubeconfig-path --name="kind")" -curl -LO https://github.com/linkerd/linkerd2/releases/download/${LINKERD_VER}/linkerd2-cli-${LINKERD_VER}-linux > ${REPO_ROOT}/bin/linkerd +curl -SsL https://github.com/linkerd/linkerd2/releases/download/${LINKERD_VER}/linkerd2-cli-${LINKERD_VER}-linux > ${REPO_ROOT}/bin/linkerd chmod +x ${REPO_ROOT}/bin/linkerd echo ">>> Installing Linkerd ${LINKERD_VER}" @@ -25,5 +25,5 @@ helm upgrade -i flagger ${REPO_ROOT}/charts/flagger \ --set metricsServer=http://linkerd-prometheus:9090 \ --set meshProvider=smi:linkerd -kubectl -n istio-system set image deployment/flagger flagger=test/flagger:latest -kubectl -n istio-system rollout status deployment/flagger \ No newline at end of file +kubectl -n linkerd set image deployment/flagger flagger=test/flagger:latest +kubectl -n linkerd rollout status deployment/flagger \ No newline at end of file From 8a809baf35ed78d69fc86ec474a915b567713749 Mon Sep 17 00:00:00 2001 From: stefanprodan Date: Sat, 29 Jun 2019 16:06:16 +0300 Subject: [PATCH 05/12] Linkerd e2e testing: set canary max weight to 50% --- test/e2e-linkerd-tests.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/e2e-linkerd-tests.sh b/test/e2e-linkerd-tests.sh index 48462e3d..400903a5 100755 --- a/test/e2e-linkerd-tests.sh +++ b/test/e2e-linkerd-tests.sh @@ -35,7 +35,7 @@ spec: canaryAnalysis: interval: 15s threshold: 15 - maxWeight: 30 + maxWeight: 50 stepWeight: 10 metrics: - name: request-success-rate From 9ed2719d1909f99ca530f864addc94bf07c6e9e1 Mon Sep 17 00:00:00 2001 From: stefanprodan Date: Sun, 30 Jun 2019 10:36:37 +0300 Subject: [PATCH 06/12] Add canary rollback test to Linkerd e2e --- test/e2e-linkerd-tests.sh | 56 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/test/e2e-linkerd-tests.sh b/test/e2e-linkerd-tests.sh index 400903a5..f5e623cb 100755 --- a/test/e2e-linkerd-tests.sh +++ b/test/e2e-linkerd-tests.sh @@ -91,3 +91,59 @@ until ${ok}; do done echo '✔ Canary promotion test passed' + +cat <>> Triggering canary deployment' +kubectl -n test set image deployment/podinfo podinfod=quay.io/stefanprodan/podinfo:1.4.2 + +echo '>>> Waiting for canary rollback' +retries=50 +count=0 +ok=false +until ${ok}; do + kubectl -n test get canary/podinfo | grep 'Failed' && ok=true || ok=false + sleep 10 + kubectl -n linkerd logs deployment/flagger --tail 1 + count=$(($count + 1)) + if [[ ${count} -eq ${retries} ]]; then + kubectl -n linkerd logs deployment/flagger + echo "No more retries left" + exit 1 + fi +done + +echo '✔ Canary rollback test passed' \ No newline at end of file From 00fa5542f7ebac8953ac37eaa516ff97f4a8339b Mon Sep 17 00:00:00 2001 From: stefanprodan Date: Sun, 30 Jun 2019 12:46:23 +0300 Subject: [PATCH 07/12] Add linkerd as mesh provider --- pkg/metrics/factory.go | 4 ++++ pkg/router/factory.go | 8 ++++++++ 2 files changed, 12 insertions(+) diff --git a/pkg/metrics/factory.go b/pkg/metrics/factory.go index cf669729..c145af94 100644 --- a/pkg/metrics/factory.go +++ b/pkg/metrics/factory.go @@ -44,6 +44,10 @@ func (factory Factory) Observer() Interface { return &LinkerdObserver{ client: factory.Client, } + case factory.MeshProvider == "linkerd": + return &LinkerdObserver{ + client: factory.Client, + } default: return &IstioObserver{ client: factory.Client, diff --git a/pkg/router/factory.go b/pkg/router/factory.go index a14fef68..b99f440f 100644 --- a/pkg/router/factory.go +++ b/pkg/router/factory.go @@ -70,6 +70,14 @@ func (factory *Factory) MeshRouter(provider string) Interface { smiClient: factory.meshClient, targetMesh: mesh, } + case provider == "linkerd": + return &SmiRouter{ + logger: factory.logger, + flaggerClient: factory.flaggerClient, + kubeClient: factory.kubeClient, + smiClient: factory.meshClient, + targetMesh: "linkerd", + } case strings.HasPrefix(provider, "supergloo"): supergloo, err := NewSuperglooRouter(context.TODO(), provider, factory.flaggerClient, factory.logger, factory.kubeConfig) if err != nil { From ad8d02f701500d4afcfebb892af56f40100c252d Mon Sep 17 00:00:00 2001 From: stefanprodan Date: Sun, 30 Jun 2019 13:03:27 +0300 Subject: [PATCH 08/12] Use Linkerd metrics when NGINX is the mesh ingress Set the metrics provider to Linkerd Prometheus when using NGINX as Linkerd Ingress. This mitigates the lack of canary metrics in the NGINX controller exporter. --- pkg/controller/scheduler.go | 13 ++++++++++++- pkg/metrics/factory.go | 14 +++++++------- 2 files changed, 19 insertions(+), 8 deletions(-) diff --git a/pkg/controller/scheduler.go b/pkg/controller/scheduler.go index 943a9809..5ed080ad 100644 --- a/pkg/controller/scheduler.go +++ b/pkg/controller/scheduler.go @@ -567,8 +567,19 @@ func (c *Controller) analyseCanary(r *flaggerv1.Canary) bool { } } + // override the global provider if one is specified in the canary spec + metricsProvider := c.meshProvider + if r.Spec.Provider != "" { + metricsProvider = r.Spec.Provider + + // set the metrics provider to Linkerd Prometheus when using NGINX as Linkerd Ingress + if r.Spec.Provider == "nginx" && strings.Contains(c.meshProvider, "linkerd") { + metricsProvider = "linkerd" + } + } + // create observer based on the mesh provider - observer := c.observerFactory.Observer() + observer := c.observerFactory.Observer(metricsProvider) // run metrics checks for _, metric := range r.Spec.CanaryAnalysis.Metrics { diff --git a/pkg/metrics/factory.go b/pkg/metrics/factory.go index c145af94..8e77f2df 100644 --- a/pkg/metrics/factory.go +++ b/pkg/metrics/factory.go @@ -22,29 +22,29 @@ func NewFactory(metricsServer string, meshProvider string, timeout time.Duration }, nil } -func (factory Factory) Observer() Interface { +func (factory Factory) Observer(provider string) Interface { switch { - case factory.MeshProvider == "none": + case provider == "none": return &HttpObserver{ client: factory.Client, } - case factory.MeshProvider == "appmesh": + case provider == "appmesh": return &EnvoyObserver{ client: factory.Client, } - case factory.MeshProvider == "nginx": + case provider == "nginx": return &NginxObserver{ client: factory.Client, } - case strings.HasPrefix(factory.MeshProvider, "gloo"): + case strings.HasPrefix(provider, "gloo"): return &GlooObserver{ client: factory.Client, } - case factory.MeshProvider == "smi:linkerd": + case provider == "smi:linkerd": return &LinkerdObserver{ client: factory.Client, } - case factory.MeshProvider == "linkerd": + case provider == "linkerd": return &LinkerdObserver{ client: factory.Client, } From a6d278ae91b174a14f9cafc8afc5f1630c4edfff Mon Sep 17 00:00:00 2001 From: stefanprodan Date: Sun, 30 Jun 2019 13:52:59 +0300 Subject: [PATCH 09/12] Add Linkerd traffic split diagram --- docs/diagrams/flagger-linkerd-traffic-split.png | Bin 0 -> 39452 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 docs/diagrams/flagger-linkerd-traffic-split.png diff --git a/docs/diagrams/flagger-linkerd-traffic-split.png b/docs/diagrams/flagger-linkerd-traffic-split.png new file mode 100644 index 0000000000000000000000000000000000000000..8ab024f5d66793e427e58622dc873f5e4908bbd4 GIT binary patch literal 39452 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
    Go)e&gZ+}R$$lz*R&kx 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 Date: Mon, 1 Jul 2019 09:23:05 +0300 Subject: [PATCH 10/12] Set min analysis interval to 10s --- pkg/apis/flagger/v1alpha3/types.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pkg/apis/flagger/v1alpha3/types.go b/pkg/apis/flagger/v1alpha3/types.go index 3d5b0364..d127a23b 100755 --- a/pkg/apis/flagger/v1alpha3/types.go +++ b/pkg/apis/flagger/v1alpha3/types.go @@ -208,6 +208,10 @@ func (c *Canary) GetAnalysisInterval() time.Duration { return AnalysisInterval } + if interval < 10*time.Second { + return time.Second * 10 + } + return interval } From 30c4faf72bb9f2c184fd1af9251628f667b1c09a Mon Sep 17 00:00:00 2001 From: stefanprodan Date: Mon, 1 Jul 2019 09:23:38 +0300 Subject: [PATCH 11/12] Add Linkerd canary deployments docs --- docs/gitbook/usage/blue-green.md | 1 + .../usage/linkerd-progressive-delivery.md | 341 ++++++++++++++++++ 2 files changed, 342 insertions(+) create mode 100644 docs/gitbook/usage/linkerd-progressive-delivery.md diff --git a/docs/gitbook/usage/blue-green.md b/docs/gitbook/usage/blue-green.md index 9eb12e11..61ab6152 100644 --- a/docs/gitbook/usage/blue-green.md +++ b/docs/gitbook/usage/blue-green.md @@ -184,6 +184,7 @@ Events: New revision detected podinfo.test Waiting for podinfo.test rollout to finish: 0 of 1 updated replicas are available +Pre-rollout check acceptance-test passed Advance podinfo.test canary iteration 1/10 Advance podinfo.test canary iteration 2/10 Advance podinfo.test canary iteration 3/10 diff --git a/docs/gitbook/usage/linkerd-progressive-delivery.md b/docs/gitbook/usage/linkerd-progressive-delivery.md new file mode 100644 index 00000000..4a4b1f35 --- /dev/null +++ b/docs/gitbook/usage/linkerd-progressive-delivery.md @@ -0,0 +1,341 @@ +# Linkerd Canary Deployments + +This guide shows you how to use Linkerd and Flagger to automate canary deployments. + +![Flagger Linkerd Traffic Split](https://raw.githubusercontent.com/weaveworks/flagger/master/docs/diagrams/flagger-linkerd-traffic-split.png) + +### Prerequisites + +Flagger requires a Kubernetes cluster **v1.11** or newer and Linker with support for SMI Traffic Spit API. + +Install Flagger in the linkerd namespace: + +```bash +helm repo add flagger https://flagger.app + +helm upgrade -i flagger flagger/flagger \ +--namespace linkerd \ +--set metricsServer=http://linkerd-prometheus:9090 \ +--set meshProvider=linkerd +``` + +Optionally you can enable Slack notifications: + +```bash +helm upgrade -i flagger flagger/flagger \ +--reuse-values \ +--namespace linkerd \ +--set slack.url=https://hooks.slack.com/services/YOUR/SLACK/WEBHOOK \ +--set slack.channel=general \ +--set slack.user=flagger +``` + +### Bootstrap + +Flagger takes a Kubernetes deployment and optionally a horizontal pod autoscaler (HPA), +then creates a series of objects (Kubernetes deployments, ClusterIP services and SMI traffic split). +These objects expose the application inside the mesh and drive the canary analysis and promotion. + +Create a test namespace and enable Linkerd proxy injection: + +```bash +kubectl create ns test +kubectl annotate namespace test linkerd.io/inject=enabled +``` + +Install the load testing service to generate traffic during the canary analysis: + +```bash +helm upgrade -i flagger-loadtester flagger/loadtester \ +--namespace=test +``` + +Create a deployment and a horizontal pod autoscaler: + +```bash +export REPO=https://raw.githubusercontent.com/weaveworks/flagger/master + +kubectl apply -f ${REPO}/artifacts/canary/deployment.yaml +kubectl apply -f ${REPO}/artifacts/canary/hpa.yaml +``` + +Create a canary custom resource for the podinfo deployment: + +```yaml +apiVersion: flagger.app/v1alpha3 +kind: Canary +metadata: + name: podinfo + namespace: test +spec: + # deployment reference + targetRef: + apiVersion: apps/v1 + kind: Deployment + name: podinfo + # HPA reference (optional) + autoscalerRef: + apiVersion: autoscaling/v2beta1 + kind: HorizontalPodAutoscaler + name: podinfo + # the maximum time in seconds for the canary deployment + # to make progress before it is rollback (default 600s) + progressDeadlineSeconds: 60 + service: + # container port + port: 9898 + canaryAnalysis: + # schedule interval (default 60s) + interval: 30s + # max number of failed metric checks before rollback + threshold: 5 + # max traffic percentage routed to canary + # percentage (0-100) + maxWeight: 50 + # canary increment step + # percentage (0-100) + stepWeight: 5 + # Linkerd Prometheus checks + metrics: + - name: request-success-rate + # minimum req success rate (non 5xx responses) + # percentage (0-100) + threshold: 99 + interval: 1m + - name: request-duration + # maximum req duration P99 + # milliseconds + threshold: 500 + interval: 30s + # testing (optional) + webhooks: + - name: acceptance-test + type: pre-rollout + url: http://flagger-loadtester.test/ + timeout: 30s + metadata: + type: bash + cmd: "curl -sd 'test' http://podinfo-canary:9898/token | grep token" + - name: load-test + type: rollout + url: http://flagger-loadtester.test/ + metadata: + cmd: "hey -z 2m -q 10 -c 2 http://podinfo:9898/" +``` + +Save the above resource as podinfo-canary.yaml and then apply it: + +```bash +kubectl apply -f ./podinfo-canary.yaml +``` + +When the canary analysis starts, Flagger will call the pre-rollout webhooks before routing traffic to the canary. +The canary analysis will run for five minutes while validating the HTTP metrics and rollout hooks every half a minute. + +After a couple of seconds Flagger will create the canary objects: + +```bash +# applied +deployment.apps/podinfo +horizontalpodautoscaler.autoscaling/podinfo +ingresses.extensions/podinfo +canary.flagger.app/podinfo + +# generated +deployment.apps/podinfo-primary +horizontalpodautoscaler.autoscaling/podinfo-primary +service/podinfo +service/podinfo-canary +service/podinfo-primary +trafficsplits.split.smi-spec.io/podinfo +``` + +After the boostrap, the podinfo deployment will be scaled to zero and the traffic to `podinfo.test` will be routed +to the primary pods. During the canary analysis, the `podinfo-canary.test` address can be used to target directly the canary pods. + +### Automated canary promotion + +Flagger implements a control loop that gradually shifts traffic to the canary while measuring key performance indicators +like HTTP requests success rate, requests average duration and pod health. +Based on analysis of the KPIs a canary is promoted or aborted, and the analysis result is published to Slack. + +![Flagger Canary Stages](https://raw.githubusercontent.com/weaveworks/flagger/master/docs/diagrams/flagger-canary-steps.png) + +Trigger a canary deployment by updating the container image: + +```bash +kubectl -n test set image deployment/podinfo \ +podinfod=quay.io/stefanprodan/podinfo:1.4.1 +``` + +Flagger detects that the deployment revision changed and starts a new rollout: + +```text +kubectl -n test describe canary/podinfo + +Status: + Canary Weight: 0 + Failed Checks: 0 + Phase: Succeeded +Events: + New revision detected! Scaling up podinfo.test + Waiting for podinfo.test rollout to finish: 0 of 1 updated replicas are available + Pre-rollout check acceptance-test passed + Advance podinfo.test canary weight 5 + Advance podinfo.test canary weight 10 + Advance podinfo.test canary weight 15 + Advance podinfo.test canary weight 20 + Advance podinfo.test canary weight 25 + Waiting for podinfo.test rollout to finish: 1 of 2 updated replicas are available + Advance podinfo.test canary weight 30 + Advance podinfo.test canary weight 35 + Advance podinfo.test canary weight 40 + Advance podinfo.test canary weight 45 + Advance podinfo.test canary weight 50 + Copying podinfo.test template spec to podinfo-primary.test + Waiting for podinfo-primary.test rollout to finish: 1 of 2 updated replicas are available + Promotion completed! Scaling down podinfo.test +``` + +**Note** that if you apply new changes to the deployment during the canary analysis, Flagger will restart the analysis. + +A canary deployment is triggered by changes in any of the following objects: +* Deployment PodSpec (container image, command, ports, env, resources, etc) +* ConfigMaps mounted as volumes or mapped to environment variables +* Secrets mounted as volumes or mapped to environment variables + +You can monitor all canaries with: + +```bash +watch kubectl get canaries --all-namespaces + +NAMESPACE NAME STATUS WEIGHT LASTTRANSITIONTIME +test podinfo Progressing 15 2019-06-30T14:05:07Z +prod frontend Succeeded 0 2019-06-30T16:15:07Z +prod backend Failed 0 2019-06-30T17:05:07Z +``` + +### Automated rollback + +During the canary analysis you can generate HTTP 500 errors and high latency to test if Flagger pauses and rolls back the faulted version. + +Trigger another canary deployment: + +```bash +kubectl -n test set image deployment/podinfo \ +podinfod=quay.io/stefanprodan/podinfo:1.4.2 +``` + +Exec into the load tester pod with: + +```bash +kubectl -n test exec -it flagger-loadtester-xx-xx sh +``` + +Generate HTTP 500 errors: + +```bash +watch -n 1 curl http://podinfo-canary.test:9898/status/500 +``` + +Generate latency: + +```bash +watch -n 1 curl http://podinfo-canary.test:9898/delay/1 +``` + +When the number of failed checks reaches the canary analysis threshold, the traffic is routed back to the primary, +the canary is scaled to zero and the rollout is marked as failed. + +```text +kubectl -n test describe canary/podinfo + +Status: + Canary Weight: 0 + Failed Checks: 10 + Phase: Failed +Events: + Starting canary analysis for podinfo.test + Pre-rollout check acceptance-test passed + Advance podinfo.test canary weight 5 + Advance podinfo.test canary weight 10 + Advance podinfo.test canary weight 15 + Halt podinfo.test advancement success rate 69.17% < 99% + Halt podinfo.test advancement success rate 61.39% < 99% + Halt podinfo.test advancement success rate 55.06% < 99% + Halt podinfo.test advancement request duration 1.20s > 0.5s + Halt podinfo.test advancement request duration 1.45s > 0.5s + Rolling back podinfo.test failed checks threshold reached 5 + Canary failed! Scaling down podinfo.test +``` + +### Custom metrics + +The canary analysis can be extended with Prometheus queries. + +Let's a define a check for not found errors. Edit the canary analysis and add the following metric: + +```yaml + canaryAnalysis: + metrics: + - name: "404s percentage" + threshold: 3 + query: | + 100 - sum( + rate( + response_total{ + namespace="test", + deployment="podinfo", + status_code!="404", + direction="inbound" + }[1m] + ) + ) + / + sum( + rate( + response_total{ + namespace="test", + deployment="podinfo", + direction="inbound" + }[1m] + ) + ) + * 100 +``` + +The above configuration validates the canary version by checking if the HTTP 404 req/sec percentage is below +three percent of the total traffic. If the 404s rate reaches the 3% threshold, then the analysis is aborted and the +canary is marked as failed. + +Trigger a canary deployment by updating the container image: + +```bash +kubectl -n test set image deployment/podinfo \ +podinfod=quay.io/stefanprodan/podinfo:1.4.3 +``` + +Generate 404s: + +```bash +watch -n 1 curl http://podinfo-canary:9898/status/404 +``` + +Watch Flagger logs: + +``` +kubectl -n linkerd logs deployment/flagger -f | jq .msg + +Starting canary deployment for podinfo.test +Pre-rollout check acceptance-test passed +Advance podinfo.test canary weight 5 +Halt podinfo.test advancement 404s percentage 6.20 > 3 +Halt podinfo.test advancement 404s percentage 6.45 > 3 +Halt podinfo.test advancement 404s percentage 7.22 > 3 +Halt podinfo.test advancement 404s percentage 6.50 > 3 +Halt podinfo.test advancement 404s percentage 6.34 > 3 +Rolling back podinfo.test failed checks threshold reached 5 +Canary failed! Scaling down podinfo.test +``` + +If you have Slack configured, Flagger will send a notification with the reason why the canary failed. From 92c971c0d7f000a088941d562623d4d70a2da4fc Mon Sep 17 00:00:00 2001 From: stefanprodan Date: Mon, 1 Jul 2019 10:02:50 +0300 Subject: [PATCH 12/12] Add ingress and A/B testing example to Linkerd docs --- docs/diagrams/flagger-nginx-linkerd.png | Bin 0 -> 39992 bytes .../usage/linkerd-progressive-delivery.md | 145 ++++++++++++++++++ 2 files changed, 145 insertions(+) create mode 100644 docs/diagrams/flagger-nginx-linkerd.png diff --git a/docs/diagrams/flagger-nginx-linkerd.png b/docs/diagrams/flagger-nginx-linkerd.png new file mode 100644 index 0000000000000000000000000000000000000000..55e7bfa855300d3456848a40e35a8dd01ce17935 GIT binary patch literal 39992 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