├── .gitignore ├── Makefile ├── README.md ├── istio-cross-namespace-canary-release.png ├── istio.yaml ├── nginx ├── .dockerignore ├── Dockerfile ├── charts │ ├── .helmignore │ ├── Chart.yaml │ ├── templates │ │ ├── _helpers.tpl │ │ ├── deployment.yaml │ │ └── service.yaml │ └── values.yaml └── default.conf ├── services ├── .helmignore ├── Chart.yaml ├── requirements.lock ├── requirements.yaml └── values.yaml ├── tiller-rbac.yaml └── traffic-manager ├── .helmignore ├── Chart.yaml ├── templates ├── _helpers.tpl ├── gateway.yaml └── virtualservice.yaml └── values.yaml /.gitignore: -------------------------------------------------------------------------------- 1 | *.tgz 2 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | OS = $(shell uname -s | tr A-Z a-z) 2 | 3 | ifeq ($(OS),darwin) 4 | OS = osx 5 | endif 6 | 7 | # Istio 8 | # Ref: https://github.com/istio/istio 9 | # Daily releases: https://gcsweb.istio.io/gcs/istio-prerelease/daily-build/ 10 | ISTIO_VERSION = release-0.8-20180511-16-55 11 | ISTIO_DAILY_RELEASE = true 12 | 13 | .PHONY: all 14 | all: buildnginx \ 15 | deployistio \ 16 | preparenamespaces \ 17 | deployservices \ 18 | releasestable 19 | 20 | .PHONY: deployistio 21 | deployistio: 22 | rm -rf istio-$(ISTIO_VERSION) istio-$(ISTIO_VERSION)-$(OS).tar.gz 23 | if [ "$(ISTIO_DAILY_RELEASE)" == "true" ]; then \ 24 | wget https://storage.googleapis.com/istio-prerelease/daily-build/$(ISTIO_VERSION)/istio-$(ISTIO_VERSION)-$(OS).tar.gz; \ 25 | else \ 26 | wget https://github.com/istio/istio/releases/download/$(ISTIO_VERSION)/istio-$(ISTIO_VERSION)-$(OS).tar.gz; \ 27 | fi 28 | tar -xvf istio-$(ISTIO_VERSION)-$(OS).tar.gz 29 | helm upgrade -i istio \ 30 | --namespace=istio-system \ 31 | --values ./istio.yaml \ 32 | ./istio-$(ISTIO_VERSION)/install/kubernetes/helm/istio 33 | 34 | .PHONY: preparenamespaces 35 | preparenamespaces: 36 | kubectl create ns services-v1 || true 37 | kubectl create ns services-v2 || true 38 | kubectl label ns services-v1 services-v2 istio-injection=enabled --overwrite 39 | 40 | .PHONY: updateservicesdep 41 | updateservicesdep: 42 | cd services && helm dep update --skip-refresh 43 | 44 | .PHONY: deployservices 45 | deployservices: updateservicesdep 46 | helm upgrade -i services-v1 \ 47 | --namespace=services-v1 \ 48 | ./services 49 | helm upgrade -i services-v2 \ 50 | --namespace=services-v2 \ 51 | ./services 52 | 53 | .PHONY: releasestable 54 | releasestable: 55 | helm upgrade -i traffic-manager \ 56 | ./traffic-manager 57 | 58 | .PHONY: releasecanary 59 | releasecanary: 60 | helm upgrade -i traffic-manager \ 61 | --set canary.enabled=true \ 62 | ./traffic-manager 63 | 64 | .PHONY: releasecanarymatchheader 65 | releasecanarymatchheader: 66 | helm upgrade -i traffic-manager \ 67 | --set canary.enabled=true \ 68 | --set canary.onlyMatchHeader=true \ 69 | ./traffic-manager 70 | 71 | .PHONY: buildnginx 72 | buildnginx: 73 | eval $$(minikube docker-env); \ 74 | docker build -t nginx:alpine-curl nginx 75 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Cross-namespace canary release using Kubernetes, Istio and Helm 2 | =============================================================== 3 | 4 | > Demo of a cross-namespace canary release using Istio service mesh with 5 | mutual TLS enabled and Helm charts. It makes use of the new 6 | [Istio v1alpha3 routing API](https://preliminary.istio.io/blog/2018/v1alpha3-routing.html). 7 | 8 | ![cross-namespace-canary-release](istio-cross-namespace-canary-release.png) 9 | 10 | ## Overview 11 | 12 | ``` 13 | . 14 | ├── Makefile 15 | ├── istio.yaml // Istio chart configuration 16 | ├── nginx // nginx is used as an example of microservice 17 | │   ├── Dockerfile 18 | │   ├── charts 19 | │   │   ├── templates 20 | │   │   │   ├── _helpers.tpl 21 | │   │   │   ├── deployment.yaml 22 | │   │   │   └── service.yaml 23 | │   │   ├── Chart.yaml 24 | │   │   └── values.yaml 25 | │   └── default.conf 26 | ├── services // all microservices should be defined in this directory as subchart 27 | │   ├── Chart.yaml 28 | │   ├── requirements.yaml 29 | │   └── values.yaml 30 | └── traffic-manager // chart with all the Istio configuration. 31 | ├── templates 32 | │   ├── _helpers.tpl 33 | │   ├── gateway.yaml // ingress configuration 34 | │   └── virtualservice.yaml // routing 35 | ├── Chart.yaml 36 | └── values.yaml 37 | ``` 38 | 39 | ## Getting started 40 | 41 | ### Requirements 42 | 43 | - [Docker](http://docker.io/) 44 | - [Minikube](https://github.com/kubernetes/minikube) 45 | - [Helm](https://helm.sh) 46 | 47 | ### Play yourself 48 | 49 | ``` 50 | # start Minikube with necessary flags to get started with Istio mTLS and automatic sidecar injection 51 | $ minikube start --kubernetes-version=v1.10.0 --memory 8192 --cpus 2 52 | 53 | # install Tiller 54 | $ kubectl apply -f ./tiller-rbac.yaml 55 | $ helm init --service-account tiller --upgrade --wait 56 | 57 | # install Istio with mTLS 58 | $ make deployistio 59 | 60 | # update Helm subcharts 61 | $ cd services && helm dep update --skip-refresh && cd ../ 62 | 63 | # build nginx container which contains curl 64 | # if using Istio with mTLS, we need to use curl to run the probe 65 | # ref: https://github.com/istio/istio/issues/1194 66 | $ make buildnginx 67 | 68 | # 69 | # Deploy services v1 70 | # 71 | 72 | # create namespace and enable automatic sidecar injection 73 | $ kubectl create ns services-v1 74 | $ kubectl label ns services-v1 istio-injection=enabled 75 | 76 | $ helm upgrade -i services-v1 --namespace=services-v1 ./services 77 | 78 | # deploy Istio routing via traffic manager 79 | $ helm upgrade -i traffic-manager ./traffic-manager 80 | 81 | 82 | # To test if the release was successful: 83 | $ ingress_gateway=$(minikube service istio-ingressgateway -n istio-system --url | head -n1) 84 | $ curl $ingress_gateway -H 'Host: search.local' 85 | Host: search-77f9697d44-l8dtc 86 | 87 | 88 | # 89 | # Deploy services v2 90 | # 91 | 92 | $ kubectl create ns services-v2 93 | $ kubectl label ns services-v2 istio-injection=enabled 94 | $ helm upgrade -i services-v2 --namespace=services-v2 ./services 95 | 96 | 97 | # 98 | # Canary 99 | # 100 | 101 | # shift 10% of the traffic to services-v2 102 | $ helm upgrade traffic-manager --set canary.enabled=true ./traffic-manager 103 | 104 | $ ingress_gateway=$(minikube service istio-ingressgateway -n istio-system --url | head -n1) 105 | $ while sleep 0.1; do curl $ingress_gateway -v -H 'Host: search.local'; done 106 | Host: search-77f9697d44-l8dtc 107 | Host: search-77f9697d44-l8dtc 108 | Host: search-77f9697d44-l8dtc 109 | Host: search-77f9697d44-l8dtc 110 | Host: search-77f9697d44-l8dtc 111 | Host: search-77f9697d44-l8dtc 112 | Host: search-6b8cc5d6f7-pzwcb 113 | Host: search-77f9697d44-l8dtc 114 | Host: search-77f9697d44-l8dtc 115 | 116 | 117 | # 118 | # Full rollout 119 | # 120 | 121 | # when we are ready, shift 100% 122 | $ helm upgrade traffic-manager \ 123 | --reset-values \ 124 | --set stable.serviceName=search.services-v2.svc.cluster.local \ 125 | ./traffic-manager 126 | 127 | 128 | # 129 | # Rollback 130 | # 131 | 132 | # shift 100% traffic back to services-v1 133 | $ helm upgrade traffic-manager \ 134 | --reset-values \ 135 | ./traffic-manager 136 | 137 | 138 | # 139 | # Cleanup 140 | # 141 | $ helm del --purge services-v1 142 | ``` 143 | 144 | 145 | ### Access release based on Headers 146 | 147 | If you want to run end-to-end tests against a new release in a "production" 148 | environment, you can configure Istio to match traffic based on HTTP Headers. 149 | 150 | You can enable it in the traffic-manager values.yaml file. Then redeploy: 151 | 152 | ``` 153 | $ helm upgrade -i traffic-manager \ 154 | --set canary.enabled=true \ 155 | ./traffic-manager 156 | $ ingress_gateway=$(minikube service istio-ingressgateway -n istio-system --url | head -n1) 157 | $ curl $ingress_gateway -v -H 'Host: proxy.local' -H 'X-Track: canary' 158 | ``` 159 | -------------------------------------------------------------------------------- /istio-cross-namespace-canary-release.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/etiennetremel/istio-cross-namespace-canary-release-demo/f07036e7d6a92dbebdd026d37206642a5ef340e5/istio-cross-namespace-canary-release.png -------------------------------------------------------------------------------- /istio.yaml: -------------------------------------------------------------------------------- 1 | global: 2 | hub: gcr.io/istio-release 3 | 4 | # controlPlaneMtls enabled. Will result in delays starting the pods while secrets are 5 | # propagated, not recommended for tests. 6 | controlPlaneSecurityEnabled: true 7 | 8 | mtls: 9 | # Default setting for service-to-service mtls. Can be set explicitly using 10 | # destination rules or service annotations. 11 | enabled: true 12 | 13 | rbacEnabled: true 14 | 15 | refreshInterval: 1s 16 | 17 | ## Sidecar Injector 18 | ## 19 | sidecar-injector: 20 | enabled: true 21 | 22 | ## Ingress 23 | ## Disable default ingress controller since we use the ingress gateway 24 | ingress: 25 | enabled: false 26 | -------------------------------------------------------------------------------- /nginx/.dockerignore: -------------------------------------------------------------------------------- 1 | charts 2 | -------------------------------------------------------------------------------- /nginx/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM nginx:alpine 2 | 3 | # if using Istio with mTLS, we need to use curl to run the probe 4 | # ref: https://github.com/istio/istio/issues/1194 5 | RUN apk --no-cache add curl 6 | 7 | COPY default.conf /etc/nginx/conf.d/default.conf 8 | -------------------------------------------------------------------------------- /nginx/charts/.helmignore: -------------------------------------------------------------------------------- 1 | # Patterns to ignore when building packages. 2 | # This supports shell glob matching, relative path matching, and 3 | # negation (prefixed with !). Only one pattern per line. 4 | .DS_Store 5 | # Common VCS dirs 6 | .git/ 7 | .gitignore 8 | .bzr/ 9 | .bzrignore 10 | .hg/ 11 | .hgignore 12 | .svn/ 13 | # Common backup files 14 | *.swp 15 | *.bak 16 | *.tmp 17 | *~ 18 | # Various IDEs 19 | .project 20 | .idea/ 21 | *.tmproj 22 | -------------------------------------------------------------------------------- /nginx/charts/Chart.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | appVersion: "1.0" 3 | description: A Helm chart for Kubernetes 4 | name: nginx 5 | version: 1.0.0 6 | -------------------------------------------------------------------------------- /nginx/charts/templates/_helpers.tpl: -------------------------------------------------------------------------------- 1 | {{/* vim: set filetype=mustache: */}} 2 | {{/* 3 | Expand the name of the chart. 4 | */}} 5 | {{- define "app.name" -}} 6 | {{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}} 7 | {{- end -}} 8 | 9 | {{/* 10 | Create chart name and version as used by the chart label. 11 | */}} 12 | {{- define "app.chart" -}} 13 | {{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}} 14 | {{- end -}} 15 | -------------------------------------------------------------------------------- /nginx/charts/templates/deployment.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1beta2 2 | kind: Deployment 3 | metadata: 4 | name: {{ template "app.name" . }} 5 | labels: 6 | app: {{ template "app.name" . }} 7 | chart: {{ template "app.chart" . }} 8 | release: {{ .Release.Name }} 9 | heritage: {{ .Release.Service }} 10 | spec: 11 | replicas: {{ .Values.replicaCount }} 12 | selector: 13 | matchLabels: 14 | app: {{ template "app.name" . }} 15 | release: {{ .Release.Name }} 16 | template: 17 | metadata: 18 | labels: 19 | app: {{ template "app.name" . }} 20 | release: {{ .Release.Name }} 21 | spec: 22 | containers: 23 | - name: {{ .Chart.Name }} 24 | image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}" 25 | imagePullPolicy: {{ .Values.image.pullPolicy }} 26 | ports: 27 | - containerPort: 80 28 | livenessProbe: 29 | exec: 30 | command: 31 | - curl 32 | - http://localhost/-/liveness 33 | readinessProbe: 34 | exec: 35 | command: 36 | - curl 37 | - http://localhost/-/readiness 38 | resources: 39 | {{ toYaml .Values.resources | indent 12 }} 40 | {{- with .Values.nodeSelector }} 41 | nodeSelector: 42 | {{ toYaml . | indent 8 }} 43 | {{- end }} 44 | {{- with .Values.affinity }} 45 | affinity: 46 | {{ toYaml . | indent 8 }} 47 | {{- end }} 48 | {{- with .Values.tolerations }} 49 | tolerations: 50 | {{ toYaml . | indent 8 }} 51 | {{- end }} 52 | -------------------------------------------------------------------------------- /nginx/charts/templates/service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: {{ template "app.name" . }} 5 | labels: 6 | app: {{ template "app.name" . }} 7 | chart: {{ template "app.chart" . }} 8 | release: {{ .Release.Name }} 9 | heritage: {{ .Release.Service }} 10 | spec: 11 | ports: 12 | - port: {{ .Values.service.port }} 13 | targetPort: 80 14 | protocol: TCP 15 | name: http 16 | selector: 17 | app: {{ template "app.name" . }} 18 | -------------------------------------------------------------------------------- /nginx/charts/values.yaml: -------------------------------------------------------------------------------- 1 | # Default values for payment. 2 | # This is a YAML-formatted file. 3 | # Declare variables to be passed into your templates. 4 | 5 | replicaCount: 1 6 | 7 | image: 8 | repository: nginx 9 | tag: alpine-curl 10 | pullPolicy: IfNotPresent 11 | 12 | service: 13 | port: 80 14 | 15 | resources: {} 16 | # We usually recommend not to specify default resources and to leave this as a conscious 17 | # choice for the user. This also increases chances charts run on environments with little 18 | # resources, such as Minikube. If you do want to specify resources, uncomment the following 19 | # lines, adjust them as necessary, and remove the curly braces after 'resources:'. 20 | # limits: 21 | # cpu: 100m 22 | # memory: 128Mi 23 | # requests: 24 | # cpu: 100m 25 | # memory: 128Mi 26 | 27 | nodeSelector: {} 28 | 29 | tolerations: [] 30 | 31 | affinity: {} 32 | -------------------------------------------------------------------------------- /nginx/default.conf: -------------------------------------------------------------------------------- 1 | server { 2 | listen 80 default; 3 | server_name _; 4 | 5 | root /usr/share/nginx/html; 6 | index index.html index.htm; 7 | 8 | location = /-/liveness { 9 | access_log off; 10 | add_header Content-Type text/plain; 11 | return 200; 12 | } 13 | 14 | location = /-/readiness { 15 | access_log off; 16 | add_header Content-Type text/plain; 17 | return 200; 18 | } 19 | 20 | location / { 21 | return 200 "Host: $hostname\n"; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /services/.helmignore: -------------------------------------------------------------------------------- 1 | # Patterns to ignore when building packages. 2 | # This supports shell glob matching, relative path matching, and 3 | # negation (prefixed with !). Only one pattern per line. 4 | .DS_Store 5 | # Common VCS dirs 6 | .git/ 7 | .gitignore 8 | .bzr/ 9 | .bzrignore 10 | .hg/ 11 | .hgignore 12 | .svn/ 13 | # Common backup files 14 | *.swp 15 | *.bak 16 | *.tmp 17 | *~ 18 | # Various IDEs 19 | .project 20 | .idea/ 21 | *.tmproj 22 | -------------------------------------------------------------------------------- /services/Chart.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | appVersion: "1.0" 3 | description: A Helm chart for Kubernetes 4 | name: services 5 | version: 0.1.0 6 | -------------------------------------------------------------------------------- /services/requirements.lock: -------------------------------------------------------------------------------- 1 | dependencies: 2 | - name: nginx 3 | repository: file://../nginx/charts 4 | version: 1.0.0 5 | digest: sha256:19183b1f7ebde04d52e9914759e33532c1d212feed3dde2d534de412512b3afd 6 | generated: 2018-05-12T17:30:46.745225275+01:00 7 | -------------------------------------------------------------------------------- /services/requirements.yaml: -------------------------------------------------------------------------------- 1 | dependencies: 2 | - name: nginx 3 | version: 1.0.0 4 | repository: file://../nginx/charts 5 | alias: search 6 | # - name: nginx 7 | # version: 1.0.0 8 | # repository: file://../nginx/charts 9 | # alias: booking 10 | # - name: nginx 11 | # version: 1.0.0 12 | # repository: file://../nginx/charts 13 | # alias: payment 14 | -------------------------------------------------------------------------------- /services/values.yaml: -------------------------------------------------------------------------------- 1 | search: 2 | replicaCount: 1 3 | 4 | ## Not used, but we could assume we have a set of applications 5 | # booking: 6 | # replicaCount: 1 7 | 8 | # payment: 9 | # replicaCount: 1 10 | -------------------------------------------------------------------------------- /tiller-rbac.yaml: -------------------------------------------------------------------------------- 1 | # Create a service account for Helm and grant the cluster admin role. 2 | # It is assumed that helm should be installed with this service account 3 | # (tiller). 4 | apiVersion: v1 5 | kind: ServiceAccount 6 | metadata: 7 | name: tiller 8 | namespace: kube-system 9 | --- 10 | apiVersion: rbac.authorization.k8s.io/v1beta1 11 | kind: ClusterRoleBinding 12 | metadata: 13 | name: tiller 14 | roleRef: 15 | apiGroup: rbac.authorization.k8s.io 16 | kind: ClusterRole 17 | name: cluster-admin 18 | subjects: 19 | - kind: ServiceAccount 20 | name: tiller 21 | namespace: kube-system 22 | -------------------------------------------------------------------------------- /traffic-manager/.helmignore: -------------------------------------------------------------------------------- 1 | # Patterns to ignore when building packages. 2 | # This supports shell glob matching, relative path matching, and 3 | # negation (prefixed with !). Only one pattern per line. 4 | .DS_Store 5 | # Common VCS dirs 6 | .git/ 7 | .gitignore 8 | .bzr/ 9 | .bzrignore 10 | .hg/ 11 | .hgignore 12 | .svn/ 13 | # Common backup files 14 | *.swp 15 | *.bak 16 | *.tmp 17 | *~ 18 | # Various IDEs 19 | .project 20 | .idea/ 21 | *.tmproj 22 | -------------------------------------------------------------------------------- /traffic-manager/Chart.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | appVersion: "1.0" 3 | description: A Helm chart for Kubernetes 4 | name: traffic-manager 5 | version: 0.1.0 6 | -------------------------------------------------------------------------------- /traffic-manager/templates/_helpers.tpl: -------------------------------------------------------------------------------- 1 | {{/* vim: set filetype=mustache: */}} 2 | {{/* 3 | Expand the name of the chart. 4 | */}} 5 | {{- define "traffic-manager.name" -}} 6 | {{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}} 7 | {{- end -}} 8 | 9 | {{/* 10 | Create a default fully qualified app name. 11 | We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). 12 | If release name contains chart name it will be used as a full name. 13 | */}} 14 | {{- define "traffic-manager.fullname" -}} 15 | {{- if .Values.fullnameOverride -}} 16 | {{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}} 17 | {{- else -}} 18 | {{- $name := default .Chart.Name .Values.nameOverride -}} 19 | {{- if contains $name .Release.Name -}} 20 | {{- .Release.Name | trunc 63 | trimSuffix "-" -}} 21 | {{- else -}} 22 | {{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}} 23 | {{- end -}} 24 | {{- end -}} 25 | {{- end -}} 26 | 27 | {{/* 28 | Create chart name and version as used by the chart label. 29 | */}} 30 | {{- define "traffic-manager.chart" -}} 31 | {{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}} 32 | {{- end -}} 33 | -------------------------------------------------------------------------------- /traffic-manager/templates/gateway.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: networking.istio.io/v1alpha3 2 | kind: Gateway 3 | metadata: 4 | name: {{ template "traffic-manager.fullname" . }} 5 | labels: 6 | app: {{ template "traffic-manager.name" . }} 7 | chart: {{ template "traffic-manager.chart" . }} 8 | release: {{ .Release.Name }} 9 | heritage: {{ .Release.Service }} 10 | spec: 11 | servers: 12 | - port: 13 | number: 80 14 | name: http 15 | protocol: HTTP 16 | hosts: 17 | - {{ .Values.host }} 18 | -------------------------------------------------------------------------------- /traffic-manager/templates/virtualservice.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: networking.istio.io/v1alpha3 2 | kind: VirtualService 3 | metadata: 4 | name: {{ template "traffic-manager.fullname" . }} 5 | labels: 6 | app: {{ template "traffic-manager.name" . }} 7 | chart: {{ template "traffic-manager.chart" . }} 8 | release: {{ .Release.Name }} 9 | heritage: {{ .Release.Service }} 10 | spec: 11 | hosts: 12 | - {{ .Values.host }} 13 | gateways: 14 | - {{ template "traffic-manager.fullname" . }} 15 | http: 16 | - route: 17 | {{- if .Values.canary.enabled }} 18 | {{- if gt (len .Values.canary.match) 0 }} 19 | - destination: 20 | host: {{ .Values.canary.serviceName }} 21 | match: 22 | {{ toYaml .Values.canary.match | indent 8 }} 23 | 24 | - route: 25 | {{- else }} 26 | - destination: 27 | host: {{ .Values.canary.serviceName }} 28 | weight: {{ .Values.canary.weight }} 29 | {{- end }} 30 | {{- end }} 31 | - destination: 32 | host: {{ .Values.stable.serviceName }} 33 | {{- if and .Values.canary.enabled (eq (len .Values.canary.match) 0) }} 34 | weight: {{ .Values.stable.weight }} 35 | {{- end }} 36 | -------------------------------------------------------------------------------- /traffic-manager/values.yaml: -------------------------------------------------------------------------------- 1 | ## Entrypoint domain 2 | host: search.local 3 | 4 | ## Canary 5 | canary: 6 | enabled: false 7 | weight: 10 8 | serviceName: search.services-v2.svc.cluster.local 9 | 10 | ## If we only want users to access the canary release based on headers 11 | match: {} 12 | ## Only users with cookie 'access=v2' can access the canary release 13 | # - headers: 14 | # cookie: 15 | # regex: "^(.*?;)?(access=v2)(;.*)?" 16 | 17 | ## Only users with header 'X-Track: canary' can access the canary release 18 | # - headers: 19 | # x-track: 20 | # exact: canary 21 | 22 | ## Stable 23 | stable: 24 | serviceName: search.services-v1.svc.cluster.local 25 | weight: 90 26 | --------------------------------------------------------------------------------