Technical Documentation: fluxcd/flagger
ℹ️ Provenance: Multi-Tier Ingestion · CodeWiki Reference
Overview (`README.md`)
Flagger
Flagger is a progressive delivery tool that automates the release process for applications running on Kubernetes.
It reduces the risk of introducing a new software version in production
by gradually shifting traffic to the new version while measuring metrics and running conformance tests.
Flagger implements several deployment strategies (Canary releases, A/B testing, Blue/Green mirroring)
and integrates with various Kubernetes ingress controllers, service mesh, and monitoring solutions.
Flagger is a Cloud Native Computing Foundation graduated project
and part of the Flux family of GitOps tools.
Documentation
The Flagger documentation can be found at docs.flagger.app.
- Install
- Usage
Adopters
The list of production users can be found at fluxcd.io/adopters/#flagger.
If you are using Flagger, please
submit a PR to add your organization to the list!
Canary CRD
Flagger takes a Kubernetes deployment and optionally a horizontal pod autoscaler (HPA),
then creates a series of objects (Kubernetes deployments, ClusterIP services, service mesh, or ingress routes).
These objects expose the application on the mesh and drive the canary analysis and promotion.
Flagger keeps track of ConfigMaps and Secrets referenced by a Kubernetes Deployment and triggers a canary analysis if any of those objects change.
When promoting a workload in production, both code (container images) and configuration (config maps and secrets) are being synchronized.
For a deployment named podinfo, a canary promotion can be defined using Flagger's custom resource:
/* Detailed source-code truncated for AI context efficiency. */For more details on how the canary analysis and promotion works please read the docs.
Features
Service Mesh
| Feature | Istio | Linkerd | Kuma | Knative | Kubernetes CNI |
|---|---|---|---|---|---|
| Canary deployments (weighted traffic) | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_minus_sign: |
| A/B testing (headers and cookies routing) | :heavy_check_mark: | :heavy_minus_sign: | :heavy_minus_sign: | :heavy_minus_sign: | :heavy_minus_sign: |
| Blue/Green deployments (traffic switch) | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_minus_sign: | :heavy_check_mark: |
| Blue/Green deployments (traffic mirroring) | :heavy_check_mark: | :heavy_minus_sign: | :heavy_minus_sign: | :heavy_minus_sign: | :heavy_minus_sign: |
| Webhooks (acceptance/load testing) | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: |
| Manual gating (approve/pause/resume) | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: |
| Request success rate check (L7 metric) | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_minus_sign: | :heavy_check_mark: |
| Request duration check (L7 metric) | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_minus_sign: | :heavy_check_mark: |
| Custom metric checks | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: |
Ingress
| Feature | Contour | Gloo | NGINX | Skipper | Traefik | Apache APISIX |
|---|---|---|---|---|---|---|
| Canary deployments (weighted traffic) | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: |
| A/B testing (headers and cookies routing) | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_minus_sign: | :heavy_minus_sign: | :heavy_minus_sign: |
| Blue/Green deployments (traffic switch) | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: |
| Webhooks (acceptance/load testing) | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: |
| Manual gating (approve/pause/resume) | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: |
| Request success rate check (L7 metric) | :heavy_check_mark: | :heavy_check_mark: | :heavy_minus_sign: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: |
| Request duration check (L7 metric) | :heavy_check_mark: | :heavy_check_mark: | :heavy_minus_sign: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: |
| Custom metric checks | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: |
Networking Interface
| Feature | Gateway API | SMI |
|---|---|---|
| Canary deployments (weighted traffic) | :heavy_check_mark: | :heavy_check_mark: |
| Canary deployments with session affinity | :heavy_check_mark: | :heavy_minus_sign: |
| A/B testing (headers and cookies routing) | :heavy_check_mark: | :heavy_minus_sign: |
| Blue/Green deployments (traffic switch) | :heavy_check_mark: | :heavy_check_mark: |
| Blue/Green deployments (traffic mirroring) | :heavy_minus_sign: | :heavy_minus_sign: |
| Webhooks (acceptance/load testing) | :heavy_check_mark: | :heavy_check_mark: |
| Manual gating (approve/pause/resume) | :heavy_check_mark: | :heavy_check_mark: |
| Request success rate check (L7 metric) | :heavy_minus_sign: | :heavy_minus_sign: |
| Request duration check (L7 metric) | :heavy_minus_sign: | :heavy_minus_sign: |
| Custom metric checks | :heavy_check_mark: | :heavy_check_mark: |
For all the Gateway API compatible ingress controllers and service meshes,
the Prometheus MetricTemplates
can be used to implement the request success rate and request duration checks.
Roadmap
[GitOps Toolkit](https://fluxcd.io/flux/components/) compatibility
- Migrate Flagger to Kubernetes controller-runtime and kubebuilder
- Make the Canary status compatible with kstatus
- Make Flagger emit Kubernetes events compatible with Flux v2 notification API
- Integrate Flagger into Flux v2 as the progressive delivery component
Integrations
- Migrate Linkerd, Kuma and other service mesh integrations to Gateway API
Contributing
Flagger is Apache 2.0 licensed and accepts contributions via GitHub pull requests.
To start contributing please read the development guide.
When submitting bug reports please include as many details as possible:
- which Flagger version
- which Kubernetes version
- what configuration (canary, ingress and workloads definitions)
- what happened (Flagger and Proxy logs)
Communication
Here is a list of good entry points into our community, how we stay in touch and how you can meet us as a team.
- Slack: Join in and talk to us in the
#flaggerchannel on CNCF Slack. - Public meetings: We run weekly meetings - join one of the upcoming dev meetings from the Flux calendar.
- Blog: Stay up to date with the latest news on the Flux blog.
- Mailing list: To be updated on Flux and Flagger progress regularly, please join the flux-dev mailing list.
Subscribing to the flux-dev calendar
To add the meetings to your e.g. Google calendar
- visit the Flux calendar
- click on "Subscribe to Calendar" at the very bottom of the page
- copy the iCalendar URL
- open e.g. your Google calendar
- find the "add calendar" option
- choose "add by URL"
- paste iCalendar URL (ends with
.ics) - done
Overview (`kustomize/README.md`)
Flagger Kustomize installer
As an alternative to Helm, Flagger can be installed with Kustomize.
Prerequisites
- Kubernetes cluster >=1.13.0
- Kustomize >=3.6.0
Service mesh specific installers
Install Flagger for Istio:
kustomize build https://github.com/fluxcd/flagger/kustomize/istio?ref=main | kubectl apply -f -This deploys Flagger in the istio-system namespace and sets the metrics server URL to Istio's Prometheus instance.
Install Flagger for AWS App Mesh:
kustomize build https://github.com/fluxcd/flagger/kustomize/appmesh?ref=main | kubectl apply -f -This deploys Flagger in the appmesh-system namespace and sets the metrics server URL to App Mesh Prometheus instance.
Install Flagger for Linkerd:
kustomize build https://github.com/fluxcd/flagger/kustomize/linkerd?ref=main | kubectl apply -f -This deploys Flagger in the linkerd namespace and sets the metrics server URL to linkerd-viz extension's Prometheus instance
which lives under linkerd-viz namespace by default.
If you want to install a specific Flagger release, add the version number to the URL:
kustomize build https://github.com/fluxcd/flagger/kustomize/linkerd?ref=v1.0.0 | kubectl apply -f -Install Flagger for Contour:
kustomize build https://github.com/fluxcd/flagger/kustomize/contour?ref=main | kubectl apply -f -This deploys Flagger and Prometheus in the projectcontour namespace and sets Prometheus to scrape Contour's Envoy instances.
Generic installer
Install Flagger and Prometheus:
kustomize build https://github.com/fluxcd/flagger/kustomize/kubernetes?ref=main | kubectl apply -f -This deploys Flagger and Prometheus in the flagger-system namespace,
sets the metrics server URL to http://flagger-prometheus.flagger-system:9090 and the mesh provider to kubernetes.
To target a different provider you can specify it in the canary custom resource:
apiVersion: flagger.app/v1alpha3
kind: Canary
metadata:
name: app
namespace: test
spec:
# can be: kubernetes, istio, linkerd, appmesh, nginx, skipper, gloo
# use the kubernetes provider for Blue/Green style deployments
provider: nginxYou'll need Prometheus when using Flagger with AWS App Mesh, Gloo, NGINX or Skipper ingress controller.
The Prometheus instance has a two hours data retention and is configured to scrape all pods in your cluster that
have the prometheus.io/scrape: "true" annotation.
Customise the installation
Create a kustomization file using Flagger as base and patch the container args:
cat > kustomization.yaml <<EOF
namespace: istio-system
bases:
- github.com/fluxcd/flagger/kustomize/base/flagger
patches:
- target:
kind: Deployment
name: flagger
patch: |-
apiVersion: apps/v1
kind: Deployment
metadata:
name: flagger
spec:
template:
spec:
containers:
- name: flagger
args:
- -mesh-provider=istio
- -metrics-server=http://prometheus.istio-system:9090
- -slack-user=flagger
- -slack-channel=alerts
- -slack-url=https://hooks.slack.com/services/YOUR/SLACK/WEBHOOK
EOFInstall Flagger for Istio with Slack notifications:
kustomize build . | kubectl apply -f -Overview (`test/README.md`)
Flagger end-to-end testing
The e2e testing infrastructure is powered by GitHub Actions and Kubernetes Kind.
e2e workflow
- create local Kubernetes cluster with KinD
- build Flagger container and load the image in KinD
- install the service mesh or ingress provider
- deploy Flagger
- create test namespace, workloads and load tester
- test the canary initialization (port discovery and metadata)
- test the canary release (progressive traffic shifting, headers routing, mirroring, analysis, promotion, rollback)
- test webhooks (conformance, load testing, pre/post rollout)
Overview (`charts/flagger/README.md`)
Flagger
Flagger is a progressive delivery tool that automates the release process
for applications running on Kubernetes. It reduces the risk of introducing a new software version in production
by gradually shifting traffic to the new version while measuring metrics and running conformance tests.
Flagger implements several deployment strategies (Canary releases, A/B testing, Blue/Green mirroring)
and integrates with various Kubernetes ingress controllers, service mesh and monitoring solutions.
Flagger is a Cloud Native Computing Foundation project
and part of Flux family of GitOps tools.
Prerequisites
- Kubernetes >= 1.19
Installing the Chart
Add Flagger Helm repository:
$ helm repo add flagger https://flagger.appInstall Flagger's custom resource definitions:
$ kubectl apply -f https://raw.githubusercontent.com/fluxcd/flagger/main/artifacts/flagger/crd.yamlTo install Flagger for Istio:
$ helm upgrade -i flagger flagger/flagger \
--namespace=istio-system \
--set meshProvider=istio \
--set metricsServer=http://prometheus:9090To install Flagger for Linkerd (requires Linkerd Viz extension):
# Note that linkerdAuthPolicy.create=true is only required for Linkerd 2.12 and
# later
$ helm upgrade -i flagger flagger/flagger \
--namespace=flagger-system \
--set meshProvider=linkerd \
--set metricsServer=http://prometheus.linkerd-viz:9090 \
--set linkerdAuthPolicy.create=trueTo install Flagger for AWS App Mesh:
$ helm upgrade -i flagger flagger/flagger \
--namespace=appmesh-system \
--set meshProvider=appmesh:v1beta2 \
--set metricsServer=http://appmesh-prometheus:9090To install Flagger for Kuma Service Mesh (requires Kuma to have been installed with Prometheus):
$ helm upgrade -i flagger flagger/flagger \
--namespace=kuma-system \
--set meshProvider=kuma \
--set metricsServer=http://prometheus-server.kuma-metrics:80To install Flagger and Prometheus for NGINX Ingress (requires controller metrics enabled):
$ helm upgrade -i flagger flagger/flagger \
--namespace=ingress-nginx \
--set meshProvider=nginx \
--set prometheus.install=trueTo install Flagger and Prometheus for Gloo (no longer requires Gloo discovery):
$ helm upgrade -i flagger flagger/flagger \
--namespace=gloo-system \
--set meshProvider=gloo \
--set prometheus.install=trueTo install Flagger and Prometheus for Contour:
$ helm upgrade -i flagger flagger/flagger \
--namespace=projectcontour \
--set meshProvider=contour \
--set ingressClass=contour \
--set prometheus.install=trueTo install Flagger and Prometheus for Traefik:
$ helm upgrade -i flagger flagger/flagger \
--namespace=traefik \
--set prometheus.install=true \
--set meshProvider=traefikIf you need to add labels to the flagger deployment or pods, you can pass the labels as parameters as shown below.
helm upgrade -i flagger flagger/flagger \
<other parameters> \
--set podLabels.<labelName>=<labelValue> \
--set deploymentLabels.<labelName>=<labelValue>The configuration section lists the parameters that can be configured during installation.
Uninstalling the Chart
To uninstall/delete the flagger deployment:
$ helm delete flaggerThe command removes all the Kubernetes components associated with the chart and deletes the release.
Configuration
The following tables lists the configurable parameters of the Flagger chart and their default values.
| Parameter | Description | Default |
|---|---|---|
image.repository |
Image repository | ghcr.io/fluxcd/flagger |
image.tag |
Image tag | <VERSION> |
image.pullPolicy |
Image pull policy | IfNotPresent |
logLevel |
Log level | info |
metricsServer |
Prometheus URL, used when prometheus.install is false |
http://prometheus.istio-system:9090 |
prometheus.install |
If true, installs Prometheus configured to scrape all pods in the custer |
false |
prometheus.retention |
Prometheus data retention | 2h |
selectorLabels |
List of labels that Flagger uses to create pod selectors | app,name,app.kubernetes.io/name |
serviceMonitor.enabled |
If true, creates service and serviceMonitor for monitoring Flagger metrics |
false |
serviceMonitor.honorLabels |
If true, label conflicts are resolved by keeping label values from the scraped data and ignoring the conflicting server-side labels |
false |
serviceMonitor.namespace |
Namespace Servicemonitor is installed in | the same namespace |
serviceMonitor.labels |
labels for the ServiceMonitor passed to Prometheus Operator | {} |
configTracking.enabled |
If true, flagger will track changes in Secrets and ConfigMaps referenced in the target deployment |
true |
eventWebhook |
If set, Flagger will publish events to the given webhook | None |
slack.url |
Slack incoming webhook | None |
slack.proxyUrl |
Slack proxy url | None |
slack.channel |
Slack channel | None |
slack.user |
Slack username | flagger |
msteams.url |
Microsoft Teams incoming webhook | None |
msteams.proxyUrl |
Microsoft Teams proxy url | None |
clusterName |
When specified, Flagger will add the cluster name to alerts | "" |
podMonitor.enabled |
If true, create a PodMonitor for monitoring the metrics |
false |
podMonitor.namespace |
Namespace where the PodMonitor is created | the same namespace |
podMonitor.interval |
Interval at which metrics should be scraped | 15s |
podMonitor.podMonitor |
Additional labels to add to the PodMonitor | {} |
podMonitor.honorLabels |
If true, label conflicts are resolved by keeping label values from the scraped data and ignoring the conflicting server-side labels |
false |
leaderElection.enabled |
If true, Flagger will run in HA mode |
false |
leaderElection.replicaCount |
Number of replicas | 1 |
serviceAccount.create |
If true, Flagger will create service account |
true |
serviceAccount.name |
The name of the service account to create or use. If not set and serviceAccount.create is true, a name is generated using the Flagger fullname |
"" |
serviceAccount.annotations |
Annotations for service account | {} |
ingressAnnotationsPrefix |
Annotations prefix for ingresses | custom.ingress.kubernetes.io |
includeLabelPrefix |
List of prefixes of labels that are copied when creating primary deployments or daemonsets. Use * to include all | "" |
rbac.create |
If true, create and use RBAC resources |
true |
rbac.pspEnabled |
If true, create and use a restricted pod security policy |
false |
crd.create |
If true, create Flagger's CRDs (should be enabled for Helm v2 only) |
false |
resources.requests/cpu |
Pod CPU request | 10m |
resources.requests/memory |
Pod memory request | 32Mi |
resources.limits/cpu |
Pod CPU limit | 1000m |
resources.limits/memory |
Pod memory limit | 512Mi |
affinity |
Node/pod affinities | prefer spread across hosts |
nodeSelector |
Node labels for pod assignment | {} |
threadiness |
Number of controller workers | 2 |
tolerations |
List of node taints to tolerate | [] |
controlplane.kubeconfig.secretName |
The name of the Kubernetes secret containing the service mesh control plane kubeconfig | None |
controlplane.kubeconfig.key |
The name of Kubernetes secret data key that contains the service mesh control plane kubeconfig | kubeconfig |
ingressAnnotationsPrefix |
Annotations prefix for NGINX ingresses | None |
ingressClass |
Ingress class used for annotating HTTPProxy objects, e.g. contour |
None |
podPriorityClassName |
PriorityClass name for pod priority configuration | "" |
podDisruptionBudget.enabled |
A PodDisruptionBudget will be created if true |
false |
podDisruptionBudget.minAvailable |
The minimal number of available replicas that will be set in the PodDisruptionBudget | 1 |
podDisruptionBudget.minAvailable |
The minimal number of available replicas that will be set in the PodDisruptionBudget | 1 |
noCrossNamespaceRefs |
If true, cross namespace references to custom resources will be disabled |
false |
namespace |
When specified, Flagger will restrict itself to watching Canary objects from that namespace | "" |
additionalVolumes |
Extra volumes to add to the Flagger pod | [] |
additionalVolumeMounts |
Extra volume mounts to add to the Flagger container | [] |
deploymentLabels |
Labels to add to Flagger deployment | {} |
podLabels |
Labels to add to pods of Flagger deployment | {} |
Specify each parameter using the --set key=value[,key=value] argument to helm upgrade. For example,
$ helm upgrade -i flagger flagger/flagger \
--namespace flagger-system \
--set slack.url=https://hooks.slack.com/services/YOUR/SLACK/WEBHOOK \
--set slack.channel=generalAlternatively, a YAML file that specifies the values for the above parameters can be provided while installing the chart. For example,
$ helm upgrade -i flagger flagger/flagger \
--namespace istio-system \
-f values.yamlTip: You can use the default values.yaml
Overview (`charts/grafana/README.md`)
Flagger Grafana
Grafana dashboards for monitoring progressive deployments powered by Flagger and Prometheus.
Prerequisites
- Kubernetes >= 1.11
- Prometheus >= 2.6
Installing the Chart
Add Flagger Helm repository:
helm repo add flagger https://flagger.appTo install the chart for Istio run:
helm upgrade -i flagger-grafana flagger/grafana \
--namespace=istio-system \
--set url=http://prometheus:9090To install the chart for AWS App Mesh run:
helm upgrade -i flagger-grafana flagger/grafana \
--namespace=appmesh-system \
--set url=http://appmesh-prometheus:9090The command deploys Grafana on the Kubernetes cluster in the default namespace.
The configuration section lists the parameters that can be configured during installation.
Uninstalling the Chart
To uninstall/delete the flagger-grafana deployment:
helm delete --purge flagger-grafanaThe command removes all the Kubernetes components associated with the chart and deletes the release.
Configuration
The following tables lists the configurable parameters of the Grafana chart and their default values.
| Parameter | Description | Default |
|---|---|---|
image.repository |
Image repository | grafana/grafana |
image.pullPolicy |
Image pull policy | IfNotPresent |
image.tag |
Image tag | <VERSION> |
replicaCount |
desired number of pods | 1 |
resources |
pod resources | none |
tolerations |
List of node taints to tolerate | [] |
affinity |
node/pod affinities | node |
nodeSelector |
node labels for pod assignment | {} |
service.type |
type of service | ClusterIP |
url |
Prometheus URL | http://prometheus:9090 |
Specify each parameter using the --set key=value[,key=value] argument to helm install. For example,
helm install flagger/grafana --name flagger-grafana \
--set token=WEAVE-CLOUD-TOKENAlternatively, a YAML file that specifies the values for the above parameters can be provided while installing the chart. For example,
helm install flagger/grafana --name flagger-grafana -f values.yamlTip: You can use the default values.yaml
Overview (`charts/loadtester/README.md`)
Flagger load testing service
Flagger's load testing service is based on
rakyll/hey and
bojand/ghz.
It can be used to generate HTTP and gRPC traffic during canary analysis when configured as a webhook.
Prerequisites
- Kubernetes >= 1.19
Installing the Chart
Add Flagger Helm repository:
helm repo add flagger https://flagger.appTo install the chart with the release name flagger-loadtester:
helm upgrade -i flagger-loadtester flagger/loadtesterThe command deploys loadtester on the Kubernetes cluster in the default namespace.
Tip: Note that the namespace where you deploy the load tester should
have the Istio, App Mesh, Linkerd or Open Service Mesh sidecar injection enabled
The configuration section lists the parameters that can be configured during installation.
Uninstalling the Chart
To uninstall/delete the flagger-loadtester deployment:
helm delete flagger-loadtesterThe command removes all the Kubernetes components associated with the chart and deletes the release.
Configuration
The following tables lists the configurable parameters of the load tester chart and their default values.
| Parameter | Description | Default |
|---|---|---|
image.repository |
Image repository | ghcr.io/fluxcd/flagger-loadtester |
image.pullPolicy |
Image pull policy | IfNotPresent |
image.tag |
Image tag | <VERSION> |
replicaCount |
Desired number of pods | 1 |
serviceAccountName |
Kubernetes service account name | none |
resources.requests.cpu |
CPU requests | 10m |
resources.requests.memory |
Memory requests | 64Mi |
tolerations |
List of node taints to tolerate | [] |
affinity |
node/pod affinities | node |
nodeSelector |
Node labels for pod assignment | {} |
service.type |
Type of service | ClusterIP |
service.port |
ClusterIP port | 80 |
cmd.timeout |
Command execution timeout | 1h |
cmd.namespaceRegexp |
Restrict access to canaries in matching namespaces | "" |
logLevel |
Log level can be debug, info, warning, error or panic | info |
appmesh.enabled |
Create AWS App Mesh v1beta2 virtual node | false |
appmesh.backends |
AWS App Mesh virtual services | none |
istio.enabled |
Create Istio virtual service | false |
istio.host |
Loadtester hostname | flagger-loadtester.flagger |
istio.gateway.enabled |
Create Istio gateway in namespace | false |
istio.tls.enabled |
Enable TLS in gateway ( TLS secrets should be in namespace ) | false |
istio.tls.httpsRedirect |
Redirect traffic to TLS port | false |
podPriorityClassName |
PriorityClass name for pod priority configuration | "" |
securityContext.enabled |
Add securityContext to container | false |
SecurityContext.context |
securityContext to add | "" |
podSecurityContext.enabled |
Add securityContext to pod | false |
podSecurityContext.context |
securityContext to add | "" |
podDisruptionBudget.enabled |
A PodDisruptionBudget will be created if true |
false |
podDisruptionBudget.minAvailable |
The minimal number of available replicas that will be set in the PodDisruptionBudget | 1 |
Specify each parameter using the --set key=value[,key=value] argument to helm upgrade. For example,
helm upgrade -i flagger-loadtester flagger/loadtester \
--set "appmesh.enabled=true" \
--set "appmesh.backends[0]=podinfo" \
--set "appmesh.backends[1]=podinfo-canary"Alternatively, a YAML file that specifies the values for the above parameters can be provided while installing the chart. For example,
helm install flagger/loadtester --name flagger-loadtester -f values.yamlTip: You can use the default values.yaml
Overview (`charts/podinfo/README.md`)
Podinfo
Podinfo is a tiny web application made with Go
that showcases best practices of running canary deployments with Flagger and Istio.
Installing the Chart
Add Flagger Helm repository:
helm repo add flagger https://flagger.appTo install the chart with the release name frontend:
helm upgrade -i frontend flagger/podinfo \
--namespace test \
--set nameOverride=frontend \
--set backend=http://backend.test:9898/echo \
--set canary.enabled=true \
--set canary.istioIngress.enabled=true \
--set canary.istioIngress.gateway=istio-system/public-gateway \
--set canary.istioIngress.host=frontend.istio.example.comTo install the chart as backend:
helm upgrade -i backend flagger/podinfo \
--namespace test \
--set nameOverride=backend \
--set canary.enabled=trueUninstalling the Chart
To uninstall/delete the frontend deployment:
$ helm delete --purge frontendThe command removes all the Kubernetes components associated with the chart and deletes the release.
Configuration
The following tables lists the configurable parameters of the podinfo chart and their default values.
| Parameter | Description | Default |
|---|---|---|
image.repository |
image repository | quay.io/stefanprodan/podinfo |
image.tag |
image tag | <VERSION> |
image.pullPolicy |
image pull policy | IfNotPresent |
hpa.enabled |
enables HPA | true |
hpa.cpu |
target CPU usage per pod | 80 |
hpa.memory |
target memory usage per pod | 512Mi |
hpa.minReplicas |
maximum pod replicas | 2 |
hpa.maxReplicas |
maximum pod replicas | 4 |
resources.requests/cpu |
pod CPU request | 1m |
resources.requests/memory |
pod memory request | 16Mi |
backend |
backend URL | None |
faults.delay |
random HTTP response delays between 0 and 5 seconds | false |
faults.error |
1/3 chances of a random HTTP response error | false |
Specify each parameter using the --set key=value[,key=value] argument to helm install. For example,
$ helm install flagger/podinfo --name frontend \
--set=image.tag=1.4.1,hpa.enabled=falseAlternatively, a YAML file that specifies the values for the above parameters can be provided while installing the chart. For example,
$ helm install flagger/podinfo --name frontend -f values.yamlOverview (`docs/gitbook/README.md`)
Introduction
Flagger is a progressive delivery tool that automates the release
process for applications running on Kubernetes. It reduces the risk of introducing a new software
version in production by gradually shifting traffic to the new version while measuring metrics
and running conformance tests.
Flagger implements several deployment strategies (Canary releases, A/B testing, Blue/Green mirroring)
using a service mesh or an ingress controller for traffic routing.
For release analysis, Flagger can query Prometheus, InfluxDB, Datadog, New Relic, CloudWatch, Stackdriver
or Graphite and for alerting it uses Slack, MS Teams, Discord and Rocket.
Flagger can be configured with Kubernetes custom resources and is compatible with
any CI/CD solutions made for Kubernetes. Since Flagger is declarative and reacts to Kubernetes events,
it can be used in GitOps pipelines together with tools like Flux CD.
Flagger is a Cloud Native Computing Foundation graduated project
and part of Flux family of GitOps tools.
Getting started
To get started with Flagger, choose one of the supported routing providers and
install Flagger with Flux CD.
After installing Flagger, you can follow one of these tutorials to get started:
Service mesh tutorials
Ingress controller tutorials
The Linux Foundation has registered trademarks and uses trademarks. For a list of trademarks of The Linux Foundation,
please see our Trademark Usage page.
Gitbook SUMMARY (`docs/gitbook/SUMMARY.md`)
Table of contents
Install
Usage
Tutorials
- Gateway API Canary Deployments
- Istio Canary Deployments
- Istio A/B Testing
- Linkerd Canary Deployments
- Kuma Canary Deployments
- Knative Canary Deployments
- Contour Canary Deployments
- Gloo Canary Deployments
- NGINX Canary Deployments
- Skipper Canary Deployments
- Traefik Canary Deployments
- Apache APISIX Canary Deployments
- Blue/Green Deployments
- Canary analysis with Prometheus Operator
- Canary analysis with KEDA ScaledObjects
- Zero downtime deployments
Dev
Gitbook Faq (`docs/gitbook/faq.md`)
FAQ
Deployment Strategies
Which deployment strategies are supported by Flagger?
Flagger implements the following deployment strategies:
When should I use A/B testing instead of progressive traffic shifting?
For frontend applications that require session affinity, you should use HTTP headers or
cookie match conditions to ensure a set of users will stay on the same version for
the whole duration of the canary analysis.
Can I use Flagger to manage applications that live outside of a service mesh?
For applications that are not deployed on a service mesh,
Flagger can orchestrate Blue/Green style deployments with Kubernetes L4 networking.
When can I use traffic mirroring?
Traffic mirroring can be used for Blue/Green deployment strategy or a pre-stage in a Canary release.
Traffic mirroring will copy each incoming request, sending one request to the primary and one to the canary service.
Mirroring should be used for requests that are idempotent
or capable of being processed twice (once by the primary and once by the canary).
How to retry a failed release?
A canary analysis is triggered by changes in any of the following objects:
- Deployment/DaemonSet PodSpec (metadata, 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
To retry a release you can add or change an annotation on the pod template:
apiVersion: apps/v1
kind: Deployment
spec:
template:
metadata:
annotations:
timestamp: "2020-03-10T14:24:48+0000"How to change replicas for a deployment when not using HPA?
To change replicas for a deployment when not using HPA, you have to update the canary deployment with the desired replica count
and trigger an analysis by annotating the template. After the analysis finishes, Flagger will promote the spec.replicas changes to the primary deployment.
Example:
apiVersion: apps/v1
kind: Deployment
spec:
replicas: 4 #update replicas
template:
metadata:
annotations:
timestamp: "2022-02-10T14:24:48+0000" #add annotation to trigger analysisWhy is there a window of downtime during the canary initializing process when analysis is disabled?
A window of downtime is the intended behavior when the analysis is disabled. This allows instant rollback and also mimics the way
a Kubernetes deployment initialization works. To avoid this, enable the analysis (skipAnalysis: false), wait for the initialization
to finish, and disable it afterward (skipAnalysis: true).
How to disable cross namespace references?
Flagger by default can access resources across namespaces (AlertProivder, MetricProvider and Gloo Upsteream).
If you're in a multi-tenant environment and wish to disable this, you can do so through the no-cross-namespace-refs flag.
flagger \
-no-cross-namespace-refs=true \
...Kubernetes services
How is an application exposed inside the cluster?
Assuming the app name is podinfo, you can define a canary like:
apiVersion: flagger.app/v1beta1
kind: Canary
metadata:
name: podinfo
namespace: test
spec:
targetRef:
apiVersion: apps/v1
kind: Deployment
name: podinfo
service:
# service name (optional)
name: podinfo
# ClusterIP port number (required)
port: 9898
# container port name or number
targetPort: http
# port name can be http or grpc (default http)
portName: httpIf the service.name is not specified, then targetRef.name is used for
the apex domain and canary/primary services name prefix.
You should treat the service name as an immutable field; changing its could result in routing conflicts.
Based on the canary spec service, Flagger generates the following Kubernetes ClusterIP service:
<service.name>.<namespace>.svc.cluster.localselector
app=<name>-primary<service.name>-primary.<namespace>.svc.cluster.localselector
app=<name>-primary<service.name>-canary.<namespace>.svc.cluster.localselector
app=<name>
This ensures that traffic coming from a namespace outside the mesh to podinfo.test:9898
will be routed to the latest stable release of your app.
apiVersion: v1
kind: Service
metadata:
name: podinfo
spec:
type: ClusterIP
selector:
app: podinfo-primary
ports:
- name: http
port: 9898
protocol: TCP
targetPort: http
---
apiVersion: v1
kind: Service
metadata:
name: podinfo-primary
spec:
type: ClusterIP
selector:
app: podinfo-primary
ports:
- name: http
port: 9898
protocol: TCP
targetPort: http
---
apiVersion: v1
kind: Service
metadata:
name: podinfo-canary
spec:
type: ClusterIP
selector:
app: podinfo
ports:
- name: http
port: 9898
protocol: TCP
targetPort: httpThe podinfo-canary.test:9898 address is available only during the canary analysis
and can be used for conformance testing or load testing.
Multiple ports
My application listens on multiple ports. How can I expose them inside the cluster?
If port discovery is enabled, Flagger scans the deployment spec and extracts the containers ports excluding
the port specified in the canary service and Envoy sidecar ports.
These ports will be used when generating the ClusterIP services.
For a deployment that exposes two ports:
apiVersion: apps/v1
kind: Deployment
spec:
template:
metadata:
annotations:
prometheus.io/scrape: "true"
prometheus.io/port: "9899"
spec:
containers:
- name: app
ports:
- containerPort: 8080
- containerPort: 9090You can enable port discovery so that Prometheus will be able to reach port 9090 over mTLS:
apiVersion: flagger.app/v1beta1
kind: Canary
spec:
service:
# container port used for canary analysis
port: 8080
# port name can be http or grpc (default http)
portName: http
# add all the other container ports
# to the ClusterIP services (default false)
portDiscovery: true
trafficPolicy:
tls:
mode: ISTIO_MUTUALBoth port 8080 and 9090 will be added to the ClusterIP services.
Label selectors
What labels selectors are supported by Flagger?
The target deployment must have a single label selector in the format app: <DEPLOYMENT-NAME>:
apiVersion: apps/v1
kind: Deployment
metadata:
name: podinfo
spec:
selector:
matchLabels:
app: podinfo
template:
metadata:
labels:
app: podinfoBesides app, Flagger supports name and app.kubernetes.io/name selectors.
If you use a different convention, you can specify your label with the -selector-labels flag.
For example:
flagger \
-selector-labels=service,name,app.kubernetes.io/name \
...Is pod affinity and anti affinity supported?
Flagger will rewrite the first value in each match expression,
defined in the target deployment's pod anti-affinity and topology spread constraints,
satisfying the following two requirements when creating, or updating, the primary deployment:
- The key in the match expression must be one of the labels specified by the parameter selector-labels.
The default labels areapp,name,app.kubernetes.io/name. - The value must match the name of the target deployment.
The rewrite done by Flagger in these cases is to suffix the value with -primary.
This rewrite can be used to spread the pods created by the canary
and primary deployments across different availability zones.
Example target deployment:
apiVersion: apps/v1
kind: Deployment
metadata:
name: podinfo
spec:
selector:
matchLabels:
app: podinfo
template:
metadata:
labels:
app: podinfo
spec:
affinity:
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
labelSelector:
matchExpressions:
- key: app
operator: In
values:
- podinfo
topologyKey: topology.kubernetes.io/zoneExample of generated primary deployment:
apiVersion: apps/v1
kind: Deployment
metadata:
name: podinfo-primary
spec:
selector:
matchLabels:
app: podinfo-primary
template:
metadata:
labels:
app: podinfo-primary
spec:
affinity:
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
labelSelector:
matchExpressions:
- key: app
operator: In
values:
- podinfo-primary
topologyKey: topology.kubernetes.io/zoneIt is also possible to use a different label than the app, name or app.kubernetes.io/name.
Anti affinity example(using a different label):
apiVersion: apps/v1
kind: Deployment
metadata:
name: podinfo
spec:
selector:
matchLabels:
app: podinfo
affinity: podinfo
template:
metadata:
labels:
app: podinfo
affinity: podinfo
spec:
affinity:
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
labelSelector:
matchLabels:
affinity: podinfo
topologyKey: topology.kubernetes.io/zoneMetrics
How does Flagger measure the request success rate and duration?
By default, Flagger measures the request success rate and duration using Prometheus queries.
HTTP requests success rate percentage
Spec:
analysis:
metrics:
- name: request-success-rate
# minimum req success rate (non 5xx responses)
# percentage (0-100)
thresholdRange:
min: 99
interval: 1mIstio query:
sum(
rate(
istio_requests_total{
reporter="destination",
destination_workload_namespace=~"{{ namespace }}",
destination_workload=~"{{ target }}",
response_code!~"5.*"
}[{{ interval }}]
)
)
/
sum(
rate(
istio_requests_total{
reporter="destination",
destination_workload_namespace=~"{{ namespace }}",
destination_workload=~"{{ target }}"
}[{{ interval }}]
)
)Envoy query (App Mesh):
sum(
rate(
envoy_cluster_upstream_rq{
kubernetes_namespace="{{ namespace }}",
kubernetes_pod_name=~"{{ target }}",
envoy_response_code!~"5.*"
}[{{ interval }}]
)
)
/
sum(
rate(
envoy_cluster_upstream_rq{
kubernetes_namespace="{{ namespace }}",
kubernetes_pod_name=~"{{ target }}"
}[{{ interval }}]
)
)Envoy query (Contour and Gloo):
sum(
rate(
envoy_cluster_upstream_rq{
envoy_cluster_name=~"{{ namespace }}-{{ target }}",
envoy_response_code!~"5.*"
}[{{ interval }}]
)
)
/
sum(
rate(
envoy_cluster_upstream_rq{
envoy_cluster_name=~"{{ namespace }}-{{ target }}",
}[{{ interval }}]
)
)HTTP requests milliseconds duration P99
Spec:
analysis:
metrics:
- name: request-duration
# maximum req duration P99
# milliseconds
thresholdRange:
max: 500
interval: 1mIstio query:
histogram_quantile(0.99,
sum(
irate(
istio_request_duration_milliseconds_bucket{
reporter="destination",
destination_workload=~"{{ target }}",
destination_workload_namespace=~"{{ namespace }}"
}[{{ interval }}]
)
) by (le)
)Envoy query (App Mesh, Contour and Gloo):
histogram_quantile(0.99,
sum(
irate(
envoy_cluster_upstream_rq_time_bucket{
kubernetes_pod_name=~"{{ target }}",
kubernetes_namespace=~"{{ namespace }}"
}[{{ interval }}]
)
) by (le)
)Note that the metric interval should be lower or equal to the control loop interval.
Can I use custom metrics?
The analysis can be extended with metrics provided by Prometheus, Datadog, AWS CloudWatch, New Relic and Graphite.
For more details on how custom metrics can be used, please read the metrics docs.
Istio Gateway API
If you're using Istio with Gateway API, the Prometheus query needs to include reporter="source". For example, to calculate HTTP requests error percentage, the query would be:
100 - sum(
rate(
istio_requests_total{
reporter="source",
destination_workload_namespace=~"{{ namespace }}",
destination_workload=~"{{ target }}",
response_code!~"5.*"
}[{{ interval }}]
)
)
/
sum(
rate(
istio_requests_total{
reporter="source",
destination_workload_namespace=~"{{ namespace }}",
destination_workload=~"{{ target }}"
}[{{ interval }}]
)
) * 100Istio routing
How does Flagger interact with Istio?
Flagger creates an Istio Virtual Service and Destination Rules based on the Canary service spec.
The service configuration lets you expose an app inside or outside the mesh. You can also define traffic policies,
HTTP match conditions, URI rewrite rules, CORS policies, timeout and retries.
The following spec exposes the frontend workload inside the mesh on frontend.test.svc.cluster.local:9898
and outside the mesh on frontend.example.com. You'll have to specify an Istio ingress gateway for external hosts.
apiVersion: flagger.app/v1beta1
kind: Canary
metadata:
name: frontend
namespace: test
spec:
service:
# container port
port: 9898
# service port name (optional, will default to "http")
portName: http-frontend
# Istio gateways (optional)
gateways:
- istio-system/public-gateway
- mesh
# Istio virtual service host names (optional)
hosts:
- frontend.example.com
# Istio traffic policy
trafficPolicy:
tls:
# use ISTIO_MUTUAL when mTLS is enabled
mode: DISABLE
# HTTP match conditions (optional)
match:
- uri:
prefix: /
# HTTP rewrite (optional)
rewrite:
uri: /
# Istio retry policy (optional)
retries:
attempts: 3
perTryTimeout: 1s
retryOn: "gateway-error,connect-failure,refused-stream"
# Add headers (optional)
headers:
request:
add:
x-some-header: "value"
# cross-origin resource sharing policy (optional)
corsPolicy:
allowOrigin:
- example.com
allowMethods:
- GET
allowCredentials: false
allowHeaders:
- x-some-header
maxAge: 24hFor the above spec Flagger will generate the following virtual service:
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
name: frontend
namespace: test
ownerReferences:
- apiVersion: flagger.app/v1beta1
blockOwnerDeletion: true
controller: true
kind: Canary
name: podinfo
uid: 3a4a40dd-3875-11e9-8e1d-42010a9c0fd1
spec:
gateways:
- istio-system/public-gateway
- mesh
hosts:
- frontend.example.com
- frontend
http:
- corsPolicy:
allowHeaders:
- x-some-header
allowMethods:
- GET
allowOrigin:
- example.com
maxAge: 24h
headers:
request:
add:
x-some-header: "value"
match:
- uri:
prefix: /
rewrite:
uri: /
route:
- destination:
host: podinfo-primary
weight: 100
- destination:
host: podinfo-canary
weight: 0
retries:
attempts: 3
perTryTimeout: 1s
retryOn: "gateway-error,connect-failure,refused-stream"For each destination in the virtual service a rule is generated:
apiVersion: networking.istio.io/v1beta1
kind: DestinationRule
metadata:
name: frontend-primary
namespace: test
spec:
host: frontend-primary
trafficPolicy:
tls:
mode: DISABLE
---
apiVersion: networking.istio.io/v1beta1
kind: DestinationRule
metadata:
name: frontend-canary
namespace: test
spec:
host: frontend-canary
trafficPolicy:
tls:
mode: DISABLEFlagger keeps in sync the virtual service and destination rules with the canary service spec.
Any direct modification to the virtual service spec will be overwritten.
To expose a workload inside the mesh on http://backend.test.svc.cluster.local:9898,
the service spec can contain only the container port and the traffic policy:
apiVersion: flagger.app/v1beta1
kind: Canary
metadata:
name: backend
namespace: test
spec:
service:
port: 9898
trafficPolicy:
tls:
mode: DISABLEBased on the above spec, Flagger will create several ClusterIP services like:
apiVersion: v1
kind: Service
metadata:
name: backend-primary
ownerReferences:
- apiVersion: flagger.app/v1beta1
blockOwnerDeletion: true
controller: true
kind: Canary
name: backend
uid: 2ca1a9c7-2ef6-11e9-bd01-42010a9c0145
spec:
type: ClusterIP
ports:
- name: http
port: 9898
protocol: TCP
targetPort: 9898
selector:
app: backend-primaryFlagger works for user facing apps exposed outside the cluster via an ingress gateway and for backend HTTP APIs
that are accessible only from inside the mesh.
If Delegation is enabled, Flagger would generate Istio VirtualService without hosts and gateway,
making the service compatible with Istio delegation.
apiVersion: flagger.app/v1beta1
kind: Canary
metadata:
name: backend
namespace: test
spec:
service:
delegation: true
port: 9898
targetRef:
apiVersion: v1
kind: Deployment
name: podinfo
analysis:
interval: 15s
threshold: 15
maxWeight: 30
stepWeight: 10Based on the above spec, Flagger will create the following virtual service:
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
name: backend
namespace: test
ownerReferences:
- apiVersion: flagger.app/v1beta1
blockOwnerDeletion: true
controller: true
kind: Canary
name: backend
uid: 58562662-5e10-4512-b269-2b789c1b30fe
spec:
http:
- route:
- destination:
host: podinfo-primary
weight: 100
- destination:
host: podinfo-canary
weight: 0Therefore, the following virtual service forwards the traffic to /podinfo by the above delegate VirtualService.
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
name: frontend
namespace: test
spec:
gateways:
- istio-system/public-gateway
- mesh
hosts:
- frontend.example.com
- frontend
http:
- match:
- uri:
prefix: /podinfo
rewrite:
uri: /
delegate:
name: backend
namespace: testNote that pilot env PILOT_ENABLE_VIRTUAL_SERVICE_DELEGATE must also be set.
For the use of Istio Delegation, you can refer to the documentation of
Virtual Service
and pilot environment variables.
Istio Ingress Gateway
How can I expose multiple canaries on the same external domain?
Assuming you have two apps -- one that serves the main website and one that serves its REST API --
you can define a canary object for each app as:
apiVersion: flagger.app/v1beta1
kind: Canary
metadata:
name: website
spec:
service:
port: 8080
gateways:
- istio-system/public-gateway
hosts:
- my-site.com
match:
- uri:
prefix: /
rewrite:
uri: /
---
apiVersion: flagger.app/v1beta1
kind: Canary
metadata:
name: webapi
spec:
service:
port: 8080
gateways:
- istio-system/public-gateway
hosts:
- my-site.com
match:
- uri:
prefix: /api
rewrite:
uri: /Based on the above configuration, Flagger will create two virtual services bounded
to the same ingress gateway and external host.
Istio Pilot will
merge
the two services and the website rule will be moved to the end of the list in the merged configuration.
Note that host merging only works if the canaries are bounded to an ingress gateway other than the mesh gateway.
Istio Mutual TLS
How can I enable mTLS for a canary?
When deploying Istio with global mTLS enabled, you have to set the TLS mode to ISTIO_MUTUAL:
apiVersion: flagger.app/v1beta1
kind: Canary
spec:
service:
trafficPolicy:
tls:
mode: ISTIO_MUTUALIf you run Istio in permissive mode, you can disable TLS:
apiVersion: flagger.app/v1beta1
kind: Canary
spec:
service:
trafficPolicy:
tls:
mode: DISABLEIf Flagger is outside of the mesh, how can it start the load test?
In order for Flagger to be able to call the load tester service from outside the mesh,
you need to disable mTLS:
apiVersion: networking.istio.io/v1beta1
kind: DestinationRule
metadata:
name: flagger-loadtester
namespace: test
spec:
host: "flagger-loadtester.test.svc.cluster.local"
trafficPolicy:
tls:
mode: DISABLE
---
apiVersion: security.istio.io/v1beta1
kind: PeerAuthentication
metadata:
name: flagger-loadtester
namespace: test
spec:
selector:
matchLabels:
app: flagger-loadtester
mtls:
mode: DISABLEExternalDNS
Can I use annotations?
Flagger propagates annotations (and labels) to all the generated apex,
primary and canary objects. This allows using external-dns annotations.
You can configure Flagger to set annotations with:
spec:
service:
apex:
annotations:
external-dns.alpha.kubernetes.io/hostname: "mydomain.com"
primary:
annotations:
external-dns.alpha.kubernetes.io/hostname: "primary.mydomain.com"
canary:
annotations:
external-dns.alpha.kubernetes.io/hostname: "canary.mydomain.com"Multiple sources and Istio
/!\ The apex annotations are added to both the generated Kubernetes Services and the generated Istio
VirtualServices objects. If you have configured external-dns to use both sources,
this will create conflicts!
spec:
containers:
args:
- --source=service # choose only one
- --source=istio-virtualservice # of these twoCheckout ExternalDNS documentation
Gitbook Dev Dev Guide (`docs/gitbook/dev/dev-guide.md`)
Development Guide
This document describes how to build, test and run Flagger from source.
Setup dev environment
Flagger is written in Go and uses Go modules for dependency management.
On your dev machine install the following tools:
- go >= 1.26
- kubectl >= 1.30
- kustomize >= 5.0
- helm >= 3.0
You'll also need a Kubernetes cluster for testing Flagger.
You can use Minikube, Kind, Docker desktop or any remote cluster (AKS/EKS/GKE/etc).
To start contributing to Flagger, fork the repository on GitHub.
Create a dir inside your GOPATH:
mkdir -p $GOPATH/src/github.com/fluxcdClone your fork:
cd $GOPATH/src/github.com/fluxcd
git clone https://github.com/YOUR_USERNAME/flagger
cd flaggerSet Flagger repository as upstream:
git remote add upstream https://github.com/fluxcd/flagger.gitSync your fork regularly to keep it up-to-date with upstream:
git fetch upstream
git checkout main
git merge upstream/mainBuild
Download Go modules:
go mod downloadBuild Flagger binary:
make buildBuild load tester binary:
make loadtester-buildCode changes
We require all commits to be signed. By signing off with your signature, you
certify that you wrote the patch or otherwise have the right to contribute the
material by the rules of the DCO.
If your user.name and user.email are configured in your Git config,
you can sign your commit automatically with:
git commit -sBefore submitting a PR, make sure your changes are covered by unit tests.
If you made changes to go.mod run:
go mod tidyIf you made changes to pkg/apis regenerate Kubernetes client sets with:
make codegenRun code formatters:
go install golang.org/x/tools/cmd/goimports@latest
make fmtRun unit tests:
make testAPI changes
If you made changes to pkg/apis regenerate the Kubernetes client sets with:
make codegenUpdate the validation spec in artifacts/flagger/crd.yaml and run:
make crdNote that any change to the CRDs must be accompanied by an update to the Open API schema.
Manual testing
Install a service mesh and/or an ingress controller on your cluster
and deploy Flagger using one of the install options
listed here.
If you made changes to the CRDs, apply your local copy with:
kubectl apply -f artifacts/flagger/crd.yamlShutdown the Flagger instance installed on your cluster (replace the namespace with your mesh/ingress one):
kubectl -n istio-system scale deployment/flagger --replicas=0Port forward to your Prometheus instance:
kubectl -n istio-system port-forward svc/prometheus 9090:9090Run Flagger locally against your remote cluster by specifying a kubeconfig path:
go run cmd/flagger/ -kubeconfig=$HOME/.kube/config \
-log-level=info \
-mesh-provider=istio \
-metrics-server=http://localhost:9090Another option to manually test your changes is to build and push the image to your container registry:
make build
docker build -t <YOUR-DOCKERHUB-USERNAME>/flagger:<YOUR-TAG> .
docker push <YOUR-DOCKERHUB-USERNAME>/flagger:<YOUR-TAG>Deploy your image on the cluster and scale up Flagger:
kubectl -n istio-system set image deployment/flagger flagger=<YOUR-DOCKERHUB-USERNAME>/flagger:<YOUR-TAG>
kubectl -n istio-system scale deployment/flagger --replicas=1Now you can use one of the tutorials to manually test your changes.
Integration testing
Flagger end-to-end tests can be run locally with Kubernetes Kind.
Create a Kind cluster:
kind create clusterBuild Flagger container image and load it on the cluster:
make build
docker build -t test/flagger:latest .
kind load docker-image test/flagger:latestRun the Istio e2e tests:
./test/istio/run.shFor each service mesh and ingress controller, there is a dedicated e2e test suite,
choose one that matches your changes from this list.
When you open a pull request on Flagger repo, the unit and integration tests will be run in CI.
Gitbook Dev Release Guide (`docs/gitbook/dev/release-guide.md`)
Release Guide
This document describes how to release Flagger.
Release
Flagger
To release a new Flagger version (e.g. 2.0.0) follow these steps:
- create a branch
git checkout -b release-2.0.0 - set the version in code and manifests
TAG=2.0.0 make version-set - commit changes and merge PR
- checkout main
git checkout main && git pull - tag main
make release
Flagger load tester
To release a new Flagger load tester version (e.g. 2.0.0) follow these steps:
- create a branch
git checkout -b release-ld-2.0.0 - set the version in code (
cmd/loadtester/main.go#VERSION) - set the version in the Helm chart (
charts/loadtester/Chart.yamlandvalues.yaml) - set the version in manifests (
kustomize/tester/deployment.yaml) - commit changes and push the branch upstream
- in GitHub UI, navigate to Actions and run the
push-ldworkflow selecting the release branch - after the workflow finishes, open the PR which will run the e2e tests using the new tester version
- merge the PR if the tests pass
CI
After the tag has been pushed to GitHub, the CI release pipeline does the following:
- creates a GitHub release
- pushes the Flagger binary and change log to GitHub release
- pushes the Flagger container image to GitHub Container Registry
- pushed the Flagger install manifests to GitHub Container Registry
- signs all OCI artifacts and release assets with Cosign and GitHub OIDC
- pushes the Helm chart to github-pages branch
- GitHub pages publishes the new chart version on the Helm repository
Docs
The documentation website is built from the docs branch.
After a Flagger release, publish the docs with:
git checkout main && git pullgit checkout docsgit rebase maingit push origin docs
Lastly open a PR with all the docs changes on fluxcd/website to
update fluxcd.io/flagger.
Gitbook Dev Upgrade Guide (`docs/gitbook/dev/upgrade-guide.md`)
Upgrade Guide
This document describes how to upgrade Flagger.
Upgrade canaries v1alpha3 to v1beta1
Canary CRD changes in canaries.flagger.app/v1beta1:
- the
spec.canaryAnalysisfield has been deprecated and replaced withspec.analysis - the
spec.analysis.intervalandspec.analysis.thresholdfields are required - the
status.lastAppliedSpecandstatus.lastPromotedSpechashing algorithm changed tohash/fnv - the
spec.analysis.alertsarray can referencealertproviders.flagger.app/v1beta1resources - the
spec.analysis.metrics[].templateRefcan reference ametrictemplate.flagger.app/v1beta1resource - the
metric.thresholdfield has been deprecated and replaced withmetric.thresholdRange - the
metric.queryfield has been deprecated and replaced withmetric.templateRef - the
spec.ingressRef.apiVersionacceptsnetworking.k8s.io/v1beta1 - the
spec.targetRefcan referenceDaemonSetkind - the
spec.service.meshNamefield has been deprecated and no longer used forprovider: appmesh:v1beta2
Upgrade procedure:
- install the
v1beta1CRDs - update Flagger deployment
- replace
apiVersion: flagger.app/v1alpha3withapiVersion: flagger.app/v1beta1in all canary manifests - replace
spec.canaryAnalysiswithspec.analysisin all canary manifests - update canary manifests in cluster
Note that after upgrading Flagger, all canaries will be triggered as the hash value used for tracking changes is computed differently. You can set spec.skipAnalysis: true in all canary manifests before upgrading Flagger, do the upgrade, wait for Flagger to finish the no-op promotions and finally set skipAnalysis to false.
Update builtin metrics:
- replace
thresholdwiththresholdRange.minfor request-success-rate - replace
thresholdwiththresholdRange.maxfor request-duration
metrics:
- name: request-success-rate
thresholdRange:
min: 99
interval: 1m
- name: request-duration
thresholdRange:
max: 500
interval: 1mIstio telemetry v2
Istio 1.5 comes with a breaking change for Flagger uses. In Istio telemetry v2 the metric istio_request_duration_seconds_bucket has been removed and replaced with istio_request_duration_milliseconds_bucket and this breaks the request-duration metric check.
If are using Istio 1.4, you can create a metric template using the old duration metric like this:
apiVersion: flagger.app/v1beta1
kind: MetricTemplate
metadata:
name: latency
namespace: istio-system
spec:
provider:
type: prometheus
address: http://prometheus.istio-system:9090
query: |
histogram_quantile(
0.99,
sum(
rate(
istio_request_duration_seconds_bucket{
reporter="destination",
destination_workload_namespace="{{ namespace }}",
destination_workload=~"{{ target }}"
}[{{ interval }}]
)
) by (le)
)In the canary manifests, replace the request-duration metric with latency:
metrics:
- name: latency
templateRef:
name: latency
namespace: istio-system
thresholdRange:
max: 0.500
interval: 1mGitbook Install Flagger Install On Kubernetes (`docs/gitbook/install/flagger-install-on-kubernetes.md`)
Flagger Install on Kubernetes
This guide walks you through setting up Flagger on a Kubernetes cluster with Helm or Kubectl.
See the Flux install guide for installing Flagger and keeping it up to date the GitOps way.
Install Flagger with Helm
Add Flagger Helm repository:
helm repo add flagger https://flagger.appInstall Flagger's Canary CRD:
kubectl apply -f https://raw.githubusercontent.com/fluxcd/flagger/main/artifacts/flagger/crd.yamlDeploy Flagger for Istio:
helm upgrade -i flagger flagger/flagger \
--namespace=istio-system \
--set crd.create=false \
--set meshProvider=istio \
--set metricsServer=http://prometheus:9090Note that Flagger depends on Istio telemetry and Prometheus, if you're installing
Istio with istioctl then you should be using the
default profile.
For Istio multi-cluster shared control plane you can install Flagger on each remote cluster and set the
Istio control plane host cluster kubeconfig:
helm upgrade -i flagger flagger/flagger \
--namespace=istio-system \
--set crd.create=false \
--set meshProvider=istio \
--set metricsServer=http://istio-cluster-prometheus:9090 \
--set controlplane.kubeconfig.secretName=istio-kubeconfig \
--set controlplane.kubeconfig.key=kubeconfigNote that the Istio kubeconfig must be stored in a Kubernetes secret with a data key named kubeconfig.
For more details on how to configure Istio multi-cluster
credentials read the Istio docs.
Deploy Flagger for Linkerd:
helm upgrade -i flagger flagger/flagger \
--namespace=linkerd \
--set crd.create=false \
--set meshProvider=linkerd \
--set metricsServer=http://linkerd-prometheus:9090If you need to add labels to the flagger deployment or pods, you can pass the labels as parameters as shown below.
helm upgrade -i flagger flagger/flagger \
<other parameters> \
--set podLabels.<labelName>=<labelValue> \
--set deploymentLabels.<labelName>=<labelValue>You can install Flagger in any namespace as long as it can talk to the Prometheus service on port 9090.
For ingress controllers, the installation instructions are:
To uninstall the Flagger release with Helm run:
helm delete flaggerThe command removes all the Kubernetes components associated with the chart and deletes the release.
Note that on uninstall the Canary CRD will not be removed. Deleting the CRD will make Kubernetes
remove all the objects owned by Flagger like Istio virtual services, Kubernetes deployments and ClusterIP services.
If you want to remove all the objects created by Flagger you have to delete the Canary CRD with kubectl:
kubectl delete crd canaries.flagger.appInstall Grafana with Helm
Flagger comes with a Grafana dashboard made for monitoring the canary analysis.
Deploy Grafana in the istio-system namespace:
helm upgrade -i flagger-grafana flagger/grafana \
--namespace=istio-system \
--set url=http://prometheus.istio-system:9090 \
--set user=admin \
--set password=change-meYou can access Grafana using port forwarding:
kubectl -n istio-system port-forward svc/flagger-grafana 3000:80Install Flagger with Kubectl
Install Flagger and Prometheus using the Kustomize overlay from the GitHub repository:
kubectl apply -k https://github.com/fluxcd/flagger/kustomize/kubernetes?ref=mainThis deploys Flagger and Prometheus in the flagger-system namespace,
sets the metrics server URL to http://flagger-prometheus.flagger-system:9090 and the mesh provider to kubernetes.
The Prometheus instance has a two hours data retention and is configured to scrape all pods in your cluster
that have the prometheus.io/scrape: "true" annotation.
Customized installer
Create a kustomization file using Flagger as base and patch the container args:
cat > kustomization.yaml <<EOF
namespace: istio-system
bases:
- https://github.com/fluxcd/flagger/kustomize/kubernetes?ref=main
patches:
- target:
kind: Deployment
name: flagger
patch: |-
apiVersion: apps/v1
kind: Deployment
metadata:
name: flagger
spec:
template:
spec:
containers:
- name: flagger
args:
- -mesh-provider=istio
- -metrics-server=http://prometheus.istio-system:9090
- -include-label-prefix=app.kubernetes.io
EOFGitbook Install Flagger Install With Flux (`docs/gitbook/install/flagger-install-with-flux.md`)
Flagger Install on Kubernetes with Flux
This guide walks you through setting up Flagger on a Kubernetes cluster the GitOps way.
You'll configure Flux to scan the Flagger OCI artifacts and deploy the
latest stable version on Kubernetes.
Flagger OCI artifacts
Flagger OCI artifacts (container images, Helm charts, Kustomize overlays) are published to
GitHub Container Registry, and they are signed with Cosign
and GitHub Actions OIDC at every release.
OCI artifacts
ghcr.io/fluxcd/flagger:<version>multi-arch container imagesghcr.io/fluxcd/flagger-manifests:<version>Kubernetes manifestsghcr.io/fluxcd/charts/flagger:<version>Helm charts
Starting with Flagger 1.43, artifacts are signed with Cosign v3 using the
sigstore bundle format.
Verifying these signatures with Flux requires Flux 2.8 or later.
Prerequisites
To follow this guide you’ll need a Kubernetes cluster with Flux installed on it.
Please see the Flux get started guide
or the Flux installation guide.
To verify the Flagger OCI artifacts at reconciliation time, Flux 2.8 or later is
required. Earlier versions ship Cosign v2 and cannot verify the sigstore bundles
produced by the Flagger 1.43+ release workflow.
Deploy Flagger with Flux
First define the namespace where Flagger will be installed:
---
apiVersion: v1
kind: Namespace
metadata:
name: flagger-system
labels:
toolkit.fluxcd.io/tenant: sre-teamDefine a Flux OCIRepository that points to where the Flagger Helm charts are stored:
apiVersion: source.toolkit.fluxcd.io/v1
kind: OCIRepository
metadata:
name: flagger
namespace: flagger-system
spec:
interval: 1h
url: oci://ghcr.io/fluxcd/charts/flagger
layerSelector:
mediaType: "application/vnd.cncf.helm.chart.content.v1.tar+gzip"
operation: copy
ref:
semver: "1.x" # update to the latest version
verify:
provider: cosign
matchOIDCIdentity:
- issuer: ^https://token\.actions\.githubusercontent\.com$
subject: ^https://github\.com/fluxcd/flagger/\.github/workflows/release\.yml@refs/tags/v\d+\.\d+\.\d+$The .spec.verify block instructs Flux to perform Cosign keyless verification and to
reject the artifact unless it was signed by the Flagger release workflow running on a
tagged release (vX.Y.Z). See the Flux
OCIRepository verification
documentation for more details.
Define a Flux HelmRelease that verifies and installs Flagger's latest version on the cluster:
---
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
name: flagger
namespace: flagger-system
spec:
interval: 12h
releaseName: flagger
install: # override existing Flagger CRDs
crds: CreateReplace
upgrade: # update Flagger CRDs
crds: CreateReplace
chartRef:
kind: OCIRepository
name: flagger
values:
nodeSelector:
kubernetes.io/os: linuxCopy the above manifests into a file called flagger.yaml, place the YAML file
in the Git repository bootstrapped with Flux, then commit and push it to upstream.
After Flux reconciles the changes on your cluster, you can check if Flagger got deployed with:
$ helm list -n flagger-system
NAME NAMESPACE REVISION STATUS CHART APP VERSION
flagger flagger-system 1 deployed flagger-1.42.0 1.42.0To uninstall Flagger, delete the flagger.yaml from your repository, then Flux will uninstall
the Helm release and will remove the namespace from your cluster.
Deploy Flagger load tester with Flux
Flagger comes with a load testing service that generates traffic during analysis when configured as a webhook.
The load tester container images and deployment manifests are published to GitHub Container Registry.
The container images and the manifests are signed with Cosign and GitHub Actions OIDC.
Assuming the applications managed by Flagger are in the apps namespace, you can configure Flux to
deploy the load tester there.
Define a Flux OCIRepository that points to where the Flagger Kustomize overlays are stored:
---
apiVersion: source.toolkit.fluxcd.io/v1
kind: OCIRepository
metadata:
name: flagger-loadtester
namespace: apps
spec:
interval: 6h # scan for new versions every six hours
url: oci://ghcr.io/fluxcd/flagger-manifests
ref:
semver: "*" # update to the latest version
verify:
provider: cosign
matchOIDCIdentity:
- issuer: ^https://token\.actions\.githubusercontent\.com$
subject: ^https://github\.com/fluxcd/flagger/\.github/workflows/release\.yml@refs/tags/v\d+\.\d+\.\d+$As with the Helm chart, the .spec.verify block ensures that only manifests signed by
the official Flagger release workflow are fetched and applied to the cluster.
Define a Flux Kustomization that deploys the Flagger load tester to the apps namespace:
---
apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
name: flagger-loadtester
namespace: apps
spec:
targetNamespace: apps
interval: 6h
wait: true
timeout: 5m
prune: true
sourceRef:
kind: OCIRepository
name: flagger-loadtester
path: ./testerCopy the above manifests into a file called flagger-loadtester.yaml, place the YAML file
in the Git repository bootstrapped with Flux, then commit and push it to upstream.
After Flux reconciles the changes on your cluster, you can check if the load tester got deployed with:
$ flux -n apps get kustomization flagger-loadtester
NAME READY MESSAGE
flagger-loadtester True Applied revision: v1.23.0/a80af71e001To uninstall the load tester, delete the flagger-loadtester.yaml from your repository,
and Flux will delete the load tester deployment from the cluster.
Gitbook Tutorials Apisix Progressive Delivery (`docs/gitbook/tutorials/apisix-progressive-delivery.md`)
Apache APISIX Canary Deployments
This guide shows you how to use the Apache APISIX and Flagger to automate canary deployments.
Prerequisites
Flagger requires a Kubernetes cluster v1.19 or newer and Apache APISIX v2.15 or newer and Apache APISIX Ingress Controller v1.5.0 or newer.
Install Apache APISIX and Apache APISIX Ingress Controller with Helm v3:
helm repo add apisix https://charts.apiseven.com
kubectl create ns apisix
helm upgrade -i apisix apisix/apisix --version=0.11.3 \
--namespace apisix \
--set apisix.podAnnotations."prometheus\.io/scrape"=true \
--set apisix.podAnnotations."prometheus\.io/port"=9091 \
--set apisix.podAnnotations."prometheus\.io/path"=/apisix/prometheus/metrics \
--set pluginAttrs.prometheus.export_addr.ip=0.0.0.0 \
--set pluginAttrs.prometheus.export_addr.port=9091 \
--set pluginAttrs.prometheus.export_uri=/apisix/prometheus/metrics \
--set pluginAttrs.prometheus.metric_prefix=apisix_ \
--set ingress-controller.enabled=true \
--set ingress-controller.config.apisix.serviceNamespace=apisixInstall Flagger and the Prometheus add-on in the same namespace as Apache APISIX:
helm repo add flagger https://flagger.app
helm upgrade -i flagger flagger/flagger \
--namespace apisix \
--set prometheus.install=true \
--set meshProvider=apisixBootstrap
Flagger takes a Kubernetes deployment and optionally a horizontal pod autoscaler (HPA), then creates a series of objects (Kubernetes deployments, ClusterIP services and an ApisixRoute). These objects expose the application outside the cluster and drive the canary analysis and promotion.
Create a test namespace:
kubectl create ns testCreate a deployment and a horizontal pod autoscaler:
kubectl apply -k https://github.com/fluxcd/flagger//kustomize/podinfo?ref=mainDeploy the load testing service to generate traffic during the canary analysis:
helm upgrade -i flagger-loadtester flagger/loadtester \
--namespace=testCreate an Apache APISIX ApisixRoute, Flagger will reference and generate the canary Apache APISIX ApisixRoute (replace app.example.com with your own domain):
apiVersion: apisix.apache.org/v2
kind: ApisixRoute
metadata:
name: podinfo
namespace: test
spec:
http:
- backends:
- serviceName: podinfo
servicePort: 80
match:
hosts:
- app.example.com
methods:
- GET
paths:
- /*
name: method
plugins:
- name: prometheus
enable: true
config:
disable: false
prefer_name: trueSave the above resource as podinfo-apisixroute.yaml and then apply it:
kubectl apply -f ./podinfo-apisixroute.yamlCreate a canary custom resource (replace app.example.com with your own domain):
/* Detailed source-code truncated for AI context efficiency. */Save the above resource as podinfo-canary.yaml and then apply it:
kubectl apply -f ./podinfo-canary.yamlAfter a couple of seconds Flagger will create the canary objects:
# applied
deployment.apps/podinfo
horizontalpodautoscaler.autoscaling/podinfo
apisixroute/podinfo
canary.flagger.app/podinfo
# generated
deployment.apps/podinfo-primary
horizontalpodautoscaler.autoscaling/podinfo-primary
service/podinfo
service/podinfo-canary
service/podinfo-primary
apisixroute/podinfo-podinfo-canaryAutomated 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 or MS Teams.
Trigger a canary deployment by updating the container image:
kubectl -n test set image deployment/podinfo \
podinfod=stefanprodan/podinfo:6.0.1Flagger detects that the deployment revision changed and starts a new rollout:
/* Detailed source-code truncated for AI context efficiency. */Note that if you apply new changes to the deployment during the canary analysis, Flagger will restart the analysis.
You can monitor all canaries with:
watch kubectl get canaries --all-namespaces
NAMESPACE NAME STATUS WEIGHT LASTTRANSITIONTIME
test podinfo-2 Progressing 10 2022-11-23T05:00:54Z
test podinfo Succeeded 0 2022-11-23T06:00:54ZAutomated rollback
During the canary analysis you can generate HTTP 500 errors to test if Flagger pauses and rolls back the faulted version.
Trigger another canary deployment:
kubectl -n test set image deployment/podinfo \
podinfod=stefanprodan/podinfo:6.0.2Exec into the load tester pod with:
kubectl -n test exec -it deploy/flagger-loadtester bashGenerate HTTP 500 errors:
hey -z 1m -c 5 -q 5 -host app.example.com http://apisix-gateway.apisix/status/500Generate latency:
watch -n 1 curl -H \"host: app.example.com\" http://apisix-gateway.apisix/delay/1When 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.
kubectl -n apisix logs deploy/flagger -f | jq .msg
"New revision detected! Scaling up podinfo.test"
"canary deployment podinfo.test not ready: waiting for rollout to finish: 0 of 1 (readyThreshold 100%) updated replicas are available"
"Starting canary analysis for podinfo.test"
"Advance podinfo.test canary weight 10"
"Halt podinfo.test advancement success rate 0.00% < 99%"
"Halt podinfo.test advancement success rate 26.76% < 99%"
"Halt podinfo.test advancement success rate 34.19% < 99%"
"Halt podinfo.test advancement success rate 37.32% < 99%"
"Halt podinfo.test advancement success rate 39.04% < 99%"
"Halt podinfo.test advancement success rate 40.13% < 99%"
"Halt podinfo.test advancement success rate 48.28% < 99%"
"Halt podinfo.test advancement success rate 50.35% < 99%"
"Halt podinfo.test advancement success rate 56.92% < 99%"
"Halt podinfo.test advancement success rate 67.70% < 99%"
"Rolling back podinfo.test failed checks threshold reached 10"
"Canary failed! Scaling down podinfo.test"Custom metrics
The canary analysis can be extended with Prometheus queries.
Create a metric template and apply it on the cluster:
apiVersion: flagger.app/v1beta1
kind: MetricTemplate
metadata:
name: not-found-percentage
namespace: test
spec:
provider:
type: prometheus
address: http://flagger-prometheus.apisix:9090
query: |
sum(
rate(
apisix_http_status{
route=~"{{ namespace }}_{{ route }}-{{ target }}-canary_.+",
code!~"4.."
}[{{ interval }}]
)
)
/
sum(
rate(
apisix_http_status{
route=~"{{ namespace }}_{{ route }}-{{ target }}-canary_.+"
}[{{ interval }}]
)
) * 100Edit the canary analysis and add the not found error rate check:
analysis:
metrics:
- name: "404s percentage"
templateRef:
name: not-found-percentage
thresholdRange:
max: 5
interval: 1mThe above configuration validates the canary by checking if the HTTP 404 req/sec percentage is below 5 percent of the total traffic. If the 404s rate reaches the 5% threshold, then the canary fails.
The above procedures can be extended with more custom metrics checks, webhooks, manual promotion approval and Slack or MS Teams notifications.
Gitbook Tutorials Contour Progressive Delivery (`docs/gitbook/tutorials/contour-progressive-delivery.md`)
Contour Canary Deployments
This guide shows you how to use Contour ingress controller and Flagger to automate canary releases and A/B testing.
Prerequisites
Flagger requires a Kubernetes cluster v1.16 or newer and Contour v1.0 or newer.
Install Contour on a cluster with LoadBalancer support:
kubectl apply -f https://projectcontour.io/quickstart/contour.yamlThe above command will deploy Contour and an Envoy daemonset in the projectcontour namespace.
Install Flagger using Kustomize (kubectl 1.14) in the projectcontour namespace:
kubectl apply -k https://github.com/fluxcd/flagger//kustomize/contour?ref=mainThe above command will deploy Flagger and Prometheus configured to scrape the Contour's Envoy instances.
Or you can install Flagger using Helm v3:
helm repo add flagger https://flagger.app
helm upgrade -i flagger flagger/flagger \
--namespace projectcontour \
--set meshProvider=contour \
--set ingressClass=contour \
--set prometheus.install=trueYou can also enable Slack, Discord, Rocket or MS Teams notifications, see the alerting docs.
Bootstrap
Flagger takes a Kubernetes deployment and optionally a horizontal pod autoscaler (HPA), then creates a series of objects (Kubernetes deployments, ClusterIP services and Contour HTTPProxy). These objects expose the application in the cluster and drive the canary analysis and promotion.
Create a test namespace:
kubectl create ns testInstall the load testing service to generate traffic during the canary analysis:
kubectl apply -k https://github.com/fluxcd/flagger//kustomize/tester?ref=mainCreate a deployment and a horizontal pod autoscaler:
kubectl apply -k https://github.com/fluxcd/flagger//kustomize/podinfo?ref=mainCreate a canary custom resource (replace app.example.com with your own domain):
/* Detailed source-code truncated for AI context efficiency. */Save the above resource as podinfo-canary.yaml and then apply it:
kubectl apply -f ./podinfo-canary.yamlThe 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:
# applied
deployment.apps/podinfo
horizontalpodautoscaler.autoscaling/podinfo
canary.flagger.app/podinfo
# generated
deployment.apps/podinfo-primary
horizontalpodautoscaler.autoscaling/podinfo-primary
service/podinfo
service/podinfo-canary
service/podinfo-primary
httpproxy.projectcontour.io/podinfoAfter the bootstrap, 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.
Expose the app outside the cluster
Find the external address of Contour's Envoy load balancer:
export ADDRESS="$(kubectl -n projectcontour get svc/envoy -ojson \
| jq -r ".status.loadBalancer.ingress[].hostname")"
echo $ADDRESSConfigure your DNS server with a CNAME record (AWS) or A record (GKE/AKS/DOKS) and point a domain e.g. app.example.com to the LB address.
Create a HTTPProxy definition and include the podinfo proxy generated by Flagger (replace app.example.com with your own domain):
apiVersion: projectcontour.io/v1
kind: HTTPProxy
metadata:
name: podinfo-ingress
namespace: test
spec:
virtualhost:
fqdn: app.example.com
includes:
- name: podinfo
namespace: test
conditions:
- prefix: /Save the above resource as podinfo-ingress.yaml and then apply it:
kubectl apply -f ./podinfo-ingress.yamlVerify that Contour processed the proxy definition with:
kubectl -n test get httpproxies
NAME FQDN STATUS
podinfo valid
podinfo-ingress app.example.com validNow you can access podinfo UI using your domain address.
Note that you should be using HTTPS when exposing production workloads on internet. You can obtain free TLS certs from Let's Encrypt, read this guide on how to configure cert-manager to secure Contour with TLS certificates.
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.
A canary deployment is triggered by changes in any of the following objects:
- Deployment PodSpec (container image, command, ports, env, resources, etc)
- ConfigMaps and Secrets mounted as volumes or mapped to environment variables
Trigger a canary deployment by updating the container image:
kubectl -n test set image deployment/podinfo \
podinfod=ghcr.io/stefanprodan/podinfo:6.0.1Flagger detects that the deployment revision changed and starts a new rollout:
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
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
Routing all traffic to primary
Promotion completed! Scaling down podinfo.testWhen the canary analysis starts, Flagger will call the pre-rollout webhooks before routing traffic to the canary.
Note that if you apply new changes to the deployment during the canary analysis, Flagger will restart the analysis.
You can monitor all canaries with:
watch kubectl get canaries --all-namespaces
NAMESPACE NAME STATUS WEIGHT LASTTRANSITIONTIME
test podinfo Progressing 15 2019-12-20T14:05:07ZIf you’ve enabled the Slack notifications, you should receive the following messages:
Automated rollback
During the canary analysis you can generate HTTP 500 errors or high latency to test if Flagger pauses the rollout.
Trigger a canary deployment:
kubectl -n test set image deployment/podinfo \
podinfod=ghcr.io/stefanprodan/podinfo:6.0.2Exec into the load tester pod with:
kubectl -n test exec -it deploy/flagger-loadtester bashGenerate HTTP 500 errors:
hey -z 1m -c 5 -q 5 http://app.example.com/status/500Generate latency:
watch -n 1 curl http://app.example.com/delay/1When 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.
kubectl -n projectcontour logs deploy/flagger -f | jq .msg
New revision detected! progressing 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 > 500ms
Halt podinfo.test advancement request duration 1.45s > 500ms
Rolling back podinfo.test failed checks threshold reached 5
Canary failed! Scaling down podinfo.testIf 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:
A/B Testing
Besides weighted routing, Flagger can be configured to route traffic to the canary based on HTTP match conditions. In an A/B testing scenario, you'll be using HTTP headers or cookies to target a certain segment of your users. This is particularly useful for frontend applications that require session affinity.
Edit the canary analysis, remove the max/step weight and add the match conditions and iterations:
analysis:
interval: 1m
threshold: 5
iterations: 10
match:
- headers:
x-canary:
exact: "insider"
webhooks:
- name: load-test
url: http://flagger-loadtester.test/
metadata:
cmd: "hey -z 1m -q 5 -c 5 -H 'X-Canary: insider' -host app.example.com http://envoy.projectcontour"The above configuration will run an analysis for ten minutes targeting users that have a X-Canary: insider header.
You can also use a HTTP cookie. To target all users with a cookie set to insider, the match condition should be:
match:
- headers:
cookie:
suffix: "insider"
webhooks:
- name: load-test
url: http://flagger-loadtester.test/
metadata:
cmd: "hey -z 1m -q 5 -c 5 -H 'Cookie: canary=insider' -host app.example.com http://envoy.projectcontour"Trigger a canary deployment by updating the container image:
kubectl -n test set image deployment/podinfo \
podinfod=ghcr.io/stefanprodan/podinfo:6.0.3Flagger detects that the deployment revision changed and starts the A/B test:
kubectl -n projectcontour logs deploy/flagger -f | jq .msg
New revision detected! Progressing canary analysis for podinfo.test
Advance podinfo.test canary iteration 1/10
Advance podinfo.test canary iteration 2/10
Advance podinfo.test canary iteration 3/10
Advance podinfo.test canary iteration 4/10
Advance podinfo.test canary iteration 5/10
Advance podinfo.test canary iteration 6/10
Advance podinfo.test canary iteration 7/10
Advance podinfo.test canary iteration 8/10
Advance podinfo.test canary iteration 9/10
Advance podinfo.test canary iteration 10/10
Copying podinfo.test template spec to podinfo-primary.test
Waiting for podinfo-primary.test rollout to finish: 1 of 2 updated replicas are available
Routing all traffic to primary
Promotion completed! Scaling down podinfo.testThe web browser user agent header allows user segmentation based on device or OS.
For example, if you want to route all mobile users to the canary instance:
match:
- headers:
user-agent:
prefix: "Mobile"Or if you want to target only Android users:
match:
- headers:
user-agent:
prefix: "Android"Or a specific browser version:
match:
- headers:
user-agent:
suffix: "Firefox/71.0"For an in-depth look at the analysis process read the usage docs.
Gitbook Tutorials Gatewayapi Progressive Delivery (`docs/gitbook/tutorials/gatewayapi-progressive-delivery.md`)
Gateway API Canary Deployments
This guide shows you how to use Gateway API and Flagger to automate canary deployments and A/B testing.
Prerequisites
Flagger requires an ingress controller or service mesh that implements the Gateway API HTTPRoute (v1 or v1beta1).
We'll be using Istio for the sake of this tutorial, but you can use any other implementation.
Install the Gateway API CRDs:
# Suggestion: Change v1.4.0 in to the latest Gateway API version
kubectl apply --server-side -k "github.com/kubernetes-sigs/gateway-api/config/crd?ref=v1.4.0"Install Istio:
istioctl install --set profile=minimal -y
# Suggestion: Change release-1.27 in to the latest Istio version
kubectl apply -f https://raw.githubusercontent.com/istio/istio/release-1.27/samples/addons/prometheus.yamlInstall Flagger in the flagger-system namespace:
kubectl create ns flagger-system
helm repo add flagger https://flagger.app
helm upgrade -i flagger flagger/flagger \
--namespace flagger-system \
--set prometheus.install=false \
--set meshProvider=gatewayapi:v1 \
--set metricsServer=http://prometheus.istio-system:9090Create a namespace for the Gateway:
kubectl create ns istio-ingressCreate a Gateway that configures load balancing, traffic ACL, etc:
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: gateway
namespace: istio-ingress
spec:
gatewayClassName: istio
listeners:
- name: default
hostname: "*.example.com"
port: 80
protocol: HTTP
allowedRoutes:
namespaces:
from: AllBootstrap
Flagger takes a Kubernetes deployment and optionally a horizontal pod autoscaler (HPA),
then creates a series of objects (Kubernetes deployments, ClusterIP services, HTTPRoutes for the Gateway).
These objects expose the application inside the mesh and drive the canary analysis and promotion.
Create a test namespace:
kubectl create ns testCreate a deployment and a horizontal pod autoscaler:
kubectl apply -k https://github.com/fluxcd/flagger//kustomize/podinfo?ref=mainDeploy the load testing service to generate traffic during the canary analysis:
kubectl apply -k https://github.com/fluxcd/flagger//kustomize/tester?ref=mainCreate metric templates targeting the Prometheus server in the flagger-system namespace.
The PromQL queries below are meant for Envoy,
but you can change it to your ingress/mesh provider accordingly.
apiVersion: flagger.app/v1beta1
kind: MetricTemplate
metadata:
name: latency
namespace: flagger-system
spec:
provider:
type: prometheus
address: http://prometheus.istio-system:9090
query: |
histogram_quantile(0.99,
sum(
rate(
istio_request_duration_milliseconds_bucket{
reporter="source",
destination_workload_namespace=~"{{ namespace }}",
destination_workload=~"{{ target }}",
}[{{ interval }}]
)
) by (le)
)/1000
---
apiVersion: flagger.app/v1beta1
kind: MetricTemplate
metadata:
name: error-rate
namespace: flagger-system
spec:
provider:
type: prometheus
address: http://prometheus.istio-system:9090
query: |
100 - sum(
rate(
istio_requests_total{
reporter="source",
destination_workload_namespace=~"{{ namespace }}",
destination_workload=~"{{ target }}",
response_code!~"5.*"
}[{{ interval }}]
)
)
/
sum(
rate(
istio_requests_total{
reporter="source",
destination_workload_namespace=~"{{ namespace }}",
destination_workload=~"{{ target }}",
}[{{ interval }}]
)
)
* 100Save the above resource as metric-templates.yaml and then apply it:
kubectl apply -f metric-templates.yamlCreate a Canary custom resource (replace "www.example.com" with your own domain):
/* Detailed source-code truncated for AI context efficiency. */Save the above resource as podinfo-canary.yaml and then apply it:
kubectl apply -f ./podinfo-canary.yamlWhen 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 minute.
After a couple of seconds Flagger will create the canary objects:
# applied
deployment.apps/podinfo
horizontalpodautoscaler.autoscaling/podinfo
canary.flagger.app/podinfo
# generated
deployment.apps/podinfo-primary
horizontalpodautoscaler.autoscaling/podinfo-primary
service/podinfo
service/podinfo-canary
service/podinfo-primary
httproutes.gateway.networking.k8s.io/podinfoExpose the app outside the cluster
Find the external address of Istio's load balancer:
export ADDRESS="$(kubectl -n istio-ingress get svc/gateway-istio -ojson \
| jq -r ".status.loadBalancer.ingress[].hostname")"
echo $ADDRESSConfigure your DNS server with a CNAME record (AWS) or A record (GKE/AKS/DOKS) and
point a domain e.g. www.example.com to the LB address.
Now you can access the podinfo UI using your domain address.
Note that you should be using HTTPS when exposing production workloads on internet.
If you're using a local cluster you can port forward to the Envoy LoadBalancer service:
kubectl port-forward -n istio-ingress svc/gateway-istio 8080:80Now you can access podinfo via curl -H "Host: www.example.com" localhost:8080.
Automated canary promotion
With the application bootstrapped, Flagger will continuously monitor the deployment for changes.
When a new revision is detected, Flagger will start a canary analysis and gradually shift traffic to the new version.
Trigger a canary deployment by updating the container image:
kubectl -n test set image deployment/podinfo \
podinfod=stefanprodan/podinfo:6.0.1Flagger detects that the deployment revision changed and starts a new rollout:
/* Detailed source-code truncated for AI context efficiency. */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 how Flagger progressively changes the weights of
the HTTPRoute object that is attached to the Gateway with:
watch kubectl get httproute -n test podinfo -o=jsonpath='{.spec.rules}'You can monitor all canaries with:
watch kubectl get canaries --all-namespaces
NAMESPACE NAME STATUS WEIGHT LASTTRANSITIONTIME
test podinfo Progressing 15 2025-10-16T14:05:07Z
prod frontend Succeeded 0 2025-10-15T16:15:07Z
prod backend Failed 0 2025-10-14T17:05:07ZAutomated rollback
During the canary analysis you can generate HTTP 500 errors and high latency to test if Flagger pauses the rollout.
Trigger another canary deployment:
kubectl -n test set image deployment/podinfo \
podinfod=stefanprodan/podinfo:6.0.2Exec into the load tester pod with:
kubectl -n test exec -it flagger-loadtester-xx-xx shGenerate HTTP 500 errors:
watch curl http://podinfo-canary:9898/status/500Generate latency:
watch curl http://podinfo-canary:9898/delay/1When 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.
kubectl -n test describe canary/podinfo
Status:
Canary Weight: 0
Failed Checks: 10
Phase: Failed
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Synced 3m flagger Starting canary deployment for podinfo.test
Normal Synced 3m flagger Advance podinfo.test canary weight 5
Normal Synced 3m flagger Advance podinfo.test canary weight 10
Normal Synced 3m flagger Advance podinfo.test canary weight 15
Normal Synced 3m flagger Halt podinfo.test advancement error rate 69.17% > 1%
Normal Synced 2m flagger Halt podinfo.test advancement error rate 61.39% > 1%
Normal Synced 2m flagger Halt podinfo.test advancement error rate 55.06% > 1%
Normal Synced 2m flagger Halt podinfo.test advancement error rate 47.00% > 1%
Normal Synced 2m flagger (combined from similar events): Halt podinfo.test advancement error rate 38.08% > 1%
Warning Synced 1m flagger Rolling back podinfo.test failed checks threshold reached 10
Warning Synced 1m flagger Canary failed! Scaling down podinfo.testA/B Testing
Besides weighted routing, Flagger can be configured to route traffic to the canary based on HTTP match conditions.
In an A/B testing scenario, you'll be using HTTP headers and cookies to target a certain segment of your users.
Create a canary custom resource (replace "www.example.com" with your own domain):
/* Detailed source-code truncated for AI context efficiency. */The above configuration will run an analysis for ten minutes targeting those users that
have an insider cookie or are using Firefox as a browser.
Save the above resource as podinfo-ab-canary.yaml and then apply it:
kubectl apply -f ./podinfo-ab-canary.yamlTrigger a canary deployment by updating the container image:
kubectl -n test set image deployment/podinfo \
podinfod=stefanprodan/podinfo:6.0.3Flagger detects that the deployment revision changed and starts a new rollout:
/* Detailed source-code truncated for AI context efficiency. */Session Affinity
While Flagger can perform weighted routing and A/B testing individually,
with Gateway API it can combine the two leading to a Canary release with session affinity.
For more information you can read the deployment strategies docs.
Note: Session Affinity requires a Gateway API implementation that supports
theResponseHeaderModifierAPI.
Create a canary custom resource (replace www.example.com with your own domain):
/* Detailed source-code truncated for AI context efficiency. */Save the above resource as podinfo-canary-session-affinity.yaml and then apply it:
kubectl apply -f ./podinfo-canary-session-affinity.yamlTrigger a canary deployment by updating the container image:
kubectl -n test set image deployment/podinfo \
podinfod=ghcr.io/stefanprodan/podinfo:6.0.1You can load www.example.com in your browser and refresh it until you see the requests being served by podinfo:6.0.1.
All subsequent requests after that will be served by podinfo:6.0.1 and not podinfo:6.0.0 because of the session affinity
configured by Flagger in the HTTPRoute object.
To configure stickiness for the Primary deployment to ensure fair weighted traffic routing, please
checkout the deployment strategies docs.
Traffic mirroring
For applications that perform read operations, Flagger can be configured to do B/G tests with traffic mirroring.
Note: Traffic mirroring requires a Gateway API implementation that supports
theRequestMirrorfilter.
You can enable mirroring by replacing stepWeight with iterations and by setting analysis.mirror to true:
apiVersion: flagger.app/v1beta1
kind: Canary
metadata:
name: podinfo
namespace: test
spec:
# deployment reference
targetRef:
apiVersion: apps/v1
kind: Deployment
name: podinfo
service:
# service port number
port: 9898
# container port number or name (optional)
targetPort: 9898
# Gateway API HTTPRoute host names
hosts:
- www.example.com
# Reference to the Gateway that the generated HTTPRoute would attach to.
gatewayRefs:
- name: gateway
namespace: istio-ingress
analysis:
# schedule interval
interval: 1m
# max number of failed metric checks before rollback
threshold: 5
# total number of iterations
iterations: 10
# enable traffic shadowing
mirror: true
# Gateway API HTTPRoute host names
metrics:
- name: request-success-rate
thresholdRange:
min: 99
interval: 1m
- name: request-duration
thresholdRange:
max: 500
interval: 1m
webhooks:
- name: load-test
url: http://flagger-loadtester.test/
timeout: 5s
metadata:
cmd: "hey -z 2m -q 10 -c 2 -host www.example.com http://gateway-istio.istio-ingress/"Gateway API traffic mirroring will copy each incoming request, sending one request to the primary and one to the canary service.
The response from the primary is sent back to the user and the response from the canary is discarded.
Metrics are collected on both requests so that the deployment will only proceed if the canary metrics are within the threshold values.
The above procedures can be extended with custom metrics checks, webhooks, manual promotion approval and Slack or MS Teams notifications.
Customising the HTTPRoute
Besides the hosts and gatewayRefs fields, you can customize the generated HTTPRoute with various options
exposed under the spec.service field of the Canary.
Timeouts
You can configure request timeouts on the generated HTTPRoute using the spec.service.timeout andspec.service.backendTimeout fields of the Canary. Both values are
Gateway API Duration strings (e.g. 5s, 500ms).
timeoutmaps toHTTPRouteTimeouts.Requestand bounds the total request duration from when the gateway
receives the request to when the response is sent back to the client.backendTimeoutmaps toHTTPRouteTimeouts.BackendRequestand bounds the duration of an individual
request from the gateway to a backend. When set together withtimeout,backendTimeoutmust be
less than or equal totimeout.
Note: Timeouts require a Gateway API implementation that supports
HTTPRouteTimeouts.backendTimeoutadditionally requires support for theBackendRequesttimeout field.
Example configuration:
apiVersion: flagger.app/v1beta1
kind: Canary
metadata:
name: podinfo
namespace: test
spec:
service:
# Total request timeout (gateway to client)
timeout: 5s
# Per-attempt backend request timeout (gateway to backend)
backendTimeout: 2sHeader Manipulation
You can configure request and response header manipulation using the spec.service.headers field of the Canary.
Note: Header manipulation requires a Gateway API implementation that supports
theRequestHeaderModifierandResponseHeaderModifierfilters.
Example configuration:
apiVersion: flagger.app/v1beta1
kind: Canary
metadata:
name: podinfo
namespace: test
spec:
service:
headers:
request:
add:
x-custom-header: "custom-value"
set:
x-api-version: "v1"
remove:
- x-debug-header
response:
add:
x-frame-options: "DENY"
x-content-type-options: "nosniff"
set:
cache-control: "no-cache"
remove:
- x-powered-byURL Rewriting
You can configure URL rewriting using the spec.service.rewrite field of the Canary to modify the path or hostname of requests.
Note: URL rewriting requires a Gateway API implementation that supports
theURLRewritefilter.
Example configuration:
apiVersion: flagger.app/v1beta1
kind: Canary
metadata:
name: podinfo
namespace: test
spec:
service:
rewrite:
# Rewrite the URI path
uri: "/v2/api"
# Optionally specify the rewrite type: "ReplaceFullPath" or "ReplacePrefixMatch"
# Defaults to "ReplaceFullPath" if not specified
type: "ReplaceFullPath"
# Rewrite the hostname/authority header
authority: "api.example.com"The type field determines how the URI rewriting is performed:
- ReplaceFullPath: Replaces the entire request path with the specified
urivalue - ReplacePrefixMatch: Replaces only the prefix portion of the path that was matched
Example with prefix replacement:
apiVersion: flagger.app/v1beta1
kind: Canary
metadata:
name: podinfo
namespace: test
spec:
service:
rewrite:
uri: "/api/v2"
type: "ReplacePrefixMatch"When using ReplacePrefixMatch, if a request comes to /old/path, and the HTTPRoute matches the prefix /old,
the request will be rewritten to /api/v2/path.
CORS Policy
The cross-origin resource sharing policy can be configured the spec.service.corsPolicy field of the Canary.
Note: Cross-origin resource sharing requires a Gateway API implementation that supports
theCORSfilter.
Example configuration:
apiVersion: flagger.app/v1beta1
kind: Canary
metadata:
name: podinfo
namespace: test
spec:
service:
corsPolicy:
allowOrigin:
- https://foo.example
- http://foo.example
allowMethods:
- GET
- PUT
- POST
- DELETE
- PATCH
- OPTIONS
allowCredentials: true
allowHeaders:
- Keep-Alive
- User-Agent
- X-Requested-With
- If-Modified-Since
- Cache-Control
- Content-Type
- Authorization
maxAge: 24hGitbook Tutorials Gloo Progressive Delivery (`docs/gitbook/tutorials/gloo-progressive-delivery.md`)
Gloo Canary Deployments
This guide shows you how to use the Gloo Edge ingress controller
and Flagger to automate canary releases and A/B testing.
Prerequisites
Flagger requires a Kubernetes cluster v1.16 or newer and Gloo Edge ingress 1.6.0 or newer.
This guide was written for Flagger version 1.6.0 or higher. Prior versions of Flagger
used Gloo UpstreamGroups to handle canaries, but newer versions of Flagger use GlooRouteTables to handle canaries as well as A/B testing.
Install Gloo with Helm v3:
helm repo add gloo https://storage.googleapis.com/solo-public-helm
kubectl create ns gloo-system
helm upgrade -i gloo gloo/gloo \
--namespace gloo-systemInstall Flagger and the Prometheus add-on in the same namespace as Gloo:
helm repo add flagger https://flagger.app
helm upgrade -i flagger flagger/flagger \
--namespace gloo-system \
--set prometheus.install=true \
--set meshProvider=glooBootstrap
Flagger takes a Kubernetes deployment and optionally a horizontal pod autoscaler (HPA),
then creates a series of objects (Kubernetes deployments, ClusterIP services, Gloo route tables and upstreams).
These objects expose the application outside the cluster and drive the canary analysis and promotion.
Create a test namespace:
kubectl create ns testCreate a deployment and a horizontal pod autoscaler:
kubectl -n test apply -k https://github.com/fluxcd/flagger//kustomize/podinfo?ref=mainDeploy the load testing service to generate traffic during the canary analysis:
kubectl -n test apply -k https://github.com/fluxcd/flagger//kustomize/tester?ref=mainCreate a virtual service definition that references a route table that will be generated by Flagger
(replace app.example.com with your own domain):
apiVersion: gateway.solo.io/v1
kind: VirtualService
metadata:
name: podinfo
namespace: test
spec:
virtualHost:
domains:
- 'app.example.com'
routes:
- matchers:
- prefix: /
delegateAction:
ref:
name: podinfo
namespace: testSave the above resource as podinfo-virtualservice.yaml and then apply it:
kubectl apply -f ./podinfo-virtualservice.yamlCreate a canary custom resource (replace app.example.com with your own domain):
/* Detailed source-code truncated for AI context efficiency. */Note: when using upstreamRef the following fields are copied over from the original upstream: Labels, SslConfig, CircuitBreakers, ConnectionConfig, UseHttp2, InitialStreamWindowSize
Save the above resource as podinfo-canary.yaml and then apply it:
kubectl apply -f ./podinfo-canary.yamlAfter a couple of seconds Flagger will create the canary objects:
# applied
deployment.apps/podinfo
horizontalpodautoscaler.autoscaling/podinfo
virtualservices.gateway.solo.io/podinfo
canary.flagger.app/podinfo
# generated
deployment.apps/podinfo-primary
horizontalpodautoscaler.autoscaling/podinfo-primary
service/podinfo
service/podinfo-canary
service/podinfo-primary
routetables.gateway.solo.io/podinfo
upstreams.gloo.solo.io/test-podinfo-canaryupstream-9898
upstreams.gloo.solo.io/test-podinfo-primaryupstream-9898When the bootstrap finishes Flagger will set the canary status to initialized:
kubectl -n test get canary podinfo
NAME STATUS WEIGHT LASTTRANSITIONTIME
podinfo Initialized 0 2019-05-17T08:09:51ZAutomated 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.
Trigger a canary deployment by updating the container image:
kubectl -n test set image deployment/podinfo \
podinfod=ghcr.io/stefanprodan/podinfo:6.0.1Flagger detects that the deployment revision changed and starts a new rollout:
/* Detailed source-code truncated for AI context efficiency. */Note that if you apply new changes to the deployment during the canary analysis, Flagger will restart the analysis.
You can monitor all canaries with:
watch kubectl get canaries --all-namespaces
NAMESPACE NAME STATUS WEIGHT LASTTRANSITIONTIME
test podinfo Progressing 15 2019-05-17T14:05:07Z
prod frontend Succeeded 0 2019-05-17T16:15:07Z
prod backend Failed 0 2019-05-17T17:05:07ZAutomated 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:
kubectl -n test set image deployment/podinfo \
podinfod=ghcr.io/stefanprodan/podinfo:6.0.2Generate HTTP 500 errors:
watch curl -H 'Host: app.example.com' http://gateway-proxy.gloo-system/status/500Generate high latency:
watch curl -H 'Host: app.example.com' http://gateway-proxy.gloo-system/delay/2When 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.
kubectl -n test describe canary/podinfo
Status:
Canary Weight: 0
Failed Checks: 10
Phase: Failed
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Synced 3m flagger Starting canary deployment for podinfo.test
Normal Synced 3m flagger Advance podinfo.test canary weight 5
Normal Synced 3m flagger Advance podinfo.test canary weight 10
Normal Synced 3m flagger Advance podinfo.test canary weight 15
Normal Synced 3m flagger Halt podinfo.test advancement success rate 69.17% < 99%
Normal Synced 2m flagger Halt podinfo.test advancement success rate 61.39% < 99%
Normal Synced 2m flagger Halt podinfo.test advancement success rate 55.06% < 99%
Normal Synced 2m flagger Halt podinfo.test advancement success rate 47.00% < 99%
Normal Synced 2m flagger (combined from similar events): Halt podinfo.test advancement success rate 38.08% < 99%
Warning Synced 1m flagger Rolling back podinfo.test failed checks threshold reached 10
Warning Synced 1m flagger Canary failed! Scaling down podinfo.testCustom metrics
The canary analysis can be extended with Prometheus queries.
The demo app is instrumented with Prometheus so you can create a custom check that will use the HTTP request
duration histogram to validate the canary.
Create a metric template and apply it on the cluster:
apiVersion: flagger.app/v1beta1
kind: MetricTemplate
metadata:
name: not-found-percentage
namespace: test
spec:
provider:
type: prometheus
address: http://flagger-prometheus.gloo-system:9090
query: |
100 - sum(
rate(
http_request_duration_seconds_count{
kubernetes_namespace="{{ namespace }}",
kubernetes_pod_name=~"{{ target }}-[0-9a-zA-Z]+(-[0-9a-zA-Z]+)"
status!="{{ interval }}"
}[1m]
)
)
/
sum(
rate(
http_request_duration_seconds_count{
kubernetes_namespace="{{ namespace }}",
kubernetes_pod_name=~"{{ target }}-[0-9a-zA-Z]+(-[0-9a-zA-Z]+)"
}[{{ interval }}]
)
) * 100Edit the canary analysis and add the following metric:
analysis:
metrics:
- name: "404s percentage"
templateRef:
name: not-found-percentage
thresholdRange:
max: 5
interval: 1mThe above configuration validates the canary by checking if the HTTP 404 req/sec percentage
is below 5 percent of the total traffic. If the 404s rate reaches the 5% threshold, then the canary fails.
Trigger a canary deployment by updating the container image:
kubectl -n test set image deployment/podinfo \
podinfod=ghcr.io/stefanprodan/podinfo:6.0.3Generate 404s:
watch curl -H 'Host: app.example.com' http://gateway-proxy.gloo-system/status/404Watch Flagger logs:
kubectl -n gloo-system logs deployment/flagger -f | jq .msg
Starting canary deployment for podinfo.test
Advance podinfo.test canary weight 5
Advance podinfo.test canary weight 10
Advance podinfo.test canary weight 15
Halt podinfo.test advancement 404s percentage 6.20 > 5
Halt podinfo.test advancement 404s percentage 6.45 > 5
Halt podinfo.test advancement 404s percentage 7.60 > 5
Halt podinfo.test advancement 404s percentage 8.69 > 5
Halt podinfo.test advancement 404s percentage 9.70 > 5
Rolling back podinfo.test failed checks threshold reached 5
Canary failed! Scaling down podinfo.testIf you have alerting configured,
Flagger will send a notification with the reason why the canary failed.
A/B Testing
Besides weighted routing, Flagger can be configured to route traffic to the canary based on HTTP match conditions.
In an A/B testing scenario, you'll be using HTTP headers or cookies to target a certain segment of your users.
This is particularly useful for frontend applications that require session affinity.
Edit the canary analysis, remove the max/step weight and add the match conditions and iterations:
analysis:
interval: 1m
threshold: 5
iterations: 10
match:
- headers:
x-canary:
exact: "insider"
webhooks:
- name: load-test
url: http://flagger-loadtester.test/
metadata:
cmd: "hey -z 1m -q 5 -c 5 -H 'X-Canary: insider' -host app.example.com http://gateway-proxy.gloo-system"The above configuration will run an analysis for ten minutes targeting users that have a X-Canary: insider header.
Trigger a canary deployment by updating the container image:
kubectl -n test set image deployment/podinfo \
podinfod=ghcr.io/stefanprodan/podinfo:6.0.4Flagger detects that the deployment revision changed and starts the A/B test:
kubectl -n gloo-system logs deploy/flagger -f | jq .msg
New revision detected! Progressing canary analysis for podinfo.test
Advance podinfo.test canary iteration 1/10
Advance podinfo.test canary iteration 2/10
Advance podinfo.test canary iteration 3/10
Advance podinfo.test canary iteration 4/10
Advance podinfo.test canary iteration 5/10
Advance podinfo.test canary iteration 6/10
Advance podinfo.test canary iteration 7/10
Advance podinfo.test canary iteration 8/10
Advance podinfo.test canary iteration 9/10
Advance podinfo.test canary iteration 10/10
Copying podinfo.test template spec to podinfo-primary.test
Waiting for podinfo-primary.test rollout to finish: 1 of 2 updated replicas are available
Routing all traffic to primary
Promotion completed! Scaling down podinfo.testThe web browser user agent header allows user segmentation based on device or OS.
For example, if you want to route all mobile users to the canary instance:
match:
- headers:
user-agent:
regex: ".*Mobile.*"Or if you want to target only Android users:
match:
- headers:
user-agent:
regex: ".*Android.*"Or a specific browser version:
match:
- headers:
user-agent:
regex: ".*Firefox.*"For an in-depth look at the analysis process read the usage docs.
Gitbook Tutorials Istio Ab Testing (`docs/gitbook/tutorials/istio-ab-testing.md`)
Istio A/B Testing
This guide shows you how to automate A/B testing with Istio and Flagger.
Besides weighted routing, Flagger can be configured to route traffic to the canary based on HTTP match conditions. In an A/B testing scenario, you'll be using HTTP headers or cookies to target a certain segment of your users. This is particularly useful for frontend applications that require session affinity.
Prerequisites
Flagger requires a Kubernetes cluster v1.16 or newer and Istio v1.0 or newer.
Install Istio with telemetry support and Prometheus:
istioctl manifest install --set profile=default
kubectl apply -f https://raw.githubusercontent.com/istio/istio/release-1.18/samples/addons/prometheus.yamlInstall Flagger in the istio-system namespace:
kubectl apply -k github.com/fluxcd/flagger//kustomize/istioCreate an ingress gateway to expose the demo app outside of the mesh:
apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
name: public-gateway
namespace: istio-system
spec:
selector:
istio: ingressgateway
servers:
- port:
number: 80
name: http
protocol: HTTP
hosts:
- "*"Bootstrap
Create a test namespace with Istio sidecar injection enabled:
kubectl create ns test
kubectl label namespace test istio-injection=enabledCreate a deployment and a horizontal pod autoscaler:
kubectl apply -k https://github.com/fluxcd/flagger//kustomize/podinfo?ref=mainDeploy the load testing service to generate traffic during the canary analysis:
kubectl apply -k https://github.com/fluxcd/flagger//kustomize/tester?ref=mainCreate a canary custom resource (replace example.com with your own domain):
/* Detailed source-code truncated for AI context efficiency. */Note that when using Istio 1.5 you have to replace the request-duration with a metric template.
The above configuration will run an analysis for ten minutes targeting Firefox users and those that have an insider cookie.
Save the above resource as podinfo-abtest.yaml and then apply it:
kubectl apply -f ./podinfo-abtest.yamlAfter a couple of seconds Flagger will create the canary objects:
# applied
deployment.apps/podinfo
horizontalpodautoscaler.autoscaling/podinfo
canary.flagger.app/podinfo
# generated
deployment.apps/podinfo-primary
horizontalpodautoscaler.autoscaling/podinfo-primary
service/podinfo
service/podinfo-canary
service/podinfo-primary
destinationrule.networking.istio.io/podinfo-canary
destinationrule.networking.istio.io/podinfo-primary
virtualservice.networking.istio.io/podinfoAutomated canary promotion
Trigger a canary deployment by updating the container image:
kubectl -n test set image deployment/podinfo \
podinfod=ghcr.io/stefanprodan/podinfo:6.0.1Flagger detects that the deployment revision changed and starts a new rollout:
/* Detailed source-code truncated for AI context efficiency. */Note that if you apply new changes to the deployment during the canary analysis, Flagger will restart the analysis.
You can monitor all canaries with:
watch kubectl get canaries --all-namespaces
NAMESPACE NAME STATUS WEIGHT LASTTRANSITIONTIME
test podinfo Progressing 100 2019-03-16T14:05:07Z
prod frontend Succeeded 0 2019-03-15T16:15:07Z
prod backend Failed 0 2019-03-14T17:05:07ZAutomated rollback
During the canary analysis you can generate HTTP 500 errors and high latency to test Flagger's rollback.
Generate HTTP 500 errors:
watch curl -b 'type=insider' http://app.example.com/status/500Generate latency:
watch curl -b 'type=insider' http://app.example.com/delay/1When 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.
kubectl -n test describe canary/podinfo
Status:
Failed Checks: 2
Phase: Failed
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Synced 3m flagger Starting canary deployment for podinfo.test
Normal Synced 3m flagger Advance podinfo.test canary iteration 1/10
Normal Synced 3m flagger Advance podinfo.test canary iteration 2/10
Normal Synced 3m flagger Advance podinfo.test canary iteration 3/10
Normal Synced 3m flagger Halt podinfo.test advancement success rate 69.17% < 99%
Normal Synced 2m flagger Halt podinfo.test advancement success rate 61.39% < 99%
Warning Synced 2m flagger Rolling back podinfo.test failed checks threshold reached 2
Warning Synced 1m flagger Canary failed! Scaling down podinfo.testThe above procedure can be extended with custom metrics checks, webhooks, manual promotion approval and Slack or MS Teams notifications.
Gitbook Tutorials Istio Progressive Delivery (`docs/gitbook/tutorials/istio-progressive-delivery.md`)
Istio Canary Deployments
This guide shows you how to use Istio and Flagger to automate canary deployments.
Prerequisites
Flagger requires a Kubernetes cluster v1.16 or newer and Istio v1.5 or newer.
Install Istio with telemetry support and Prometheus:
istioctl manifest install --set profile=default
# Suggestion: Please change release-1.8 in below command, to your real istio version.
kubectl apply -f https://raw.githubusercontent.com/istio/istio/release-1.18/samples/addons/prometheus.yamlInstall Flagger in the istio-system namespace:
kubectl apply -k github.com/fluxcd/flagger//kustomize/istioCreate an ingress gateway to expose the demo app outside of the mesh:
apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
name: public-gateway
namespace: istio-system
spec:
selector:
istio: ingressgateway
servers:
- port:
number: 80
name: http
protocol: HTTP
hosts:
- "*"Bootstrap
Flagger takes a Kubernetes deployment and optionally a horizontal pod autoscaler (HPA), then creates a series of objects (Kubernetes deployments, ClusterIP services, Istio destination rules and virtual services). These objects expose the application inside the mesh and drive the canary analysis and promotion.
Create a test namespace with Istio sidecar injection enabled:
kubectl create ns test
kubectl label namespace test istio-injection=enabledCreate a deployment and a horizontal pod autoscaler:
kubectl apply -k https://github.com/fluxcd/flagger//kustomize/podinfo?ref=mainDeploy the load testing service to generate traffic during the canary analysis:
kubectl apply -k https://github.com/fluxcd/flagger//kustomize/tester?ref=mainCreate a canary custom resource (replace example.com with your own domain):
/* Detailed source-code truncated for AI context efficiency. */Note that when using Istio 1.4 you have to replace the request-duration with a metric template.
Save the above resource as podinfo-canary.yaml and then apply it:
kubectl apply -f ./podinfo-canary.yamlWhen 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 minute.
After a couple of seconds Flagger will create the canary objects:
# applied
deployment.apps/podinfo
horizontalpodautoscaler.autoscaling/podinfo
canary.flagger.app/podinfo
# generated
deployment.apps/podinfo-primary
horizontalpodautoscaler.autoscaling/podinfo-primary
service/podinfo
service/podinfo-canary
service/podinfo-primary
destinationrule.networking.istio.io/podinfo-canary
destinationrule.networking.istio.io/podinfo-primary
virtualservice.networking.istio.io/podinfoAutomated canary promotion
Trigger a canary deployment by updating the container image:
kubectl -n test set image deployment/podinfo \
podinfod=ghcr.io/stefanprodan/podinfo:6.0.1Flagger detects that the deployment revision changed and starts a new rollout:
/* Detailed source-code truncated for AI context efficiency. */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:
watch kubectl get canaries --all-namespaces
NAMESPACE NAME STATUS WEIGHT LASTTRANSITIONTIME
test podinfo Progressing 15 2019-01-16T14:05:07Z
prod frontend Succeeded 0 2019-01-15T16:15:07Z
prod backend Failed 0 2019-01-14T17:05:07ZAutomated rollback
During the canary analysis you can generate HTTP 500 errors and high latency to test if Flagger pauses the rollout.
Trigger another canary deployment:
kubectl -n test set image deployment/podinfo \
podinfod=ghcr.io/stefanprodan/podinfo:6.0.2Exec into the load tester pod with:
kubectl -n test exec -it flagger-loadtester-xx-xx shGenerate HTTP 500 errors:
watch curl http://podinfo-canary:9898/status/500Generate latency:
watch curl http://podinfo-canary:9898/delay/1When 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.
kubectl -n test describe canary/podinfo
Status:
Canary Weight: 0
Failed Checks: 10
Phase: Failed
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Synced 3m flagger Starting canary deployment for podinfo.test
Normal Synced 3m flagger Advance podinfo.test canary weight 5
Normal Synced 3m flagger Advance podinfo.test canary weight 10
Normal Synced 3m flagger Advance podinfo.test canary weight 15
Normal Synced 3m flagger Halt podinfo.test advancement success rate 69.17% < 99%
Normal Synced 2m flagger Halt podinfo.test advancement success rate 61.39% < 99%
Normal Synced 2m flagger Halt podinfo.test advancement success rate 55.06% < 99%
Normal Synced 2m flagger Halt podinfo.test advancement success rate 47.00% < 99%
Normal Synced 2m flagger (combined from similar events): Halt podinfo.test advancement success rate 38.08% < 99%
Warning Synced 1m flagger Rolling back podinfo.test failed checks threshold reached 10
Warning Synced 1m flagger Canary failed! Scaling down podinfo.testSession Affinity
While Flagger can perform weighted routing and A/B testing individually, with Istio it can combine the two leading to a Canary
release with session affinity. For more information you can read the deployment strategies docs.
Create a canary custom resource (replace app.example.com with your own domain):
/* Detailed source-code truncated for AI context efficiency. */Save the above resource as podinfo-canary-session-affinity.yaml and then apply it:
kubectl apply -f ./podinfo-canary-session-affinity.yamlTrigger a canary deployment by updating the container image:
kubectl -n test set image deployment/podinfo \
podinfod=ghcr.io/stefanprodan/podinfo:6.0.1You can load app.example.com in your browser and refresh it until you see the requests being served by podinfo:6.0.1.
All subsequent requests after that will be served by podinfo:6.0.1 and not podinfo:6.0.0 because of the session affinity
configured by Flagger with Istio.
To configure stickiness for the Primary deployment to ensure fair weighted traffic routing, please
checkout the deployment strategies docs.
Traffic mirroring
For applications that perform read operations, Flagger can be configured to drive canary releases with traffic mirroring. Istio traffic mirroring will copy each incoming request, sending one request to the primary and one to the canary service. The response from the primary is sent back to the user and the response from the canary is discarded. Metrics are collected on both requests so that the deployment will only proceed if the canary metrics are within the threshold values.
Note that mirroring should be used for requests that are idempotent or capable of being processed twice (once by the primary and once by the canary).
You can enable mirroring by replacing stepWeight/maxWeight with iterations and by setting analysis.mirror to true:
apiVersion: flagger.app/v1beta1
kind: Canary
metadata:
name: podinfo
namespace: test
spec:
analysis:
# schedule interval
interval: 1m
# max number of failed metric checks before rollback
threshold: 5
# total number of iterations
iterations: 10
# enable traffic shadowing
mirror: true
# weight of the traffic mirrored to your canary (defaults to 100%)
mirrorWeight: 100
metrics:
- name: request-success-rate
thresholdRange:
min: 99
interval: 1m
- name: request-duration
thresholdRange:
max: 500
interval: 1m
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
url: http://flagger-loadtester.test/
timeout: 5s
metadata:
cmd: "hey -z 1m -q 10 -c 2 http://podinfo.test:9898/"With the above configuration, Flagger will run a canary release with the following steps:
- detect new revision (deployment spec, secrets or configmaps changes)
- scale from zero the canary deployment
- wait for the HPA to set the canary minimum replicas
- check canary pods health
- run the acceptance tests
- abort the canary release if tests fail
- start the load tests
- mirror 100% of the traffic from primary to canary
- check request success rate and request duration every minute
- abort the canary release if the metrics check failure threshold is reached
- stop traffic mirroring after the number of iterations is reached
- route live traffic to the canary pods
- promote the canary (update the primary secrets, configmaps and deployment spec)
- wait for the primary deployment rollout to finish
- wait for the HPA to set the primary minimum replicas
- check primary pods health
- switch live traffic back to primary
- scale to zero the canary
- send notification with the canary analysis result
The above procedure can be extended with custom metrics checks, webhooks, manual promotion approval and Slack or MS Teams notifications.
Canary Deployments for TCP Services
Performing a Canary deployment on a TCP (non HTTP) service is nearly identical to an HTTP Canary. Besides updating your Gateway document to support the TCP routing, the only difference is you have to set the appProtocol field to TCP inside of the service section of your Canary document.
Example:
apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
name: public-gateway
namespace: istio-system
spec:
selector:
istio: ingressgateway
servers:
- port:
number: 7070
name: tcp-service
protocol: TCP # <== set the protocol to tcp here
hosts:
- "*"apiVersion: flagger.app/v1beta1
kind: Canary
# omitted for brevity
spec:
service:
port: 7070
appProtocol: TCP # <== set the appProtocol here
targetPort: 7070
portName: "tcp-service-port"If the appProtocol equals TCP then Flagger will treat this as a Canary deployment for a TCP service. When it creates the VirtualService document it will add a TCP section to route requests between the primary and canary services. See Istio documentation for more information on this spec.
The resulting VirtualService will include a tcp section similar to what is shown below:
tcp:
- route:
- destination:
host: tcp-service-primary
port:
number: 7070
weight: 100
- destination:
host: tcp-service-canary
port:
number: 7070
weight: 0Once the Canary analysis begins, Flagger will be able to adjust the weights inside of this tcp section to advance the Canary deployment until it either runs into an error (and is halted) or it successfully reaches the end of the analysis and is Promoted.
It is also important to note that if you set appProtocol to anything other than TCP, for example if you set it to HTTP, it will perform the Canary and treat it as an HTTP service. The same remains true if you do not set appProtocol at all. It will ONLY treat a Canary as a TCP service if appProtocal equals TCP.
Gitbook Tutorials Keda Scaledobject (`docs/gitbook/tutorials/keda-scaledobject.md`)
Canary analysis with KEDA ScaledObjects
This guide shows you how to use Flagger with KEDA ScaledObjects to autoscale workloads during a Canary analysis run.
We will be using a Blue/Green deployment strategy with the Kubernetes provider for the sake of this tutorial, but
you can use any deployment strategy combined with any supported provider.
Prerequisites
Flagger requires a Kubernetes cluster v1.16 or newer. For this tutorial, we'll need KEDA 2.7.1 or newer.
Install KEDA:
helm repo add kedacore https://kedacore.github.io/charts
kubectl create namespace keda
helm install keda kedacore/keda --namespace kedaInstall Flagger:
helm repo add flagger https://flagger.app
kubectl create namespace flagger
helm upgrade -i flagger flagger/flagger \
--namespace flagger \
--set prometheus.install=true \
--set meshProvider=kubernetesBootstrap
Flagger takes a Kubernetes deployment and a KEDA ScaledObject targeting the deployment. It then creates a series of objects
(Kubernetes deployments, ClusterIP services and another KEDA ScaledObject targeting the created Deployment).
These objects expose the application inside the mesh and drive the Canary analysis and Blue/Green promotion.
Create a test namespace:
kubectl create ns testCreate a deployment named podinfo:
kubectl apply -n test -f https://raw.githubusercontent.com/fluxcd/flagger/main/kustomize/podinfo/deployment.yamlDeploy the load testing service to generate traffic during the analysis:
kubectl apply -k https://github.com/fluxcd/flagger//kustomize/tester?ref=mainCreate a ScaledObject which targets the podinfo deployment and uses Prometheus as a trigger:
apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
name: podinfo-so
namespace: test
spec:
scaleTargetRef:
name: podinfo
pollingInterval: 10
cooldownPeriod: 20
minReplicaCount: 1
maxReplicaCount: 3
triggers:
- type: prometheus
metadata:
name: prom-trigger
serverAddress: http://flagger-prometheus.flagger:9090
metricName: http_requests_total
query: sum(rate(http_requests_total{ app="podinfo" }[30s]))
threshold: '5'Create a canary custom resource for the podinfo deployment:
/* Detailed source-code truncated for AI context efficiency. */Save the above resource as podinfo-canary.yaml and then apply it:
kubectl apply -f ./podinfo-canary.yamlAfter a couple of seconds Flagger will create the canary objects:
# applied
deployment.apps/podinfo
scaledobject.keda.sh/podinfo-so
canary.flagger.app/podinfo
# generated
deployment.apps/podinfo-primary
horizontalpodautoscaler.autoscaling/podinfo-primary
service/podinfo
service/podinfo-canary
service/podinfo-primary
scaledobject.keda.sh/podinfo-so-primaryWe refer to our ScaledObject for the canary deployment using .spec.autoscalerRef. Flagger will use this to generate a ScaledObject which will scale the primary deployment.
By default, Flagger will try to guess the query to use for the primary ScaledObject, by replacing all mentions of .spec.targetRef.Name and {.spec.targetRef.Name}-canary
with {.spec.targetRef.Name}-primary, for all triggers.
For eg, if your ScaledObject has a trigger query defined as: sum(rate(http_requests_total{ app="podinfo" }[30s])) or sum(rate(http_requests_total{ app="podinfo-primary" }[30s])), then the primary ScaledObject will have the same trigger with a query defined as sum(rate(http_requests_total{ app="podinfo-primary" }[30s])).
If, the generated query does not meet your requirements, you can specify the query for autoscaling the primary deployment explicitly using.spec.autoscalerRef.primaryScalerQueries, which lets you define a query for each trigger. Please note that, your ScaledObject's .spec.triggers[@].name must
not be blank, as Flagger needs that to identify each trigger uniquely.
In the situation when it is desired to have different scaling replica configuration between the canary and primary deployment ScaledObject you can use
the .spec.autoscalerRef.primaryScalerReplicas to override these values for the generated primary ScaledObject.
After the boostrap, the podinfo deployment will be scaled to zero and the traffic to podinfo.test will be routed to the primary pods. To keep the podinfo deployment
at 0 replicas and pause auto scaling, Flagger will add an annotation to your ScaledObject: autoscaling.keda.sh/paused-replicas: 0.
During the canary analysis, the annotation is removed, to enable auto scaling for the podinfo deployment.
The podinfo-canary.test address can be used to target directly the canary pods.
When the canary analysis starts, Flagger will call the pre-rollout webhooks before routing traffic to the canary. The Blue/Green deployment will run for five iterations while validating the HTTP metrics and rollout hooks every 15 seconds.
Automated Blue/Green promotion
Trigger a deployment by updating the container image:
kubectl -n test set image deployment/podinfo \
podinfod=ghcr.io/stefanprodan/podinfo:6.0.1Flagger detects that the deployment revision changed and starts a new rollout:
kubectl -n test describe canary/podinfo
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
Advance podinfo.test canary iteration 4/10
Advance podinfo.test canary iteration 5/10
Advance podinfo.test canary iteration 6/10
Advance podinfo.test canary iteration 7/10
Advance podinfo.test canary iteration 8/10
Advance podinfo.test canary iteration 9/10
Advance podinfo.test canary iteration 10/10
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.testNote that if you apply new changes to the deployment during the canary analysis, Flagger will restart the analysis.
You can monitor all canaries with:
watch kubectl get canaries --all-namespaces
NAMESPACE NAME STATUS WEIGHT LASTTRANSITIONTIME
test podinfo Progressing 100 2019-06-16T14:05:07ZYou can monitor the scaling of the deployments with:
watch kubectl -n test get deploy podinfo
NAME READY UP-TO-DATE AVAILABLE AGE
flagger-loadtester 1/1 1 1 4m21s
podinfo 3/3 3 3 4m28s
podinfo-primary 3/3 3 3 3m14sYou can mointor how Flagger edits the annotations of your ScaledObject with:
watch "kubectl get -n test scaledobjects podinfo-so -o=jsonpath='{.metadata.annotations}'"Gitbook Tutorials Knative Progressive Delivery (`docs/gitbook/tutorials/knative-progressive-delivery.md`)
Knative Canary Deployments
This guide shows you how to use Knative and Flagger to automate canary deployments.
Prerequisites
Flagger requires a Kubernetes cluster v1.19 or newer and a Knative Serving installation that supports
the resources with serving.knative.dev/v1 as their API version.
Install Knative v1.17.0:
kubectl apply -f https://github.com/knative/serving/releases/download/knative-v1.17.0/serving-crds.yaml
kubectl apply -f https://github.com/knative/serving/releases/download/knative-v1.17.0/serving-core.yaml
kubectl apply -f https://github.com/knative/net-kourier/releases/download/knative-v1.17.0/kourier.yaml
kubectl patch configmap/config-network \
--namespace knative-serving \
--type merge \
--patch '{"data":{"ingress-class":"kourier.ingress.networking.knative.dev"}}'Install Flagger in the flagger-system namespace:
kubectl apply -k github.com/fluxcd/flagger//kustomize/knativeCreate a namespace for your Kntive Service:
kubectl create namespace testCreate a Knative Service that deploys podinfo:
apiVersion: serving.knative.dev/v1
kind: Service
metadata:
name: podinfo
namespace: test
spec:
template:
spec:
containers:
- image: ghcr.io/stefanprodan/podinfo:6.0.0
ports:
- containerPort: 9898
protocol: TCP
command:
- ./podinfo
- --port=9898
- --port-metrics=9797
- --grpc-port=9999
- --grpc-service-name=podinfo
- --level=info
- --random-delay=false
- --random-error=falseDeploy the load testing service to generate traffic during the canary analysis:
kubectl apply -k https://github.com/fluxcd/flagger//kustomize/tester?ref=mainCreate a Canary custom resource:
apiVersion: flagger.app/v1beta1
kind: Canary
metadata:
name: podinfo
namespace: test
spec:
provider: knative
# knative service ref
targetRef:
apiVersion: serving.knative.dev/v1
kind: Service
name: podinfo
# the maximum time in seconds for the canary deployment
# to make progress before it is rollback (default 600s)
progressDeadlineSeconds: 60
analysis:
# schedule interval (default 60s)
interval: 15s
# max number of failed metric checks before rollback
threshold: 15
# max traffic percentage routed to canary
maxWeight: 50
# canary increment step
# percentage (0-100)
stepWeight: 10
metrics:
- name: request-success-rate
# min success rate (non-5xx responses)
# percentage (0-100)
thresholdRange:
min: 99
interval: 1m
- name: request-duration
# milliseconds
thresholdRange:
max: 500
interval: 1m
webhooks:
- name: load-test
url: http://flagger-loadtester.test/
timeout: 5s
metadata:
type: cmd
cmd: "hey -z 1m -q 5 -c 2 http://podinfo.test"
logCmdOutput: "true"Note: Please note that for a Canary resource with
.spec.providerset toknative, the resource is only valid if the.spec.targetRef.kindisServiceand.spec.targetRef.apiVersionisserving.knative.dev/v1.
Save the above resource as podinfo-canary.yaml and then apply it:
kubectl apply -f ./podinfo-canary.yamlWhen 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 minute.
After a couple of seconds Flagger will make the following changes the Knative Service podinfo:
- Add an annotation to the object with the name
flagger.app/primary-revision. - Modify the
.spec.trafficsection of the object such that it can manipulate the traffic spread between
the primary and canary Knative Revision.
Automated canary promotion
Trigger a canary deployment by updating the container image:
kubectl -n test patch services.serving podinfo --type=json \
-p '[{"op": "replace", "path": "/spec/template/spec/containers/0/image", "value": "ghcr.io/stefanprodan/podinfo:6.0.1"}]'Flagger detects that the deployment revision changed and starts a new rollout:
kubectl -n test describe canary/podinfo
Status:
Canary Weight: 0
Failed Checks: 0
Phase: Succeeded
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Synced 3m flagger New revision detected podinfo.test
Normal Synced 3m flagger Scaling up podinfo.test
Normal Synced 3m flagger Advance podinfo.test canary weight 5
Normal Synced 3m flagger Advance podinfo.test canary weight 10
Normal Synced 3m flagger Advance podinfo.test canary weight 15
Normal Synced 2m flagger Advance podinfo.test canary weight 20
Normal Synced 2m flagger Advance podinfo.test canary weight 25
Normal Synced 1m flagger Advance podinfo.test canary weight 30
Normal Synced 1m flagger Advance podinfo.test canary weight 35
Normal Synced 55s flagger Advance podinfo.test canary weight 40
Normal Synced 45s flagger Advance podinfo.test canary weight 45
Normal Synced 35s flagger Advance podinfo.test canary weight 50
Normal Synced 25s flagger Copying podinfo.test template spec to podinfo-primary.test
Normal Synced 5s flagger Promotion completed! Scaling down podinfo.testA canary deployment is triggered everytime a new Knative Revision is created.
Note that if you apply new changes to the Knative Service during the canary analysis, Flagger will restart the analysis.
You can monitor how Flagger progressively changes the Knative Service object to spread traffic between Knative Revisions:
watch kubectl get httproute -n test podinfo -o=jsonpath='{.spec.traffic}'You can monitor all canaries with:
watch kubectl get canaries --all-namespaces
NAMESPACE NAME STATUS WEIGHT LASTTRANSITIONTIME
test podinfo Progressing 15 2025-03-16T14:05:07Z
prod frontend Succeeded 0 2025-03-16T16:15:07Z
prod backend Failed 0 2025-03-16T17:05:07ZAutomated rollback
During the canary analysis you can generate HTTP 500 errors and high latency to test if Flagger pauses the rollout.
Trigger another canary deployment:
kubectl -n test patch services.serving podinfo --type=json \
-p '[{"op": "replace", "path": "/spec/template/spec/containers/0/image", "value": "ghcr.io/stefanprodan/podinfo:6.0.2"}]'Exec into the load tester pod with:
kubectl -n test exec -it flagger-loadtester-xx-xx shGenerate HTTP 500 errors:
watch curl http://podinfo-canary:9898/status/500Generate latency:
watch curl http://podinfo-canary:9898/delay/1When the number of failed checks reaches the canary analysis threshold, the traffic is routed back to the primary
Knative Revision and the rollout is marked as failed.
kubectl -n test describe canary/podinfo
Status:
Canary Weight: 0
Failed Checks: 10
Phase: Failed
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Synced 3m flagger Starting canary deployment for podinfo.test
Normal Synced 3m flagger Advance podinfo.test canary weight 5
Normal Synced 3m flagger Advance podinfo.test canary weight 10
Normal Synced 3m flagger Advance podinfo.test canary weight 15
Normal Synced 3m flagger Halt podinfo.test advancement error rate 69.17% > 1%
Normal Synced 2m flagger Halt podinfo.test advancement error rate 61.39% > 1%
Normal Synced 2m flagger Halt podinfo.test advancement error rate 55.06% > 1%
Normal Synced 2m flagger Halt podinfo.test advancement error rate 47.00% > 1%
Normal Synced 2m flagger (combined from similar events): Halt podinfo.test advancement error rate 38.08% > 1%
Warning Synced 1m flagger Rolling back podinfo.test failed checks threshold reached 10
Warning Synced 1m flagger Canary failed! Scaling down podinfo.testGitbook Tutorials Kubernetes Blue Green (`docs/gitbook/tutorials/kubernetes-blue-green.md`)
Blue/Green Deployments
This guide shows you how to automate Blue/Green deployments with Flagger and Kubernetes.
For applications that are not deployed on a service mesh, Flagger can orchestrate Blue/Green style deployments with Kubernetes L4 networking. When using a service mesh blue/green can be used as specified here.
Prerequisites
Flagger requires a Kubernetes cluster v1.16 or newer.
Install Flagger and the Prometheus add-on:
helm repo add flagger https://flagger.app
helm upgrade -i flagger flagger/flagger \
--namespace flagger \
--set prometheus.install=true \
--set meshProvider=kubernetesIf you already have a Prometheus instance running in your cluster, you can point Flagger to the ClusterIP service with:
helm upgrade -i flagger flagger/flagger \
--namespace flagger \
--set metricsServer=http://prometheus.monitoring:9090Optionally you can enable Slack notifications:
helm upgrade -i flagger flagger/flagger \
--reuse-values \
--namespace flagger \
--set slack.url=https://hooks.slack.com/services/YOUR/SLACK/WEBHOOK \
--set slack.channel=general \
--set slack.user=flaggerBootstrap
Flagger takes a Kubernetes deployment and optionally a horizontal pod autoscaler (HPA), then creates a series of objects (Kubernetes deployment and ClusterIP services). These objects expose the application inside the cluster and drive the canary analysis and Blue/Green promotion.
Create a test namespace:
kubectl create ns testCreate a deployment and a horizontal pod autoscaler:
kubectl apply -k https://github.com/fluxcd/flagger//kustomize/podinfo?ref=mainDeploy the load testing service to generate traffic during the analysis:
helm upgrade -i flagger-loadtester flagger/loadtester \
--namespace=testCreate a canary custom resource:
/* Detailed source-code truncated for AI context efficiency. */The above configuration will run an analysis for five minutes.
Save the above resource as podinfo-canary.yaml and then apply it:
kubectl apply -f ./podinfo-canary.yamlAfter a couple of seconds Flagger will create the canary objects:
# applied
deployment.apps/podinfo
horizontalpodautoscaler.autoscaling/podinfo
canary.flagger.app/podinfo
# generated
deployment.apps/podinfo-primary
horizontalpodautoscaler.autoscaling/podinfo-primary
service/podinfo
service/podinfo-canary
service/podinfo-primaryBlue/Green scenario:
on bootstrap, Flagger will create three ClusterIP services (
app-primary,app-canary,app)and a shadow deployment named
app-primarythat represents the blue versionwhen a new version is detected, Flagger would scale up the green version and run the conformance tests
(the tests should target the
app-canaryClusterIP service to reach the green version)if the conformance tests are passing, Flagger would start the load tests and validate them with custom Prometheus queries
if the load test analysis is successful, Flagger will promote the new version to
app-primaryand scale down the green version
Automated Blue/Green promotion
Trigger a deployment by updating the container image:
kubectl -n test set image deployment/podinfo \
podinfod=ghcr.io/stefanprodan/podinfo:6.0.1Flagger detects that the deployment revision changed and starts a new rollout:
kubectl -n test describe canary/podinfo
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
Advance podinfo.test canary iteration 4/10
Advance podinfo.test canary iteration 5/10
Advance podinfo.test canary iteration 6/10
Advance podinfo.test canary iteration 7/10
Advance podinfo.test canary iteration 8/10
Advance podinfo.test canary iteration 9/10
Advance podinfo.test canary iteration 10/10
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.testNote that if you apply new changes to the deployment during the canary analysis, Flagger will restart the analysis.
You can monitor all canaries with:
watch kubectl get canaries --all-namespaces
NAMESPACE NAME STATUS WEIGHT LASTTRANSITIONTIME
test podinfo Progressing 100 2019-06-16T14:05:07Z
prod frontend Succeeded 0 2019-06-15T16:15:07Z
prod backend Failed 0 2019-06-14T17:05:07ZAutomated rollback
During the analysis you can generate HTTP 500 errors and high latency to test Flagger's rollback.
Exec into the load tester pod with:
kubectl -n test exec -it flagger-loadtester-xx-xx shGenerate HTTP 500 errors:
watch curl http://podinfo-canary.test:9898/status/500Generate latency:
watch curl http://podinfo-canary.test:9898/delay/1When the number of failed checks reaches the analysis threshold, the green version is scaled to zero and the rollout is marked as failed.
kubectl -n test describe canary/podinfo
Status:
Failed Checks: 2
Phase: Failed
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Synced 3m flagger New revision detected podinfo.test
Normal Synced 3m flagger Advance podinfo.test canary iteration 1/10
Normal Synced 3m flagger Advance podinfo.test canary iteration 2/10
Normal Synced 3m flagger Advance podinfo.test canary iteration 3/10
Normal Synced 3m flagger Halt podinfo.test advancement success rate 69.17% < 99%
Normal Synced 2m flagger Halt podinfo.test advancement success rate 61.39% < 99%
Warning Synced 2m flagger Rolling back podinfo.test failed checks threshold reached 2
Warning Synced 1m flagger Canary failed! Scaling down podinfo.testCustom metrics
The analysis can be extended with Prometheus queries. The demo app is instrumented with Prometheus so you can create a custom check that will use the HTTP request duration histogram to validate the canary (green version).
Create a metric template and apply it on the cluster:
apiVersion: flagger.app/v1beta1
kind: MetricTemplate
metadata:
name: not-found-percentage
namespace: test
spec:
provider:
type: prometheus
address: http://flagger-prometheus.flagger:9090
query: |
100 - sum(
rate(
http_request_duration_seconds_count{
kubernetes_namespace="{{ namespace }}",
kubernetes_pod_name=~"{{ target }}-[0-9a-zA-Z]+(-[0-9a-zA-Z]+)"
status!="{{ interval }}"
}[1m]
)
)
/
sum(
rate(
http_request_duration_seconds_count{
kubernetes_namespace="{{ namespace }}",
kubernetes_pod_name=~"{{ target }}-[0-9a-zA-Z]+(-[0-9a-zA-Z]+)"
}[{{ interval }}]
)
) * 100Edit the canary analysis and add the following metric:
analysis:
metrics:
- name: "404s percentage"
templateRef:
name: not-found-percentage
thresholdRange:
max: 5
interval: 1mThe above configuration validates the canary (green version) by checking if the HTTP 404 req/sec percentage is below 5 percent of the total traffic. If the 404s rate reaches the 5% threshold, then the rollout is rolled back.
Trigger a deployment by updating the container image:
kubectl -n test set image deployment/podinfo \
podinfod=ghcr.io/stefanprodan/podinfo:6.0.3Generate 404s:
watch curl http://podinfo-canary.test:9898/status/400Watch Flagger logs:
kubectl -n flagger logs deployment/flagger -f | jq .msg
New revision detected podinfo.test
Scaling up podinfo.test
Advance podinfo.test canary iteration 1/10
Halt podinfo.test advancement 404s percentage 6.20 > 5
Halt podinfo.test advancement 404s percentage 6.45 > 5
Rolling back podinfo.test failed checks threshold reached 2
Canary failed! Scaling down podinfo.testIf you have alerting configured, Flagger will send a notification with the reason why the canary failed.
Conformance Testing with Helm
Flagger comes with a testing service that can run Helm tests when configured as a pre-rollout webhook.
Deploy the Helm test runner in the kube-system namespace using the tiller service account:
helm repo add flagger https://flagger.app
helm upgrade -i flagger-helmtester flagger/loadtester \
--namespace=kube-system \
--set serviceAccountName=tillerWhen deployed the Helm tester API will be available at http://flagger-helmtester.kube-system/.
Add a helm test pre-rollout hook to your chart:
analysis:
webhooks:
- name: "conformance testing"
type: pre-rollout
url: http://flagger-helmtester.kube-system/
timeout: 3m
metadata:
type: "helm"
cmd: "test {{ .Release.Name }} --cleanup"When the canary analysis starts, Flagger will call the pre-rollout webhooks. If the helm test fails, Flagger will retry until the analysis threshold is reached and the canary is rolled back.
For an in-depth look at the analysis process read the usage docs.
Gitbook Tutorials Kuma Progressive Delivery (`docs/gitbook/tutorials/kuma-progressive-delivery.md`)
Kuma Canary Deployments
This guide shows you how to use Kuma and Flagger to automate canary deployments.
Prerequisites
Flagger requires a Kubernetes cluster v1.19 or newer and Kuma 1.7 or newer.
Install Kuma and Prometheus (part of Kuma Metrics):
kumactl install control-plane | kubectl apply -f -
kumactl install observability --components "grafana,prometheus" | kubectl apply -f -Install Flagger in the kong-mesh-system namespace:
kubectl apply -k github.com/fluxcd/flagger//kustomize/kumaBootstrap
Flagger takes a Kubernetes deployment and optionally a horizontal pod autoscaler (HPA),
then creates a series of objects (Kubernetes deployments, ClusterIP services and Kuma TrafficRoute).
These objects expose the application inside the mesh and drive the canary analysis and promotion.
Create a test namespace and enable Kuma sidecar injection:
kubectl create ns test
kubectl annotate namespace test kuma.io/sidecar-injection=enabledInstall the load testing service to generate traffic during the canary analysis:
kubectl apply -k https://github.com/fluxcd/flagger//kustomize/tester?ref=mainCreate a deployment and a horizontal pod autoscaler:
kubectl apply -k https://github.com/fluxcd/flagger//kustomize/podinfo?ref=mainCreate a canary custom resource for the podinfo deployment:
apiVersion: flagger.app/v1beta1
kind: Canary
metadata:
name: podinfo
namespace: test
annotations:
kuma.io/mesh: default
spec:
targetRef:
apiVersion: apps/v1
kind: Deployment
name: podinfo
progressDeadlineSeconds: 60
service:
port: 9898
targetPort: 9898
apex:
annotations:
9898.service.kuma.io/protocol: "http"
canary:
annotations:
9898.service.kuma.io/protocol: "http"
primary:
annotations:
9898.service.kuma.io/protocol: "http"
analysis:
# 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
metrics:
- name: request-success-rate
threshold: 99
interval: 1m
- name: request-duration
threshold: 500
interval: 30s
webhooks:
- name: acceptance-test
type: pre-rollout
url: http://flagger-loadtester.test/
timeout: 30s
metadata:
type: bash
cmd: "curl -sd 'test' http://podinfo-canary.test: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-canary.test:9898/"Save the above resource as podinfo-canary.yaml and then apply it:
kubectl apply -f ./podinfo-canary.yamlWhen 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:
# 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
trafficroutes.kuma.io/podinfoAfter the bootstrap, 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.
Trigger a canary deployment by updating the container image:
kubectl -n test set image deployment/podinfo \
podinfod=ghcr.io/stefanprodan/podinfo:6.0.1Flagger detects that the deployment revision changed and starts a new rollout:
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.testNote 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:
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:07ZAutomated 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:
kubectl -n test set image deployment/podinfo \
podinfod=ghcr.io/stefanprodan/podinfo:6.0.2Exec into the load tester pod with:
kubectl -n test exec -it flagger-loadtester-xx-xx shGenerate HTTP 500 errors:
watch -n 1 curl http://podinfo-canary.test:9898/status/500Generate latency:
watch -n 1 curl http://podinfo-canary.test:9898/delay/1When 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.
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.testThe above procedures can be extended with custom metrics checks, webhooks, manual promotion approval and Slack or MS Teams notifications.
Gitbook Tutorials Linkerd Progressive Delivery (`docs/gitbook/tutorials/linkerd-progressive-delivery.md`)
Linkerd Canary Deployments
This guide shows you how to use Linkerd and Flagger to automate canary deployments.
Prerequisites
Flagger requires a Kubernetes cluster v1.21 or newer and Linkerd 2.14 or newer.
Install Linkerd and Prometheus (part of Linkerd Viz):
# The CRDs need to be installed beforehand
linkerd install --crds | kubectl apply -f -
linkerd install | kubectl apply -f -
linkerd viz install | kubectl apply -f -
# For linkerd versions 2.12 and later, the SMI extension needs to be install in
# order to enable TrafficSplits
curl -sL https://linkerd.github.io/linkerd-smi/install | sh
linkerd smi install | kubectl apply -f -Install Flagger in the flagger-system namespace:
kubectl apply -k github.com/fluxcd/flagger//kustomize/linkerdIf you prefer Helm, these are the commands to install Linkerd, Linkerd Viz,
Linkerd-SMI and Flagger:
helm repo add linkerd https://helm.linkerd.io/stable
helm install linkerd-crds linkerd/linkerd-crds -n linkerd --create-namespace
# See https://linkerd.io/2/tasks/generate-certificates/ for how to generate the
# certs referred below
helm install linkerd-control-plane linkerd/linkerd-control-plane \
-n linkerd \
--set-file identityTrustAnchorsPEM=ca.crt \
--set-file identity.issuer.tls.crtPEM=issuer.crt \
--set-file identity.issuer.tls.keyPEM=issuer.key \
helm install linkerd-viz linkerd/linkerd-viz -n linkerd-viz --create-namespace
helm install flagger flagger/flagger \
--n flagger-system \
--set meshProvider=gatewayapi:v1beta1 \
--set metricsServer=http://prometheus.linkerd-viz:9090 \
--set linkerdAuthPolicy.create=trueBootstrap
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:
kubectl create ns test
kubectl annotate namespace test linkerd.io/inject=enabledInstall the load testing service to generate traffic during the canary analysis:
kubectl apply -k https://github.com/fluxcd/flagger//kustomize/tester?ref=mainCreate a deployment and a horizontal pod autoscaler:
kubectl apply -k https://github.com/fluxcd/flagger//kustomize/podinfo?ref=mainCreate a metrics template and canary custom resources for the podinfo deployment:
/* Detailed source-code truncated for AI context efficiency. */Save the above resource as podinfo-canary.yaml and then apply it:
kubectl apply -f ./podinfo-canary.yamlWhen 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:
# 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/podinfoAfter the bootstrap, 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.
Trigger a canary deployment by updating the container image:
kubectl -n test set image deployment/podinfo \
podinfod=ghcr.io/stefanprodan/podinfo:6.0.1Flagger detects that the deployment revision changed and starts a new rollout:
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.testNote 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:
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:07ZAutomated 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:
kubectl -n test set image deployment/podinfo \
podinfod=ghcr.io/stefanprodan/podinfo:6.0.2Exec into the load tester pod with:
kubectl -n test exec -it flagger-loadtester-xx-xx shGenerate HTTP 500 errors:
watch -n 1 curl http://podinfo-canary.test:9898/status/500Generate latency:
watch -n 1 curl http://podinfo-canary.test:9898/delay/1When 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.
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.testCustom 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:
analysis:
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]
)
)
* 100The 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:
kubectl -n test set image deployment/podinfo \
podinfod=ghcr.io/stefanprodan/podinfo:6.0.3Generate 404s:
watch -n 1 curl http://podinfo-canary:9898/status/404Watch Flagger logs:
kubectl -n flagger-system 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.testIf you have Slack configured, Flagger will send a notification with the reason why the canary failed.
Linkerd Ingress
There are two ingress controllers that are compatible with both Flagger and Linkerd: NGINX and Gloo.
Install NGINX:
helm upgrade -i nginx-ingress stable/nginx-ingress \
--namespace ingress-nginxCreate an ingress definition for podinfo that rewrites the incoming header to the internal service name (required by Linkerd):
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: podinfo
namespace: test
labels:
app: podinfo
annotations:
kubernetes.io/ingress.class: "nginx"
nginx.ingress.kubernetes.io/configuration-snippet: |
proxy_set_header l5d-dst-override $service_name.$namespace.svc.cluster.local:9898;
proxy_hide_header l5d-remote-ip;
proxy_hide_header l5d-server-id;
spec:
rules:
- host: app.example.com
http:
paths:
- backend:
serviceName: podinfo
servicePort: 9898When using an ingress controller, the Linkerd traffic split does not apply to incoming traffic since NGINX in running outside of the mesh. In order to run a canary analysis for a frontend app, Flagger creates a shadow ingress and sets the NGINX specific annotations.
A/B Testing
Besides weighted routing, Flagger can be configured to route traffic to the canary based on HTTP match conditions. In an A/B testing scenario, you'll be using HTTP headers or cookies to target a certain segment of your users. This is particularly useful for frontend applications that require session affinity.
Edit podinfo canary analysis, set the provider to nginx, add the ingress reference, remove the max/step weight and add the match conditions and iterations:
apiVersion: flagger.app/v1beta1
kind: Canary
metadata:
name: podinfo
namespace: test
spec:
# ingress reference
provider: nginx
ingressRef:
apiVersion: extensions/v1beta1
kind: Ingress
name: podinfo
targetRef:
apiVersion: apps/v1
kind: Deployment
name: podinfo
autoscalerRef:
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
name: podinfo
service:
# container port
port: 9898
analysis:
interval: 1m
threshold: 10
iterations: 10
match:
# curl -H 'X-Canary: always' http://app.example.com
- headers:
x-canary:
exact: "always"
# curl -b 'canary=always' http://app.example.com
- headers:
cookie:
exact: "canary"
# Linkerd Prometheus checks
metrics:
- name: request-success-rate
thresholdRange:
min: 99
interval: 1m
- name: request-duration
thresholdRange:
max: 500
interval: 30s
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 -H 'Cookie: canary=always' http://app.example.com"The above configuration will run an analysis for ten minutes targeting users that have a canary cookie set to always or those that call the service using the X-Canary: always header.
Note that the load test now targets the external address and uses the canary cookie.
Trigger a canary deployment by updating the container image:
kubectl -n test set image deployment/podinfo \
podinfod=ghcr.io/stefanprodan/podinfo:6.0.4Flagger detects that the deployment revision changed and starts the A/B testing:
kubectl -n test describe canary/podinfo
Events:
Starting canary deployment for podinfo.test
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
Advance podinfo.test canary iteration 4/10
Advance podinfo.test canary iteration 5/10
Advance podinfo.test canary iteration 6/10
Advance podinfo.test canary iteration 7/10
Advance podinfo.test canary iteration 8/10
Advance podinfo.test canary iteration 9/10
Advance podinfo.test canary iteration 10/10
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.testThe above procedure can be extended with custom metrics checks, webhooks, manual promotion approval and Slack or MS Teams notifications.
Gitbook Tutorials Nginx Progressive Delivery (`docs/gitbook/tutorials/nginx-progressive-delivery.md`)
NGINX Canary Deployments
This guide shows you how to use the NGINX ingress controller and Flagger to automate canary deployments and A/B testing.
Prerequisites
Flagger requires a Kubernetes cluster v1.19 or newer and NGINX ingress v1.0.2 or newer.
Install the NGINX ingress controller with Helm v3:
helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx
kubectl create ns ingress-nginx
helm upgrade -i ingress-nginx ingress-nginx/ingress-nginx \
--namespace ingress-nginx \
--set controller.metrics.enabled=true \
--set controller.podAnnotations."prometheus\.io/scrape"=true \
--set controller.podAnnotations."prometheus\.io/port"=10254Install Flagger and the Prometheus add-on in the same namespace as the ingress controller:
helm repo add flagger https://flagger.app
helm upgrade -i flagger flagger/flagger \
--namespace ingress-nginx \
--set prometheus.install=true \
--set meshProvider=nginxBootstrap
Flagger takes a Kubernetes deployment and optionally a horizontal pod autoscaler (HPA),
then creates a series of objects (Kubernetes deployments, ClusterIP services and canary ingress).
These objects expose the application outside the cluster and drive the canary analysis and promotion.
Create a test namespace:
kubectl create ns testCreate a deployment and a horizontal pod autoscaler:
kubectl apply -k https://github.com/fluxcd/flagger//kustomize/podinfo?ref=mainDeploy the load testing service to generate traffic during the canary analysis:
helm upgrade -i flagger-loadtester flagger/loadtester \
--namespace=testCreate an ingress definition (replace app.example.com with your own domain):
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: podinfo
namespace: test
labels:
app: podinfo
annotations:
kubernetes.io/ingress.class: "nginx"
spec:
rules:
- host: "app.example.com"
http:
paths:
- pathType: Prefix
path: "/"
backend:
service:
name: podinfo
port:
number: 80Save the above resource as podinfo-ingress.yaml and then apply it:
kubectl apply -f ./podinfo-ingress.yamlCreate a canary custom resource (replace app.example.com with your own domain):
/* Detailed source-code truncated for AI context efficiency. */Save the above resource as podinfo-canary.yaml and then apply it:
kubectl apply -f ./podinfo-canary.yamlAfter a couple of seconds Flagger will create the canary objects:
# 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
ingresses.extensions/podinfo-canaryAutomated 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 or MS Teams.
Trigger a canary deployment by updating the container image:
kubectl -n test set image deployment/podinfo \
podinfod=ghcr.io/stefanprodan/podinfo:6.0.1Flagger detects that the deployment revision changed and starts a new rollout:
/* Detailed source-code truncated for AI context efficiency. */Note that if you apply new changes to the deployment during the canary analysis, Flagger will restart the analysis.
You can monitor all canaries with:
watch kubectl get canaries --all-namespaces
NAMESPACE NAME STATUS WEIGHT LASTTRANSITIONTIME
test podinfo Progressing 15 2019-05-06T14:05:07Z
prod frontend Succeeded 0 2019-05-05T16:15:07Z
prod backend Failed 0 2019-05-04T17:05:07ZAutomated rollback
During the canary analysis you can generate HTTP 500 errors to test if Flagger pauses and rolls back the faulted version.
Trigger another canary deployment:
kubectl -n test set image deployment/podinfo \
podinfod=ghcr.io/stefanprodan/podinfo:6.0.2Generate HTTP 500 errors:
watch curl http://app.example.com/status/500When 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.
kubectl -n test describe canary/podinfo
Status:
Canary Weight: 0
Failed Checks: 10
Phase: Failed
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Synced 3m flagger Starting canary deployment for podinfo.test
Normal Synced 3m flagger Advance podinfo.test canary weight 5
Normal Synced 3m flagger Advance podinfo.test canary weight 10
Normal Synced 3m flagger Advance podinfo.test canary weight 15
Normal Synced 3m flagger Halt podinfo.test advancement success rate 69.17% < 99%
Normal Synced 2m flagger Halt podinfo.test advancement success rate 61.39% < 99%
Normal Synced 2m flagger Halt podinfo.test advancement success rate 55.06% < 99%
Normal Synced 2m flagger Halt podinfo.test advancement success rate 47.00% < 99%
Normal Synced 2m flagger (combined from similar events): Halt podinfo.test advancement success rate 38.08% < 99%
Warning Synced 1m flagger Rolling back podinfo.test failed checks threshold reached 10
Warning Synced 1m flagger Canary failed! Scaling down podinfo.testCustom metrics
The canary analysis can be extended with Prometheus queries.
The demo app is instrumented with Prometheus so you can create a custom check
that will use the HTTP request duration histogram to validate the canary.
Create a metric template and apply it on the cluster:
apiVersion: flagger.app/v1beta1
kind: MetricTemplate
metadata:
name: latency
namespace: test
spec:
provider:
type: prometheus
address: http://flagger-prometheus.ingress-nginx:9090
query: |
histogram_quantile(0.99,
sum(
rate(
http_request_duration_seconds_bucket{
kubernetes_namespace="{{ namespace }}",
kubernetes_pod_name=~"{{ target }}-[0-9a-zA-Z]+(-[0-9a-zA-Z]+)"
}[1m]
)
) by (le)
)Edit the canary analysis and add the latency check:
analysis:
metrics:
- name: "latency"
templateRef:
name: latency
thresholdRange:
max: 0.5
interval: 1mThe threshold is set to 500ms so if the average request duration in the last minute goes over
half a second then the analysis will fail and the canary will not be promoted.
Trigger a canary deployment by updating the container image:
kubectl -n test set image deployment/podinfo \
podinfod=ghcr.io/stefanprodan/podinfo:6.0.3Generate high response latency:
watch curl http://app.example.com/delay/2Watch Flagger logs:
kubectl -n nginx-ingress logs deployment/flagger -f | jq .msg
Starting canary deployment for podinfo.test
Advance podinfo.test canary weight 5
Advance podinfo.test canary weight 10
Advance podinfo.test canary weight 15
Halt podinfo.test advancement latency 1.20 > 0.5
Halt podinfo.test advancement latency 1.45 > 0.5
Halt podinfo.test advancement latency 1.60 > 0.5
Halt podinfo.test advancement latency 1.69 > 0.5
Halt podinfo.test advancement latency 1.70 > 0.5
Rolling back podinfo.test failed checks threshold reached 5
Canary failed! Scaling down podinfo.testIf you have alerting configured, Flagger will send a notification with the reason why the canary failed.
A/B Testing
Besides weighted routing, Flagger can be configured to route traffic to the canary based on HTTP match conditions.
In an A/B testing scenario, you'll be using HTTP headers or cookies to target a certain segment of your users.
This is particularly useful for frontend applications that require session affinity.
Edit the canary analysis, remove the max/step weight and add the match conditions and iterations:
analysis:
interval: 1m
threshold: 10
iterations: 10
match:
# curl -H 'X-Canary: insider' http://app.example.com
- headers:
x-canary:
exact: "insider"
# curl -b 'canary=always' http://app.example.com
- headers:
cookie:
exact: "canary"
metrics:
- name: request-success-rate
thresholdRange:
min: 99
interval: 1m
webhooks:
- name: load-test
url: http://flagger-loadtester.test/
timeout: 5s
metadata:
cmd: "hey -z 1m -q 10 -c 2 -H 'Cookie: canary=always' http://app.example.com/"The above configuration will run an analysis for ten minutes targeting users that have
a canary cookie set to always or those that call the service using the X-Canary: insider header.
Trigger a canary deployment by updating the container image:
kubectl -n test set image deployment/podinfo \
podinfod=ghcr.io/stefanprodan/podinfo:6.0.4Flagger detects that the deployment revision changed and starts the A/B testing:
/* Detailed source-code truncated for AI context efficiency. */The above procedure can be extended with
custom metrics checks,
webhooks,
manual promotion approval and
Slack or MS Teams notifications.
Gitbook Tutorials Prometheus Operator (`docs/gitbook/tutorials/prometheus-operator.md`)
Canary analysis with Prometheus Operator
This guide show you how to use
Prometheus Operator for canary analysis.
Prerequisites
Flagger requires a Kubernetes cluster v1.16 or newer and Prometheus Operator v0.40 or newer.
Install Prometheus Operator with Helm v3:
helm repo add prometheus-community https://prometheus-community.github.io/helm-charts
kubectl create ns monitoring
helm upgrade -i prometheus prometheus-community/kube-prometheus-stack \
--namespace monitoring \
--set prometheus.prometheusSpec.serviceMonitorSelectorNilUsesHelmValues=false \
--set fullnameOverride=prometheusThe prometheus.prometheusSpec.serviceMonitorSelectorNilUsesHelmValues=false
option allows Prometheus Operator to watch serviceMonitors outside of its namespace.
Install Flagger by setting the metrics server to Prometheus:
helm repo add flagger https://flagger.app
kubectl create ns flagger-system
helm upgrade -i flagger flagger/flagger \
--namespace flagger-system \
--set metricsServer=http://prometheus-prometheus.monitoring:9090 \
--set meshProvider=kubernetesInstall Flagger's tester:
helm upgrade -i loadtester flagger/loadtester \
--namespace flagger-systemInstall podinfo demo app:
helm repo add podinfo https://stefanprodan.github.io/podinfo
kubectl create ns test
helm upgrade -i podinfo podinfo/podinfo \
--namespace test \
--set service.enabled=falseService monitors
The demo app is instrumented with Prometheus,
so you can create a ServiceMonitor objects to scrape podinfo's metrics endpoint:
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
name: podinfo-canary
namespace: test
spec:
endpoints:
- path: /metrics
port: http
interval: 5s
selector:
matchLabels:
app: podinfo-canary
---
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
name: podinfo-primary
namespace: test
spec:
endpoints:
- path: /metrics
port: http
interval: 5s
selector:
matchLabels:
app: podinfoWe are setting interval: 5s to have a more aggressive scraping.
If you do not define it, you should use a longer interval in the Canary object.
Metric templates
Create a metric template to measure the HTTP requests error rate:
apiVersion: flagger.app/v1beta1
kind: MetricTemplate
metadata:
name: error-rate
namespace: test
spec:
provider:
address: http://prometheus-prometheus.monitoring:9090
type: prometheus
query: |
100 - rate(
http_requests_total{
namespace="{{ namespace }}",
job="{{ target }}-canary",
status!~"5.*"
}[{{ interval }}])
/
rate(
http_requests_total{
namespace="{{ namespace }}",
job="{{ target }}-canary"
}[{{ interval }}]
) * 100Create a metric template to measure the HTTP requests average duration:
apiVersion: flagger.app/v1beta1
kind: MetricTemplate
metadata:
name: latency
namespace: test
spec:
provider:
address: http://prometheus-prometheus.monitoring:9090
type: prometheus
query: |
histogram_quantile(0.99,
sum(
rate(
http_request_duration_seconds_bucket{
namespace="{{ namespace }}",
job="{{ target }}-canary"
}[{{ interval }}]
)
) by (le)
)Canary analysis
Using the metrics template you can configure the canary analysis with HTTP error rate and latency checks:
apiVersion: flagger.app/v1beta1
kind: Canary
metadata:
name: podinfo
namespace: test
spec:
provider: kubernetes
targetRef:
apiVersion: apps/v1
kind: Deployment
name: podinfo
progressDeadlineSeconds: 60
service:
port: 80
targetPort: http
name: podinfo
analysis:
interval: 30s
iterations: 10
threshold: 2
metrics:
- name: error-rate
templateRef:
name: error-rate
thresholdRange:
max: 1
interval: 30s
- name: latency
templateRef:
name: latency
thresholdRange:
max: 0.5
interval: 30s
webhooks:
- name: load-test
type: rollout
url: "http://loadtester.flagger-system/"
timeout: 5s
metadata:
type: cmd
cmd: "hey -z 1m -q 10 -c 2 http://podinfo-canary.test/"Based on the above specification, Flagger creates the primary and canary Kubernetes ClusterIP service.
During the canary analysis, Prometheus will scrape the canary service and Flagger will use the HTTP error rate
and latency queries to determine if the release should be promoted or rolled back.
Gitbook Tutorials Skipper Progressive Delivery (`docs/gitbook/tutorials/skipper-progressive-delivery.md`)
Skipper Canary Deployments
This guide shows you how to use the Skipper ingress controller and Flagger to automate canary deployments.
Prerequisites
Flagger requires a Kubernetes cluster v1.19 or newer and Skipper ingress v0.13 or newer.
Install Skipper ingress-controller using upstream definition.
Certain arguments are relevant:
- -enable-connection-metrics
- -histogram-metric-buckets=.01,1,10,100
- -kubernetes
- -kubernetes-in-cluster
- -kubernetes-path-mode=path-prefix
- -metrics-exp-decay-sample
- -metrics-flavour=prometheus
- -route-backend-metrics
- -route-backend-error-counters
- -route-response-metrics
- -serve-host-metrics
- -serve-route-metrics
- -whitelisted-healthcheck-cidr=0.0.0.0/0 # permit Kind source health checksInstall Flagger using kustomize:
kustomize build https://github.com/fluxcd/flagger/kustomize/kubernetes | kubectl apply -f -Bootstrap
Flagger takes a Kubernetes deployment and optionally a horizontal pod autoscaler (HPA),
then creates a series of objects (Kubernetes deployments, ClusterIP services and canary ingress).
These objects expose the application outside the cluster and drive the canary analysis and promotion.
Create a test namespace:
kubectl create ns testCreate a deployment and a horizontal pod autoscaler:
kubectl apply -k https://github.com/fluxcd/flagger//kustomize/podinfo?ref=mainDeploy the load testing service to generate traffic during the canary analysis:
helm upgrade -i flagger-loadtester flagger/loadtester \
--namespace=testCreate an ingress definition (replace app.example.com with your own domain):
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: podinfo
namespace: test
labels:
app: podinfo
annotations:
kubernetes.io/ingress.class: "skipper"
spec:
rules:
- host: "app.example.com"
http:
paths:
- pathType: Prefix
path: "/"
backend:
service:
name: podinfo
port:
number: 80Save the above resource as podinfo-ingress.yaml and then apply it:
kubectl apply -f ./podinfo-ingress.yamlCreate a canary custom resource (replace app.example.com with your own domain):
/* Detailed source-code truncated for AI context efficiency. */Save the above resource as podinfo-canary.yaml and then apply it:
kubectl apply -f ./podinfo-canary.yamlAfter a couple of seconds Flagger will create the canary objects:
# applied
deployment.apps/podinfo
horizontalpodautoscaler.autoscaling/podinfo
ingress.networking.k8s.io/podinfo-ingress
canary.flagger.app/podinfo
# generated
deployment.apps/podinfo-primary
horizontalpodautoscaler.autoscaling/podinfo-primary
service/podinfo
service/podinfo-canary
service/podinfo-primary
ingress.networking.k8s.io/podinfo-canaryAutomated 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 or MS Teams.
Trigger a canary deployment by updating the container image:
kubectl -n test set image deployment/podinfo \
podinfod=stefanprodan/podinfo:4.0.6Flagger detects that the deployment revision changed and starts a new rollout:
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
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
Routing all traffic to primary
Promotion completed! Scaling down podinfo.testNote that if you apply new changes to the deployment during the canary analysis, Flagger will restart the analysis.
You can monitor all canaries with:
watch kubectl get canaries --all-namespaces
NAMESPACE NAME STATUS WEIGHT LASTTRANSITIONTIME
test podinfo-2 Progressing 30 2020-08-14T12:32:12Z
test podinfo Succeeded 0 2020-08-14T11:23:88ZAutomated rollback
During the canary analysis you can generate HTTP 500 errors to test if Flagger pauses and rolls back the faulted version.
Trigger another canary deployment:
kubectl -n test set image deployment/podinfo \
podinfod=stefanprodan/podinfo:4.0.6Exec into the load tester pod with:
kubectl -n test exec -it deploy/flagger-loadtester bashGenerate HTTP 500 errors:
hey -z 1m -c 5 -q 5 http://app.example.com/status/500Generate latency:
watch -n 1 curl http://app.example.com/delay/1When 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.
kubectl -n flagger-system logs deploy/flagger -f | jq .msg
New revision detected! Scaling up podinfo.test
Canary deployment podinfo.test not ready: waiting for rollout to finish: 0 of 1 updated replicas are available
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
Advance podinfo.test canary weight 20
Halt podinfo.test advancement success rate 53.42% < 99%
Halt podinfo.test advancement success rate 53.19% < 99%
Halt podinfo.test advancement success rate 48.05% < 99%
Rolling back podinfo.test failed checks threshold reached 3
Canary failed! Scaling down podinfo.testCustom metrics
The canary analysis can be extended with Prometheus queries.
Create a metric template and apply it on the cluster:
apiVersion: flagger.app/v1beta1
kind: MetricTemplate
metadata:
name: latency
namespace: test
spec:
provider:
type: prometheus
address: http://flagger-prometheus.flagger-system:9090
query: |
histogram_quantile(0.99,
sum(
rate(
skipper_serve_route_duration_seconds_bucket{
route=~"{{ printf "kube(ew)?_%s__%s_canary__.*__%s_canary(_[0-9]+)?" namespace ingress service }}",
le="+Inf"
}[1m]
)
) by (le)
)Edit the canary analysis and add the latency check:
analysis:
metrics:
- name: "latency"
templateRef:
name: latency
thresholdRange:
max: 0.5
interval: 1mThe threshold is set to 500ms so if the average request duration in the last minute goes over half a second
then the analysis will fail and the canary will not be promoted.
Trigger a canary deployment by updating the container image:
kubectl -n test set image deployment/podinfo \
podinfod=stefanprodan/podinfo:4.0.6Generate high response latency:
watch curl http://app.example.com/delay/2Watch Flagger logs:
kubectl -n flagger-system logs deployment/flagger -f | jq .msg
Starting canary deployment for podinfo.test
Advance podinfo.test canary weight 5
Advance podinfo.test canary weight 10
Advance podinfo.test canary weight 15
Halt podinfo.test advancement latency 1.20 > 0.5
Halt podinfo.test advancement latency 1.45 > 0.5
Halt podinfo.test advancement latency 1.60 > 0.5
Halt podinfo.test advancement latency 1.69 > 0.5
Halt podinfo.test advancement latency 1.70 > 0.5
Rolling back podinfo.test failed checks threshold reached 5
Canary failed! Scaling down podinfo.testIf you have alerting configured, Flagger will send a notification with the reason why the canary failed.
Gitbook Tutorials Traefik Progressive Delivery (`docs/gitbook/tutorials/traefik-progressive-delivery.md`)
Traefik Canary Deployments
This guide shows you how to use the Traefik and Flagger to automate canary deployments.
Prerequisites
Flagger requires a Kubernetes cluster v1.16 or newer and Traefik v2.3 or newer.
Install Traefik with Helm v3:
helm repo add traefik https://helm.traefik.io/traefik
kubectl create ns traefik
cat <<EOF | helm upgrade -i traefik traefik/traefik --namespace traefik -f -
deployment:
podAnnotations:
prometheus.io/port: "9100"
prometheus.io/scrape: "true"
prometheus.io/path: "/metrics"
metrics:
prometheus:
entryPoint: metrics
EOFInstall Flagger and the Prometheus add-on in the same namespace as Traefik:
helm repo add flagger https://flagger.app
helm upgrade -i flagger flagger/flagger \
--namespace traefik \
--set prometheus.install=true \
--set meshProvider=traefikBootstrap
Flagger takes a Kubernetes deployment and optionally a horizontal pod autoscaler (HPA), then creates a series of objects (Kubernetes deployments, ClusterIP services and TraefikService). These objects expose the application outside the cluster and drive the canary analysis and promotion.
Create a test namespace:
kubectl create ns testCreate a deployment and a horizontal pod autoscaler:
kubectl apply -k https://github.com/fluxcd/flagger//kustomize/podinfo?ref=mainDeploy the load testing service to generate traffic during the canary analysis:
helm upgrade -i flagger-loadtester flagger/loadtester \
--namespace=testCreate Traefik IngressRoute that references TraefikService generated by Flagger (replace app.example.com with your own domain):
apiVersion: traefik.io/v1alpha1
kind: IngressRoute
metadata:
name: podinfo
namespace: test
spec:
entryPoints:
- web
routes:
- match: Host(`app.example.com`)
kind: Rule
services:
- name: podinfo
kind: TraefikService
port: 80Save the above resource as podinfo-ingressroute.yaml and then apply it:
kubectl apply -f ./podinfo-ingressroute.yamlCreate a canary custom resource (replace app.example.com with your own domain):
/* Detailed source-code truncated for AI context efficiency. */Save the above resource as podinfo-canary.yaml and then apply it:
kubectl apply -f ./podinfo-canary.yamlAfter a couple of seconds Flagger will create the canary objects:
# applied
deployment.apps/podinfo
horizontalpodautoscaler.autoscaling/podinfo
canary.flagger.app/podinfo
# generated
deployment.apps/podinfo-primary
horizontalpodautoscaler.autoscaling/podinfo-primary
service/podinfo
service/podinfo-canary
service/podinfo-primary
traefikservice.traefik.io/podinfoAutomated 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 or MS Teams.
Trigger a canary deployment by updating the container image:
kubectl -n test set image deployment/podinfo \
podinfod=stefanprodan/podinfo:4.0.6Flagger detects that the deployment revision changed and starts a new rollout:
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
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
Routing all traffic to primary
Promotion completed! Scaling down podinfo.testNote that if you apply new changes to the deployment during the canary analysis, Flagger will restart the analysis.
You can monitor all canaries with:
watch kubectl get canaries --all-namespaces
NAMESPACE NAME STATUS WEIGHT LASTTRANSITIONTIME
test podinfo-2 Progressing 30 2020-08-14T12:32:12Z
test podinfo Succeeded 0 2020-08-14T11:23:88ZAutomated rollback
During the canary analysis you can generate HTTP 500 errors to test if Flagger pauses and rolls back the faulted version.
Trigger another canary deployment:
kubectl -n test set image deployment/podinfo \
podinfod=stefanprodan/podinfo:4.0.6Exec into the load tester pod with:
kubectl -n test exec -it deploy/flagger-loadtester bashGenerate HTTP 500 errors:
hey -z 1m -c 5 -q 5 http://app.example.com/status/500Generate latency:
watch -n 1 curl http://app.example.com/delay/1When 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.
kubectl -n traefik logs deploy/flagger -f | jq .msg
New revision detected! Scaling up podinfo.test
Canary deployment podinfo.test not ready: waiting for rollout to finish: 0 of 1 updated replicas are available
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
Advance podinfo.test canary weight 20
Halt podinfo.test advancement success rate 53.42% < 99%
Halt podinfo.test advancement success rate 53.19% < 99%
Halt podinfo.test advancement success rate 48.05% < 99%
Rolling back podinfo.test failed checks threshold reached 3
Canary failed! Scaling down podinfo.testCustom metrics
The canary analysis can be extended with Prometheus queries.
Create a metric template and apply it on the cluster:
apiVersion: flagger.app/v1beta1
kind: MetricTemplate
metadata:
name: not-found-percentage
namespace: test
spec:
provider:
type: prometheus
address: http://flagger-prometheus.traefik:9090
query: |
sum(
rate(
traefik_service_request_duration_seconds_bucket{
service=~"{{ namespace }}-{{ target }}-canary-[0-9a-zA-Z-]+@kubernetescrd",
code!="404",
}[{{ interval }}]
)
)
/
sum(
rate(
traefik_service_request_duration_seconds_bucket{
service=~"{{ namespace }}-{{ target }}-canary-[0-9a-zA-Z-]+@kubernetescrd",
}[{{ interval }}]
)
) * 100Edit the canary analysis and add the not found error rate check:
analysis:
metrics:
- name: "404s percentage"
templateRef:
name: not-found-percentage
thresholdRange:
max: 5
interval: 1mThe above configuration validates the canary by checking if the HTTP 404 req/sec percentage is below 5 percent of the total traffic. If the 404s rate reaches the 5% threshold, then the canary fails.
Trigger a canary deployment by updating the container image:
kubectl -n test set image deployment/podinfo \
podinfod=stefanprodan/podinfo:4.0.6Generate 404s:
watch curl http://app.example.com/status/400Watch Flagger logs:
kubectl -n traefik logs deployment/flagger -f | jq .msg
Starting canary deployment for podinfo.test
Advance podinfo.test canary weight 5
Advance podinfo.test canary weight 10
Advance podinfo.test canary weight 15
Halt podinfo.test advancement 404s percentage 6.20 > 5
Halt podinfo.test advancement 404s percentage 6.45 > 5
Halt podinfo.test advancement 404s percentage 7.60 > 5
Halt podinfo.test advancement 404s percentage 8.69 > 5
Halt podinfo.test advancement 404s percentage 9.70 > 5
Rolling back podinfo.test failed checks threshold reached 5
Canary failed! Scaling down podinfo.testIf you have alerting configured, Flagger will send a notification with the reason why the canary failed.
For an in-depth look at the analysis process read the usage docs.
Gitbook Tutorials Zero Downtime Deployments (`docs/gitbook/tutorials/zero-downtime-deployments.md`)
Zero downtime deployments
This is a list of things you should consider when dealing with a high traffic production environment if you want to minimise the impact of rolling updates and downscaling.
Deployment strategy
Limit the number of unavailable pods during a rolling update:
apiVersion: apps/v1
kind: Deployment
spec:
progressDeadlineSeconds: 120
strategy:
type: RollingUpdate
rollingUpdate:
maxUnavailable: 0The default progress deadline for a deployment is ten minutes. You should consider adjusting this value to make the deployment process fail faster.
Liveness health check
You application should expose a HTTP endpoint that Kubernetes can call to determine if your app transitioned to a broken state from which it can't recover and needs to be restarted.
livenessProbe:
exec:
command:
- wget
- --quiet
- --tries=1
- --timeout=4
- --spider
- http://localhost:8080/healthz
timeoutSeconds: 5
initialDelaySeconds: 5If you've enabled mTLS, you'll have to use exec for liveness and readiness checks since kubelet is not part of the service mesh and doesn't have access to the TLS cert.
Readiness health check
You application should expose a HTTP endpoint that Kubernetes can call to determine if your app is ready to receive traffic.
readinessProbe:
exec:
command:
- wget
- --quiet
- --tries=1
- --timeout=4
- --spider
- http://localhost:8080/readyz
timeoutSeconds: 5
initialDelaySeconds: 5
periodSeconds: 5If your app depends on external services, you should check if those services are available before allowing Kubernetes to route traffic to an app instance. Keep in mind that the Envoy sidecar can have a slower startup than your app. This means that on application start you should retry for at least a couple of seconds any external connection.
Graceful shutdown
Before a pod gets terminated, Kubernetes sends a SIGTERM signal to every container and waits for period of time (30s by default) for all containers to exit gracefully. If your app doesn't handle the SIGTERM signal or if it doesn't exit within the grace period, Kubernetes will kill the container and any inflight requests that your app is processing will fail.
apiVersion: apps/v1
kind: Deployment
spec:
template:
spec:
terminationGracePeriodSeconds: 60
containers:
- name: app
lifecycle:
preStop:
exec:
command:
- sleep
- "10"Your app container should have a preStop hook that delays the container shutdown. This will allow the service mesh to drain the traffic and remove this pod from all other Envoy sidecars before your app becomes unavailable.
Delay Envoy shutdown
Even if your app reacts to SIGTERM and tries to complete the inflight requests before shutdown, that doesn't mean that the response will make it back to the caller. If the Envoy sidecar shuts down before your app, then the caller will receive a 503 error.
To mitigate this issue you can add a preStop hook to the Istio proxy and wait for the main app to exit before Envoy exits.
#!/bin/bash
set -e
if ! pidof envoy &>/dev/null; then
exit 0
fi
if ! pidof pilot-agent &>/dev/null; then
exit 0
fi
while [ $(netstat -plunt | grep tcp | grep -v envoy | wc -l | xargs) -ne 0 ]; do
sleep 1;
done
exit 0You'll have to build your own Envoy docker image with the above script and modify the Istio injection webhook with the preStop directive.
Thanks to Stono for his excellent tips on minimising 503s.
Resource requests and limits
Setting CPU and memory requests/limits for all workloads is a mandatory step if you're running a production system. Without limits your nodes could run out of memory or become unresponsive due to CPU exhausting. Without CPU and memory requests, the Kubernetes scheduler will not be able to make decisions about which nodes to place pods on.
apiVersion: apps/v1
kind: Deployment
spec:
template:
spec:
containers:
- name: app
resources:
limits:
cpu: 1000m
memory: 1Gi
requests:
cpu: 100m
memory: 128MiNote that without resource requests the horizontal pod autoscaler can't determine when to scale your app.
Autoscaling
A production environment should be able to handle traffic bursts without impacting the quality of service. This can be achieved with Kubernetes autoscaling capabilities. Autoscaling in Kubernetes has two dimensions: the Cluster Autoscaler that deals with node scaling operations and the Horizontal Pod Autoscaler that automatically scales the number of pods in a deployment.
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: app
minReplicas: 2
maxReplicas: 4
metrics:
- type: Resource
resource:
name: cpu
targetAverageValue: 900m
- type: Resource
resource:
name: memory
targetAverageValue: 768MiThe above HPA ensures your app will be scaled up before the pods reach the CPU or memory limits.
Ingress retries
To minimise the impact of downscaling operations you can make use of Envoy retry capabilities.
apiVersion: flagger.app/v1beta1
kind: Canary
spec:
service:
port: 9898
gateways:
- istio-system/public-gateway
hosts:
- app.example.com
retries:
attempts: 10
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.
Gitbook Usage Alerting (`docs/gitbook/usage/alerting.md`)
Alerting
Flagger can be configured to send alerts to various chat platforms.
You can define a global alert provider at install time or configure alerts on a per canary basis.
Global configuration
Slack
Slack Configuration
Flagger requires a custom webhook integration from slack, instead of the new slack app system.
The webhook can be generated by following the legacy slack documentation
Flagger configuration
Once the webhook has been generated. Flagger can be configured to send Slack notifications:
helm upgrade -i flagger flagger/flagger \
--set slack.url=https://hooks.slack.com/services/YOUR/SLACK/WEBHOOK \
--set slack.proxy=my-http-proxy.com \ # optional http/s proxy
--set slack.channel=general \
--set slack.user=flagger \
--set clusterName=my-clusterOnce configured with a Slack incoming webhook,
Flagger will post messages when a canary deployment has been initialised,
when a new revision has been detected and if the canary analysis failed or succeeded.
A canary deployment will be rolled back if the progress deadline exceeded
or if the analysis reached the maximum number of failed checks:
For using a Slack bot token, you should add token to a secret and use secretRef.
Microsoft Teams
Flagger can be configured to send notifications to Microsoft Teams:
helm upgrade -i flagger flagger/flagger \
--set msteams.url=https://outlook.office.com/webhook/YOUR/TEAMS/WEBHOOK \
--set msteams.proxy-url=my-http-proxy.com # optional http/s proxySimilar to Slack, Flagger alerts on canary analysis events:
Canary configuration
Configuring alerting globally has several limitations as it's not possible to specify different channels
or configure the verbosity on a per canary basis. To make the alerting move flexible,
the canary analysis can be extended with a list of alerts that reference an alert provider.
For each alert, users can configure the severity level. The alerts section overrides the global setting.
Slack example:
apiVersion: flagger.app/v1beta1
kind: AlertProvider
metadata:
name: on-call
namespace: flagger
spec:
type: slack
channel: on-call-alerts
username: flagger
# webhook address (ignored if secretRef is specified)
# or https://slack.com/api/chat.postMessage if you use token in the secret
address: https://hooks.slack.com/services/YOUR/SLACK/WEBHOOK
# optional http/s proxy
proxy: http://my-http-proxy.com
# secret containing the webhook address (optional)
secretRef:
name: on-call-url
---
apiVersion: v1
kind: Secret
metadata:
name: on-call-url
namespace: flagger
data:
address: <encoded-url>
token: <encoded-token>The alert provider type can be: slack, msteams, rocket or discord. When set to discord,
Flagger will use Slack formatting
and will append /slack to the Discord address.
When not specified, channel defaults to general and username defaults to flagger.
When secretRef is specified, the Kubernetes secret must contain a data field named address,
the address in the secret will take precedence over the address field in the provider spec.
The canary analysis can have a list of alerts, each alert referencing an alert provider:
analysis:
alerts:
- name: "on-call Slack"
severity: error
providerRef:
name: on-call
namespace: flagger
- name: "qa Discord"
severity: warn
providerRef:
name: qa-discord
- name: "dev MS Teams"
severity: info
providerRef:
name: dev-msteamsAlert fields:
- name (required)
- severity levels:
info,warn,error(default info) - providerRef.name alert provider name (required)
- providerRef.namespace alert provider namespace (defaults to the canary namespace)
When the severity is set to warn, Flagger will alert when waiting on manual confirmation or if the analysis fails.
When the severity is set to error, Flagger will alert only if the canary analysis fails.
To differentiate alerts based on the cluster name, you can configure Flagger with the -cluster-name=my-cluster
command flag, or with Helm --set clusterName=my-cluster.
Prometheus Alert Manager
You can use Alertmanager to trigger alerts when a canary deployment failed:
- alert: canary_rollback
expr: flagger_canary_status > 1
for: 1m
labels:
severity: warning
annotations:
summary: "Canary failed"
description: "Workload {{ $labels.name }} namespace {{ $labels.namespace }}"Gitbook Usage Deployment Strategies (`docs/gitbook/usage/deployment-strategies.md`)
Deployment Strategies
Flagger can run automated application analysis, promotion and rollback for the following deployment strategies:
- Canary Release (progressive traffic shifting)
- Istio, Linkerd, App Mesh, NGINX, Skipper, Contour, Gloo Edge, Traefik, Kuma, Gateway API, Apache APISIX, Knative
- A/B Testing (HTTP headers and cookies traffic routing)
- Istio, App Mesh, NGINX, Contour, Gloo Edge, Gateway API
- Blue/Green (traffic switching)
- Kubernetes CNI, Istio, Linkerd, App Mesh, NGINX, Contour, Gloo Edge, Gateway API
- Blue/Green Mirroring (traffic shadowing)
- Istio, Gateway API
- Canary Release with Session Affinity (progressive traffic shifting combined with cookie based routing)
- Istio, Gateway API
For Canary releases and A/B testing you'll need a Layer 7 traffic management solution like
a service mesh or an ingress controller. For Blue/Green deployments no service mesh or ingress controller is required.
A canary analysis 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
Canary Release
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.
The canary analysis runs periodically until it reaches the maximum traffic weight or the failed checks threshold.
Spec:
analysis:
# schedule interval (default 60s)
interval: 1m
# max number of failed metric checks before rollback
threshold: 10
# max traffic percentage routed to canary
# percentage (0-100)
maxWeight: 50
# canary increment step
# percentage (0-100)
stepWeight: 2
# promotion increment step (default 100)
# percentage (0-100)
stepWeightPromotion: 100
# deploy straight to production without
# the metrics and webhook checks
skipAnalysis: falseThe above analysis, if it succeeds, will run for 25 minutes while validating the HTTP metrics and webhooks every minute.
You can determine the minimum time it takes to validate and promote a canary deployment using this formula:
interval * (maxWeight / stepWeight)And the time it takes for a canary to be rollback when the metrics or webhook checks are failing:
interval * thresholdWhen stepWeightPromotion is specified, the promotion phase happens in stages, the traffic is routed back
to the primary pods in a progressive manner, the primary weight is increased until it reaches 100%.
In emergency cases, you may want to skip the analysis phase and ship changes directly to production.
At any time you can set the spec.skipAnalysis: true. When skip analysis is enabled,
Flagger checks if the canary deployment is healthy and promotes it without analysing it.
If an analysis is underway, Flagger cancels it and runs the promotion.
Gated canary promotion stages:
- scan for canary deployments
- check primary and canary deployment status
- halt advancement if a rolling update is underway
- halt advancement if pods are unhealthy
- call confirm-rollout webhooks and check results
- halt advancement if any hook returns a non HTTP 2xx result
- call pre-rollout webhooks and check results
- halt advancement if any hook returns a non HTTP 2xx result
- increment the failed checks counter
- increase canary traffic weight percentage from 0% to 2% (step weight)
- call rollout webhooks and check results
- check canary HTTP request success rate and latency
- halt advancement if any metric is under the specified threshold
- increment the failed checks counter
- check if the number of failed checks reached the threshold
- route all traffic to primary
- scale to zero the canary deployment and mark it as failed
- call post-rollout webhooks
- post the analysis result to Slack
- wait for the canary deployment to be updated and start over
- increase canary traffic weight by 2% (step weight) till it reaches 50% (max weight)
- halt advancement if any webhook call fails
- halt advancement while canary request success rate is under the threshold
- halt advancement while canary request duration P99 is over the threshold
- halt advancement while any custom metric check fails
- halt advancement if the primary or canary deployment becomes unhealthy
- halt advancement while canary deployment is being scaled up/down by HPA
- call confirm-promotion webhooks and check results
- halt advancement if any hook returns a non HTTP 2xx result
- promote canary to primary
- copy ConfigMaps and Secrets from canary to primary
- copy canary deployment spec template over primary
- wait for primary rolling update to finish
- halt advancement if pods are unhealthy
- route all traffic to primary
- scale to zero the canary deployment
- mark rollout as finished
- call post-rollout webhooks
- send notification with the canary analysis result
- wait for the canary deployment to be updated and start over
Rollout Weights
By default Flagger uses linear weight values for the promotion, with the start value,
the step and the maximum weight value in 0 to 100 range.
Example:
# canary.yaml
spec:
analysis:
maxWeight: 50
stepWeight: 20This configuration performs analysis starting from 20, increasing by 20 until weight goes above 50.
We would have steps (canary weight : primary weight):
- 20 (20 : 80)
- 40 (40 : 60)
- 60 (60 : 40)
- promotion
In order to enable non-linear promotion a new parameter was introduced:
stepWeights- determines the ordered array of weights, which shall be used during canary promotion.
Example:
# canary.yaml
spec:
analysis:
stepWeights: [1, 2, 10, 80]This configuration performs analysis starting from 1, going through stepWeights values till 80.
We would have steps (canary weight : primary weight):
- 1 (1 : 99)
- 2 (2 : 98)
- 10 (10 : 90)
- 80 (80 : 20)
- promotion
A/B Testing
For frontend applications that require session affinity you should use
HTTP headers or cookies match conditions to ensure a set of users
will stay on the same version for the whole duration of the canary analysis.
You can enable A/B testing by specifying the HTTP match conditions and the number of iterations.
If Flagger finds a HTTP match condition, it will ignore the maxWeight and stepWeight settings.
Istio example:
analysis:
# schedule interval (default 60s)
interval: 1m
# total number of iterations
iterations: 10
# max number of failed iterations before rollback
threshold: 2
# canary match condition
match:
- headers:
x-canary:
regex: ".*insider.*"
- headers:
cookie:
regex: "^(.*?;)?(canary=always)(;.*)?$"The above configuration will run an analysis for ten minutes targeting the Safari users and those that have a test cookie.
You can determine the minimum time that it takes to validate and promote a canary deployment using this formula:
interval * iterationsAnd the time it takes for a canary to be rollback when the metrics or webhook checks are failing:
interval * thresholdIstio example:
analysis:
interval: 1m
threshold: 10
iterations: 2
match:
- headers:
x-canary:
exact: "insider"
- headers:
cookie:
regex: "^(.*?;)?(canary=always)(;.*)?$"
- sourceLabels:
app.kubernetes.io/name: "scheduler"The header keys must be lowercase and use hyphen as the separator.
Header values are case-sensitive and formatted as follows:
exact: "value"for exact string matchprefix: "value"for prefix-based matchsuffix: "value"for suffix-based matchregex: "value"for RE2 style regex-based match
Note that the sourceLabels match conditions are applicable only when
the mesh gateway is included in the canary.service.gateways list.
App Mesh example:
analysis:
interval: 1m
threshold: 10
iterations: 2
match:
- headers:
user-agent:
regex: ".*Chrome.*"Note that App Mesh supports a single condition.
Contour example:
analysis:
interval: 1m
threshold: 10
iterations: 2
match:
- headers:
user-agent:
prefix: "Chrome"Note that Contour does not support regex, you can use prefix, suffix or exact.
NGINX example:
analysis:
interval: 1m
threshold: 10
iterations: 2
match:
- headers:
x-canary:
exact: "insider"
- headers:
cookie:
exact: "canary"Note that the NGINX ingress controller supports only exact matching for
cookies names where the value must be set to always.
Starting with NGINX ingress v0.31, regex matching is supported for header values.
The above configurations will route users with the x-canary header
or canary cookie to the canary instance during analysis:
curl -H 'X-Canary: insider' http://app.example.com
curl -b 'canary=always' http://app.example.comBlue/Green Deployments
For applications that are not deployed on a service mesh,
Flagger can orchestrate blue/green style deployments with Kubernetes L4 networking.
When using Istio you have the option to mirror traffic between blue and green.
You can use the blue/green deployment strategy by replacingstepWeight/maxWeight with iterations in the analysis spec:
analysis:
# schedule interval (default 60s)
interval: 1m
# total number of iterations
iterations: 10
# max number of failed iterations before rollback
threshold: 2With the above configuration Flagger will run conformance and load tests on the canary pods for ten minutes.
If the metrics analysis succeeds, live traffic will be switched from
the old version to the new one when the canary is promoted.
The blue/green deployment strategy is supported for all service mesh providers.
Blue/Green rollout steps for service mesh:
- detect new revision (deployment spec, secrets or configmaps changes)
- scale up the canary (green)
- run conformance tests for the canary pods
- run load tests and metric checks for the canary pods every minute
- abort the canary release if the failure threshold is reached
- route traffic to canary (This doesn't happen when using the kubernetes provider)
- promote canary spec over primary (blue)
- wait for primary rollout
- route traffic to primary
- scale down canary
After the analysis finishes, the traffic is routed to the canary (green) before
triggering the primary (blue) rolling update,
this ensures a smooth transition to the new version avoiding dropping
in-flight requests during the Kubernetes deployment rollout.
Blue/Green with Traffic Mirroring
Traffic Mirroring is a pre-stage in a Canary (progressive traffic shifting) or Blue/Green deployment strategy.
Traffic mirroring will copy each incoming request, sending one request to the primary and one to the canary service.
The response from the primary is sent back to the user. The response from the canary is discarded.
Metrics are collected on both requests so that the deployment will only proceed if the canary metrics are healthy.
Mirroring should be used for requests that are idempotent or capable of being processed
twice (once by the primary and once by the canary).
Reads are idempotent. Before using mirroring on requests that may be writes,
you should consider what will happen if a write is duplicated and handled by the primary and canary.
To use mirroring, set spec.analysis.mirror to true.
analysis:
# schedule interval (default 60s)
interval: 1m
# total number of iterations
iterations: 10
# max number of failed iterations before rollback
threshold: 2
# Traffic shadowing
mirror: true
# Weight of the traffic mirrored to your canary (defaults to 100%)
# Only applicable for Istio.
mirrorWeight: 100Mirroring rollout steps for service mesh:
- detect new revision (deployment spec, secrets or configmaps changes)
- scale from zero the canary deployment
- wait for the HPA to set the canary minimum replicas
- check canary pods health
- run the acceptance tests
- abort the canary release if tests fail
- start the load tests
- mirror 100% of the traffic from primary to canary
- check request success rate and request duration every minute
- abort the canary release if the failure threshold is reached
- stop traffic mirroring after the number of iterations is reached
- route live traffic to the canary pods
- promote the canary (update the primary secrets, configmaps and deployment spec)
- wait for the primary deployment rollout to finish
- wait for the HPA to set the primary minimum replicas
- check primary pods health
- switch live traffic back to primary
- scale to zero the canary
- send notification with the canary analysis result
After the analysis finishes, the traffic is routed to the canary (green) before
triggering the primary (blue) rolling update, this ensures a smooth transition
to the new version avoiding dropping in-flight requests during the Kubernetes deployment rollout.
Canary Release with Session Affinity
This deployment strategy mixes a Canary Release with A/B testing. A Canary Release is helpful when
we're trying to expose new features to users progressively, but because of the very nature of its
routing (weight based), users can land on the application's old version even after they have been
routed to the new version previously. This can be annoying, or worse break how other services interact
with our application. To address this issue, we borrow some things from A/B testing.
Since A/B testing is particularly helpful for applications that require session affinity, we integrate
cookie based routing with regular weight based routing. This means once a user is exposed to the new
version of our application (based on the traffic weights), they're always routed to that version, i.e.
they're never routed back to the old version of our application.
You can enable this, by specifying .spec.analysis.sessionAffinity in the Canary:
analysis:
# schedule interval (default 60s)
interval: 1m
# max number of failed metric checks before rollback
threshold: 10
# max traffic percentage routed to canary
# percentage (0-100)
maxWeight: 50
# canary increment step
# percentage (0-100)
stepWeight: 2
# session affinity config
sessionAffinity:
# name of the cookie used
cookieName: flagger-cookie
# max age of the cookie (in seconds)
# optional; defaults to 86400
maxAge: 21600.spec.analysis.sessionAffinity.cookieName is the name of the Cookie that is stored. The value of the
cookie is a randomly generated string of characters that act as a unique identifier. For the above
config, the response header of a request routed to the canary deployment during a Canary run will look like:
Set-Cookie: flagger-cookie=LpsIaLdoNZ; Max-Age=21600After a Canary run is over and all traffic is shifted back to the primary deployment, all responses will
have the following header:
Set-Cookie: flagger-cookie=LpsIaLdoNZ; Max-Age=-1This tells the client to delete the cookie, making sure there are no junk cookies lying around in the user's
system.
If a new Canary run is triggered, the response header will set a new cookie for all requests routed to
the Canary deployment:
Set-Cookie: flagger-cookie=McxKdLQoIN; Max-Age=21600Configuring stickiness for Primary deployment
The above strategy is helpful because it makes sure that any user that's routed to the Canary deployment
once is always routed to that deployment. But, this can results in an imbalance in the traffic shifting,
as over time, most of the traffic flows to the Canary deployment. To ensure fair traffic distribution, we
can also configure stickiness for the Primary deployment. You can configure this by specifying aprimaryCookieName field:
analysis:
# schedule interval (default 60s)
interval: 1m
sessionAffinity:
# name of the cookie used
cookieName: flagger-cookie
# max age of the cookie (in seconds)
# optional; defaults to 86400
maxAge: 21600
# name of the cookie to use for the primary backend
# optional; unset means no primary stickiness
primaryCookieName: primary-flagger-cookieNote: This is only supported for the Gateway API and Istio providers for now.
Let's understand what the above configuration does. All the session affinity stuff in the above section
still occurs, but now the response header for requests routed to the primary deployment also include aSet-Cookie header:
Set-Cookie: primary-flagger-cookie=ApvLdqCoMF; Max-Age=60Note that the age of the cookie is the same as the Canary analysis's interval. This means that the cookie
expires when a new steps of the analysis begins and a new cookie is generated like so:
Set-Cookie: primary-flagger-cookie=BRtlVaQoPC; Max-Age=60This ensures that, if the first request of a user during a particular step is routed to the primary deployment,
then all subsequent requests will be routed to the same until the next step starts. During a new step, a new cookie
value is generated which is then included in the headers of responses from the primary workload. This allows for
weighted traffic routing to happen while ensuring that users don't ever switch back to the primary deployment from
the canary deployment during a Canary analysis.
Configuring additional cookie attributes
Depending on your use case, you may neet to set additional cookie attributes in order for your application to route requests correctly.
You may set the following attributes:
analysis:
# schedule interval (default 60s)
interval: 1m
sessionAffinity:
# name of the cookie used
cookieName: flagger-cookie
# max age of the cookie (in seconds)
# optional; defaults to 86400
maxAge: 21600
# defines the host to which the cookie will be sent.
# optional
domain: fluxcd.io
# forbids JavaScript from accessing the cookie, for example, through the Document.cookie property.
# optional
httpOnly: true
# indicates that the cookie should be stored using partitioned storage.
# optional
partitioned: true
# indicates the path that must exist in the requested URL for the browser to send the Cookie header.
# optional
path: /flagger
# controls whether or not a cookie is sent with cross-site requests.
# optional; valid values are Strict, Lax or None
sameSite: Strict
# indicates that the cookie is sent to the server only when a request is made with the https: scheme (except on localhost)
# optional
secure: trueGitbook Usage How It Works (`docs/gitbook/usage/how-it-works.md`)
How it works
Flagger can be configured to automate the release process for
Kubernetes workloads with a custom resource named canary.
Canary resource
The canary custom resource defines the release process of an application running on Kubernetes and is
portable across clusters, service meshes and ingress providers.
For a deployment named podinfo, a canary release with progressive traffic shifting can be defined as:
apiVersion: flagger.app/v1beta1
kind: Canary
metadata:
name: podinfo
spec:
targetRef:
apiVersion: apps/v1
kind: Deployment
name: podinfo
service:
port: 9898
analysis:
interval: 1m
threshold: 10
maxWeight: 50
stepWeight: 5
metrics:
- name: request-success-rate
thresholdRange:
min: 99
interval: 1m
- name: request-duration
thresholdRange:
max: 500
interval: 1m
webhooks:
- name: load-test
url: http://flagger-loadtester.test/
metadata:
cmd: "hey -z 1m -q 10 -c 2 http://podinfo-canary.test:9898/"When you deploy a new version of an app, Flagger gradually shifts traffic to the canary, and at the same time,
measures the requests success rate as well as the average response duration.
You can extend the canary analysis with custom metrics,
acceptance and load testing to harden the validation process of your app release process.
If you are running multiple service meshes or ingress controllers in the same cluster,
you can override the global provider for a specific canary with spec.provider.
Canary target
A canary resource can target a Kubernetes Deployment or DaemonSet.
Kubernetes Deployment example:
spec:
progressDeadlineSeconds: 60
targetRef:
apiVersion: apps/v1
kind: Deployment
name: podinfo
autoscalerRef:
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
name: podinfo
primaryScalerReplicas:
minReplicas: 2
maxReplicas: 5Based on the above configuration, Flagger generates the following Kubernetes objects:
deployment/<targetRef.name>-primaryhpa/<autoscalerRef.name>-primary
The primary deployment is considered the stable release of your app,
by default all traffic is routed to this version and the target deployment is scaled to zero.
Flagger will detect changes to the target deployment (including secrets and configmaps)
and will perform a canary analysis before promoting the new version as primary.
Use .spec.autoscalerRef.primaryScalerReplicas to override the replica scaling
configuration for the generated primary HorizontalPodAutoscaler. This is useful
for situations when you want to have a different scaling configuration for the
primary workload as opposed to using the same values from the original workload HorizontalPodAutoscaler.
Note that the target deployment must have a single label selector in the format app: <DEPLOYMENT-NAME>:
apiVersion: apps/v1
kind: Deployment
metadata:
name: podinfo
spec:
selector:
matchLabels:
app: podinfo
template:
metadata:
labels:
app: podinfoIn addition to app, Flagger supports name and app.kubernetes.io/name selectors.
If you use a different convention you can specify your label with the -selector-labels=my-app-label
command flag in the Flagger deployment manifest under containers args
or by setting --set selectorLabels=my-app-label when installing Flagger with Helm.
If the target deployment uses secrets and/or configmaps,
Flagger will create a copy of each object using the -primary suffix
and will reference these objects in the primary deployment.
If you annotate your ConfigMap or Secret with flagger.app/config-tracking: disabled,
Flagger will use the same object for the primary deployment instead of making a primary copy.
You can disable the secrets/configmaps tracking globally with the -enable-config-tracking=false
command flag in the Flagger deployment manifest under containers args
or by setting --set configTracking.enabled=false when installing Flagger with Helm,
but disabling config-tracking using the per Secret/ConfigMap annotation may fit your use-case better.
The autoscaler reference is optional, when specified,
Flagger will pause the traffic increase while the target and primary deployments are scaled up or down.
HPA can help reduce the resource usage during the canary analysis.
When the autoscaler reference is specified, any changes made to the autoscaler are only made active
in the primary autoscaler when a rollout for the deployment starts and completes successfully.
Optionally, you can create two HPAs, one for canary and one for the primary to update the HPA without
doing a new rollout. As the canary deployment will be scaled to 0, the HPA on the canary will be inactive.
Note Flagger requires autoscaling/v2 API version for HPAs.
The progress deadline represents the maximum time in seconds for the canary deployment to
make progress before it is rolled back, defaults to ten minutes.
Canary service
A canary resource dictates how the target workload is exposed inside the cluster.
The canary target should expose a TCP port that will be used by Flagger to create the ClusterIP Services.
spec:
service:
name: podinfo
port: 9898
portName: http
appProtocol: http
targetPort: 9898
portDiscovery: true
headless: false
trafficDistribution: PreferCloseThe container port from the target workload should match the service.port or service.targetPort.
The service.name is optional, defaults to spec.targetRef.name.
The service.targetPort can be a container port number or name.
The service.portName is optional (defaults to http), if your workload uses gRPC then set the port name to grpc.
The service.appProtocol is optional, more details can be found here.
The service.trafficDistribution is optional, more details can be found here.
If port discovery is enabled, Flagger scans the target workload and extracts the containers ports
excluding the port specified in the canary service and service mesh sidecar ports.
These ports will be used when generating the ClusterIP services.
Based on the canary spec service, Flagger creates the following Kubernetes ClusterIP service:
<service.name>.<namespace>.svc.cluster.localselector
app=<name>-primary<service.name>-primary.<namespace>.svc.cluster.localselector
app=<name>-primary<service.name>-canary.<namespace>.svc.cluster.localselector
app=<name>
This ensures that traffic to podinfo.test:9898 will be routed to the latest stable release of your app.
The podinfo-canary.test:9898 address is available only during the canary analysis
and can be used for conformance testing or load testing.
You can configure Flagger to set annotations and labels for the generated services with:
spec:
service:
port: 9898
apex:
annotations:
test: "test"
labels:
test: "test"
canary:
annotations:
test: "test"
labels:
test: "test"
primary:
annotations:
test: "test"
labels:
test: "test"Note that the apex annotations are added to both the generated Kubernetes Service and the
generated service mesh/ingress object. This allows using external-dns with Istio VirtualServices
and TraefikServices. Beware of configuration conflicts here.
Note that if any annotations or labels are added that are not specified here,
Flagger will remove them during reconciliation. To specify metadata
that should be ignored by Flagger, configure unmanagedMetadata.
If you want for the generated Kubernetes ClusterIP services to be headless,
then set service.headless to true.
Besides port mapping and metadata, the service specification can
contain URI match and rewrite rules, timeout and retry polices:
spec:
service:
port: 9898
match:
- uri:
prefix: /
rewrite:
uri: /
retries:
attempts: 3
perTryTimeout: 1s
timeout: 5s
# backendTimeout is only supported by the Gateway API provider and
# maps to HTTPRouteTimeouts.BackendRequest.
backendTimeout: 2sWhen using Istio as the mesh provider, you can also specify HTTP header operations,
CORS and traffic policies, Istio gateways and hosts.
The Istio routing configuration can be found here.
Canary status
You can use kubectl to get the current status of canary deployments cluster wide:
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:07ZThe status condition reflects the last known state of the canary analysis:
kubectl -n test get canary/podinfo -oyaml | awk '/status/,0'A successful rollout status:
status:
canaryWeight: 0
failedChecks: 0
iterations: 0
lastAppliedSpec: "14788816656920327485"
lastPromotedSpec: "14788816656920327485"
conditions:
- lastTransitionTime: "2019-07-10T08:23:18Z"
lastUpdateTime: "2019-07-10T08:23:18Z"
message: Canary analysis completed successfully, promotion finished.
reason: Succeeded
status: "True"
type: PromotedThe Promoted status condition can have one of the following reasons:
Initialized, Waiting, Progressing, WaitingPromotion, Promoting, Finalising, Succeeded or Failed.
A failed canary will have the promoted status set to false,
the reason to failed and the last applied spec will be different to the last promoted one.
Wait for a successful rollout:
kubectl wait canary/podinfo --for=condition=promotedCI example:
# update the container image
kubectl set image deployment/podinfo podinfod=stefanprodan/podinfo:3.0.1
# wait for Flagger to detect the change
ok=false
until ${ok}; do
kubectl get canary/podinfo | grep 'Progressing' && ok=true || ok=false
sleep 5
done
# wait for the canary analysis to finish
kubectl wait canary/podinfo --for=condition=promoted --timeout=5m
# check if the deployment was successful
kubectl get canary/podinfo | grep SucceededCanary finalizers
The default behavior of Flagger on canary deletion is to leave resources that aren't owned
by the controller in their current state.
This simplifies the deletion action and avoids possible deadlocks during resource finalization.
In the event the canary was introduced with existing resource(s) (i.e. service, virtual service, etc.),
they would be mutated during the initialization phase and no longer reflect their initial state.
If the desired functionality upon deletion is to revert the resources to their initial state,
the revertOnDeletion attribute can be enabled.
spec:
revertOnDeletion: trueWhen a deletion action is submitted to the cluster, Flagger will attempt to revert the following resources:
- Canary target replicas will be updated to the primary replica count
- Canary service selector will be reverted
- Mesh/Ingress traffic routed to the target
The recommended approach to disable canary analysis would be utilization of the skipAnalysis attribute,
which limits the need for resource reconciliation.
Utilizing the revertOnDeletion attribute should be enabled when
you no longer plan to rely on Flagger for deployment management.
Note When this feature is enabled expect a delay in the delete action due to the reconciliation.
Canary analysis
The canary analysis defines:
- the type of deployment strategy
- the metrics used to validate the canary version
- the webhooks used for conformance testing, load testing and manual gating
- the alerting settings
Spec:
analysis:
# schedule interval (default 60s)
interval:
# max number of failed metric checks before rollback
threshold:
# max traffic percentage routed to canary
# percentage (0-100)
maxWeight:
# canary increment step
# percentage (0-100)
stepWeight:
# promotion increment step
# percentage (0-100)
stepWeightPromotion:
# total number of iterations
# used for A/B Testing and Blue/Green
iterations:
# threshold of primary pods that need to be available to consider it ready
# before starting rollout. this is optional and the default is 100
# percentage (0-100)
primaryReadyThreshold: 100
# threshold of canary pods that need to be available to consider it ready
# before starting rollout. this is optional and the default is 100
# percentage (0-100)
canaryReadyThreshold: 100
# canary match conditions
# used for A/B Testing
match:
- # HTTP header
# key performance indicators
metrics:
- # metric check
# alerting
alerts:
- # alert provider
# external checks
webhooks:
- # hookThe canary analysis runs periodically until it reaches the maximum traffic weight or the number of iterations.
On each run, Flagger calls the webhooks, checks the metrics and if the failed checks threshold is reached,
stops the analysis and rolls back the canary.
If alerting is configured, Flagger will post the analysis result using the alert providers.
Canary suspend
The suspend field can be set to true to suspend the Canary. If a Canary is suspended,
its reconciliation is completely paused. This means that changes to target workloads,
tracked ConfigMaps and Secrets don't trigger a Canary run and changes to resources generated
by Flagger are not corrected. If the Canary was suspended during an active Canary run,
then the run is paused without disturbing the workloads or the traffic weights.
Gitbook Usage Metrics (`docs/gitbook/usage/metrics.md`)
Metrics Analysis
As part of the analysis process, Flagger can validate service level objectives
(SLOs) like availability, error rate percentage, average response time and any other objective
based on app specific metrics.
If a drop in performance is noticed during the SLOs analysis,
the release will be automatically rolled back with minimum impact to end-users.
Builtin metrics
Flagger comes with two builtin metric checks: HTTP request success rate and duration.
analysis:
metrics:
- name: request-success-rate
interval: 1m
# minimum req success rate (non 5xx responses)
# percentage (0-100)
thresholdRange:
min: 99
- name: request-duration
interval: 1m
# maximum req duration P99
# milliseconds
thresholdRange:
max: 500For each metric you can specify a range of accepted values with thresholdRange and
the window size or the time series with interval.
The builtin checks are available for every service mesh / ingress controller
and are implemented with Prometheus queries.
Custom metrics
The canary analysis can be extended with custom metric checks.
Using a MetricTemplate custom resource,
you configure Flagger to connect to a metric provider and run a query that returns a float64 value.
The query result is used to validate the canary based on the specified threshold range.
apiVersion: flagger.app/v1beta1
kind: MetricTemplate
metadata:
name: my-metric
spec:
provider:
type: # can be prometheus, datadog, etc
address: # API URL
insecureSkipVerify: # if set to true, disables the TLS cert validation
secretRef:
name: # name of the secret containing the API credentials
query: # metric queryThe following variables are available in query templates:
name(canary.metadata.name)namespace(canary.metadata.namespace)target(canary.spec.targetRef.name)service(canary.spec.service.name)ingress(canary.spec.ingresRef.name)interval(canary.spec.analysis.metrics[].interval)variables(canary.spec.analysis.metrics[].templateVariables)
A canary analysis metric can reference a template with templateRef:
analysis:
metrics:
- name: "my metric"
templateRef:
name: my-metric
# namespace is optional
# when not specified, the canary namespace will be used
namespace: flagger
# accepted values
thresholdRange:
min: 10
max: 1000
# metric query time window
interval: 1mA canary analysis metric can reference a set of custom variables with templateVariables. These variables will be then injected into the query defined in the referred MetricTemplate object during canary analysis:
analysis:
metrics:
- name: "my metric"
templateRef:
name: my-metric
namespace: flagger
# accepted values
thresholdRange:
min: 10
max: 1000
# metric query time window
interval: 1m
# custom variables used within the referenced metric template
templateVariables:
direction: inboundapiVersion: flagger.app/v1beta1
kind: MetricTemplate
metadata:
name: my-metric
spec:
provider:
type: prometheus
address: http://prometheus.linkerd-viz:9090
query: |
histogram_quantile(
0.99,
sum(
rate(
response_latency_ms_bucket{
namespace="{{ namespace }}",
deployment=~"{{ target }}",
direction="{{ variables.direction }}"
}[{{ interval }}]
)
) by (le)
)Prometheus
You can create custom metric checks targeting a Prometheus server by
setting the provider type to prometheus and writing the query in PromQL.
Prometheus template example:
apiVersion: flagger.app/v1beta1
kind: MetricTemplate
metadata:
name: not-found-percentage
namespace: istio-system
spec:
provider:
type: prometheus
address: http://prometheus.istio-system:9090
query: |
100 - sum(
rate(
istio_requests_total{
reporter="destination",
destination_workload_namespace="{{ namespace }}",
destination_workload="{{ target }}",
response_code!="404"
}[{{ interval }}]
)
)
/
sum(
rate(
istio_requests_total{
reporter="destination",
destination_workload_namespace="{{ namespace }}",
destination_workload="{{ target }}"
}[{{ interval }}]
)
) * 100Reference the template in the canary analysis:
analysis:
metrics:
- name: "404s percentage"
templateRef:
name: not-found-percentage
namespace: istio-system
thresholdRange:
max: 5
interval: 1mThe above configuration validates the canary by checking if the HTTP 404 req/sec percentage
is below 5 percent of the total traffic. If the 404s rate reaches the 5% threshold, then the canary fails.
Prometheus gRPC error rate example:
apiVersion: flagger.app/v1beta1
kind: MetricTemplate
metadata:
name: grpc-error-rate-percentage
namespace: flagger
spec:
provider:
type: prometheus
address: http://flagger-prometheus.flagger-system:9090
query: |
100 - sum(
rate(
grpc_server_handled_total{
grpc_code!="OK",
kubernetes_namespace="{{ namespace }}",
kubernetes_pod_name=~"{{ target }}-[0-9a-zA-Z]+(-[0-9a-zA-Z]+)"
}[{{ interval }}]
)
)
/
sum(
rate(
grpc_server_started_total{
kubernetes_namespace="{{ namespace }}",
kubernetes_pod_name=~"{{ target }}-[0-9a-zA-Z]+(-[0-9a-zA-Z]+)"
}[{{ interval }}]
)
) * 100The above template is for gRPC services instrumented with
go-grpc-prometheus.
Prometheus authentication
If your Prometheus API requires basic authentication, you can create a secret in the same namespace
as the MetricTemplate with the basic-auth credentials:
apiVersion: v1
kind: Secret
metadata:
name: prom-auth
namespace: flagger
data:
username: your-user
password: your-passwordor if you require bearer token authentication (via a SA token):
apiVersion: v1
kind: Secret
metadata:
name: prom-auth
namespace: flagger
data:
token: ey1234...Then reference the secret in the MetricTemplate:
apiVersion: flagger.app/v1beta1
kind: MetricTemplate
metadata:
name: my-metric
namespace: flagger
spec:
provider:
type: prometheus
address: http://prometheus.monitoring:9090
secretRef:
name: prom-authDatadog
You can create custom metric checks using the Datadog provider.
Create a secret with your Datadog API credentials:
apiVersion: v1
kind: Secret
metadata:
name: datadog
namespace: istio-system
data:
datadog_api_key: your-datadog-api-key
datadog_application_key: your-datadog-application-keyDatadog template example:
apiVersion: flagger.app/v1beta1
kind: MetricTemplate
metadata:
name: not-found-percentage
namespace: istio-system
spec:
provider:
type: datadog
address: https://api.datadoghq.com
secretRef:
name: datadog
query: |
100 - (
sum:istio.mesh.request.count{
reporter:destination,
destination_workload_namespace:{{ namespace }},
destination_workload:{{ target }},
!response_code:404
}.as_count()
/
sum:istio.mesh.request.count{
reporter:destination,
destination_workload_namespace:{{ namespace }},
destination_workload:{{ target }}
}.as_count()
) * 100Reference the template in the canary analysis:
analysis:
metrics:
- name: "404s percentage"
templateRef:
name: not-found-percentage
namespace: istio-system
thresholdRange:
max: 5
interval: 1mDatadog Rate Limits
For bigger setups, you might run into rate limits on the Datadog API. To avoid this,
you can use the Datadog Cluster Agent to retrieve metrics in batches instead. It will then
expose these metrics as an external metrics server.
Once you have enabled Datadog's external metrics endpoint and DatadogMetric CRD (without
necessarily using registerAPIService), you can use Flagger's
External Metrics Provider to query the metrics from there.
The server address is usually datadog-cluster-agent-metrics-server and exposed on port 8443.
ExternalMetrics will be named as datadogmetric@<namespace>:<metricname>, for exampledatadogmetric@istio-system:istio-mesh-request-count.
Amazon CloudWatch
You can create custom metric checks using the CloudWatch metrics provider.
CloudWatch template example:
/* Detailed source-code truncated for AI context efficiency. */The query format documentation can be found
here.
Reference the template in the canary analysis:
analysis:
metrics:
- name: "app error rate"
templateRef:
name: cloudwatch-error-rate
thresholdRange:
max: 0.1
interval: 1mNote that Flagger need AWS IAM permission to perform cloudwatch:GetMetricData to use this provider.
New Relic
You can create custom metric checks using the New Relic provider.
Create a secret with your New Relic Insights credentials:
apiVersion: v1
kind: Secret
metadata:
name: newrelic
namespace: istio-system
data:
newrelic_account_id: your-account-id
newrelic_query_key: your-insights-query-keyNew Relic template example:
apiVersion: flagger.app/v1beta1
kind: MetricTemplate
metadata:
name: newrelic-error-rate
namespace: ingress-nginx
spec:
provider:
type: newrelic
secretRef:
name: newrelic
query: |
SELECT
filter(sum(nginx_ingress_controller_requests), WHERE status >= '500') /
sum(nginx_ingress_controller_requests) * 100
FROM Metric
WHERE metricName = 'nginx_ingress_controller_requests'
AND ingress = '{{ ingress }}' AND namespace = '{{ namespace }}'Reference the template in the canary analysis:
analysis:
metrics:
- name: "error rate"
templateRef:
name: newrelic-error-rate
namespace: ingress-nginx
thresholdRange:
max: 5
interval: 1mGraphite
You can create custom metric checks using the Graphite provider.
Graphite template example:
apiVersion: flagger.app/v1beta1
kind: MetricTemplate
metadata:
name: graphite-request-success-rate
spec:
provider:
type: graphite
address: http://graphite.monitoring
query: |
target=summarize(
asPercent(
sumSeries(
stats.timers.httpServerRequests.app.{{target}}.exception.*.method.*.outcome.{CLIENT_ERROR,INFORMATIONAL,REDIRECTION,SUCCESS}.status.*.uri.*.count
),
sumSeries(
stats.timers.httpServerRequests.app.{{target}}.exception.*.method.*.outcome.*.status.*.uri.*.count
)
),
{{interval}},
'avg'
)Reference the template in the canary analysis:
analysis:
metrics:
- name: "success rate"
templateRef:
name: graphite-request-success-rate
thresholdRange:
min: 90
interval: 1minGraphite authentication
If your Graphite API requires basic authentication, you can create a secret in the same namespace
as the MetricTemplate with the basic-auth credentials:
apiVersion: v1
kind: Secret
metadata:
name: graphite-basic-auth
namespace: flagger
data:
username: your-user
password: your-passwordThen, reference the secret in the MetricTemplate:
apiVersion: flagger.app/v1beta1
kind: MetricTemplate
metadata:
name: my-metric
namespace: flagger
spec:
provider:
type: graphite
address: http://graphite.monitoring
secretRef:
name: graphite-basic-authGoogle Cloud Monitoring (Stackdriver)
Enable Workload Identity on your cluster, create a service account key that has read access to the
Cloud Monitoring API and then create an IAM policy binding between the GCP service account and the Flagger
service account on Kubernetes. You can take a look at this guide
Annotate the flagger service account
kubectl annotate serviceaccount flagger \
--namespace <namespace> \
iam.gke.io/gcp-service-account=<gcp-serviceaccount-name>@<project-id>.iam.gserviceaccount.comAlternatively, you can download the json keys and add it to your secret with the key serviceAccountKey (This method is not recommended).
Create a secret that contains your project-id (and, if workload identity is not enabled on your cluster,
your service account json).
kubectl create secret generic gcloud-sa --from-literal=project=<project-id>Then reference the secret in the metric template.
Note: The particular MQL query used here works if Istio is installed on GKE.
apiVersion: flagger.app/v1beta1
kind: MetricTemplate
metadata:
name: bytes-sent
namespace: test
spec:
provider:
type: stackdriver
secretRef:
name: gcloud-sa
query: |
fetch k8s_container
| metric 'istio.io/service/server/response_latencies'
| filter
(metric.destination_service_name == '{{ service }}-canary'
&& metric.destination_service_namespace == '{{ namespace }}')
| align delta(1m)
| every 1m
| group_by [],
[value_response_latencies_percentile:
percentile(value.response_latencies, 99)]The reference for the query language can be found here
InfluxDB
The InfluxDB provider uses the flux query language.
Create a secret that contains your authentication token that can be found in the InfluxDB UI.
kubectl create secret generic influx-token --from-literal=token=<token>Then reference the secret in the metric template.
Note: The particular MQL query used here works if Istio is installed on GKE.
apiVersion: flagger.app/v1beta1
kind: MetricTemplate
metadata:
name: not-found
namespace: test
spec:
provider:
type: influxdb
secretRef:
name: influx-token
query: |
from(bucket: "default")
|> range(start: -2h)
|> filter(fn: (r) => r["_measurement"] == "istio_requests_total")
|> filter(fn: (r) => r[" destination_workload_namespace"] == "{{ namespace }}")
|> filter(fn: (r) => r["destination_workload"] == "{{ target }}")
|> filter(fn: (r) => r["response_code"] == "500")
|> count()
|> yield(name: "count")Dynatrace
You can create custom metric checks using the Dynatrace provider.
Create a secret with your Dynatrace token:
apiVersion: v1
kind: Secret
metadata:
name: dynatrace
namespace: istio-system
data:
dynatrace_token: ZHQwYz...Dynatrace metric template example:
apiVersion: flagger.app/v1beta1
kind: MetricTemplate
metadata:
name: response-time-95pct
namespace: istio-system
spec:
provider:
type: dynatrace
address: https://xxxxxxxx.live.dynatrace.com
secretRef:
name: dynatrace
query: |
builtin:service.response.time:filter(eq(dt.entity.service,SERVICE-ABCDEFG0123456789)):percentile(95)Reference the template in the canary analysis:
analysis:
metrics:
- name: "response-time-95pct"
templateRef:
name: response-time-95pct
namespace: istio-system
thresholdRange:
max: 1000
interval: 1mKeptn
You can create custom metric checks using the Keptn provider.
This Provider allows to verify either the value of a single KeptnMetric,
representing the value of a single metric,
or of a Keptn Analysis,
which provides a flexible grading logic for analysing and prioritising a number of different
metric values coming from different data sources.
This provider requires Keptn to be installed in the cluster.
Example for a Keptn metric template:
apiVersion: flagger.app/v1beta1
kind: MetricTemplate
metadata:
name: response-time
namespace: istio-system
spec:
provider:
type: keptn
query: keptnmetric/my-namespace/response-time/2m/reporter=destinationThis will reference the KeptnMetric with the name response-time in
the namespace my-namespace, which could look like the following:
apiVersion: metrics.keptn.sh/v1beta1
kind: KeptnMetric
metadata:
name: response-time
namespace: my-namespace
spec:
fetchIntervalSeconds: 10
provider:
name: my-prometheus-keptn-provider
query: histogram_quantile(0.8, sum by(le) (rate(http_server_request_latency_seconds_bucket{status_code='200',
job='simple-go-backend'}[5m[])))The query contains the following components, which are divided by / characters:
<type>/<namespace>/<resource-name>/<timeframe>/<arguments>- type (required): Must be either
keptnmetricoranalysis. - namespace (required): The namespace of the referenced
KeptnMetric/AnalysisDefinition. - resource-name (required): The name of the referenced
KeptnMetric/AnalysisDefinition. - timeframe (optional): The timeframe used for the Analysis.
This will usually be set to the same value as the analysis interval of aCanary.
Only relevant if thetypeis set toanalysis. - arguments (optional): Arguments to be passed to an
Analysis.
Arguments are passed as a list of key value pairs, separated by;characters,
e.g.foo=bar;bar=foo.
Only relevant if thetypeis set toanalysis.
For the type analysis, the value returned by the provider is either 0
(if the analysis failed), or 1 (analysis passed).
Splunk
You can create custom metric checks using the Splunk provider.
Create a secret that contains your authentication token that can be found in the Splunk o11y UI.
apiVersion: v1
kind: Secret
metadata:
name: splunk
namespace: istio-system
data:
sf_token_key: your-access-tokenSplunk template example:
apiVersion: flagger.app/v1beta1
kind: MetricTemplate
metadata:
name: success-rate
namespace: istio-system
spec:
provider:
type: splunk
address: https://api.<REALM>.signalfx.com
secretRef:
name: splunk
query: |
total = data('traces.count', filter=filter('sf_service', '{{target}}')).sum().publish(enable=False)
success = data('traces.count', filter=filter('sf_service', '{{target}}') and filter('sf_error', 'false')).sum().publish(enable=False)
((success/total) * 100).publish()The query format documentation can be found here.
Reference the template in the canary analysis:
analysis:
metrics:
- name: "success rate"
templateRef:
name: success-rate
namespace: istio-system
thresholdRange:
max: 99
interval: 1mAzure Monitor Workspace
You can create custom metric checks using the Azure Monitor provider, which queries the
Prometheus compatible API
of an Azure Monitor Workspace.
Queries are authenticated with a Microsoft Entra ID token, so no authentication proxy is
required in front of the workspace. Set address to the workspace Query endpoint and grant
the identity that Flagger uses the Monitoring Data Reader role on the workspace. Azure
Government and Azure China workspaces are detected from the endpoint.
With no secretRef, Flagger authenticates with the
workload identity of its own
pod. This requires a federated credential for the system:serviceaccount:flagger-system:flagger
subject, and a Flagger install that carries the annotation and label the webhook looks for:
serviceAccount:
annotations:
azure.workload.identity/client-id: your-managed-identity-client-id
podLabels:
azure.workload.identity/use: "true"The metric template then needs no credentials:
apiVersion: flagger.app/v1beta1
kind: MetricTemplate
metadata:
name: request-success-rate
namespace: istio-system
spec:
provider:
type: azuremonitor
address: https://flagger-abc1.eastus.prometheus.monitor.azure.com
query: |
100 - sum(
rate(
istio_requests_total{
reporter="destination",
destination_workload_namespace="{{ namespace }}",
destination_workload="{{ target }}",
response_code=~"5.."
}[{{ interval }}]
)
)
/
sum(
rate(
istio_requests_total{
reporter="destination",
destination_workload_namespace="{{ namespace }}",
destination_workload="{{ target }}"
}[{{ interval }}]
)
) * 100Reference the template in the canary analysis:
analysis:
metrics:
- name: "request success rate"
templateRef:
name: request-success-rate
namespace: istio-system
thresholdRange:
min: 99
interval: 1mTo use a different identity, reference a secret with secretRef. The keys it contains select
the identity:
| Secret keys | Identity used |
|---|---|
clientId, tenantId, clientSecret |
Microsoft Entra ID application (service principal) |
clientId, tenantId |
Workload identity, for an identity other than the pod default |
apiVersion: v1
kind: Secret
metadata:
name: azure-monitor
namespace: istio-system
stringData:
clientId: your-application-id
tenantId: your-tenant-id
clientSecret: your-client-secretBecause Flagger can authenticate with its own identity, address is restricted to an https
workspace query endpoint and insecureSkipVerify is not supported.
Troubleshooting:
no token file specifiedorno client ID specifiedmeans the workload identity webhook did
not project a token into the Flagger pod. Check the service account annotation and pod label.- A
403response means the identity is missing theMonitoring Data Readerrole assignment
on the Azure Monitor Workspace. - A
429response means the workspace query limits have been reached. Flagger treats this as a
failed metric check, which can cause a canary to be rolled back.
Kubernetes External Metrics
You can query an external metrics provider that implements the
Kubernetes External Metrics API.
By default, Flagger will use its bound Service Account for authentication. Optionally you can provide a Bearer token through a Secret (that must contain a field named token) :
apiVersion: v1
kind: Secret
metadata:
name: external-metric-server-token
namespace: default
stringData:
token: your-access-tokenExternal Metrics template example:
apiVersion: flagger.app/v1beta1
kind: MetricTemplate
metadata:
name: my-external-metric
namespace: default
spec:
provider:
type: externalmetrics
address: https://external-metrics-server.default.svc.cluster.local:8443
secretRef: # Optional
name: external-metric-server-token
query: webapp-frontend/job-success-rate?labelSelector=env%3DproductionGitbook Usage Monitoring (`docs/gitbook/usage/monitoring.md`)
Monitoring
Grafana
Flagger comes with a Grafana dashboard made for canary analysis. Install Grafana with Helm:
helm upgrade -i flagger-grafana flagger/grafana \
--set url=http://prometheus:9090The dashboard shows the RED and USE metrics for the primary and canary workloads:
Logging
The canary errors and latency spikes have been recorded as Kubernetes events and logged by Flagger in json format:
kubectl -n istio-system logs deployment/flagger --tail=100 | jq .msg
Starting canary deployment for podinfo.test
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
Advance podinfo.test canary weight 30
Advance podinfo.test canary weight 35
Halt podinfo.test advancement success rate 98.69% < 99%
Advance podinfo.test canary weight 40
Halt podinfo.test advancement request duration 1.515s > 500ms
Advance podinfo.test canary weight 45
Advance podinfo.test canary weight 50
Copying podinfo.test template spec to podinfo-primary.test
Halt podinfo-primary.test advancement waiting for rollout to finish: 1 old replicas are pending termination
Scaling down podinfo.test
Promotion completed! podinfo.testEvent Webhook
Flagger can be configured to send event payloads to a specified webhook:
helm upgrade -i flagger flagger/flagger \
--set eventWebhook=https://example.com/flagger-canary-event-webhookThe environment variable EVENT_WEBHOOK_URL can be used for activating the event-webhook, too.
This is handy for using a secret to store a sensible value that could contain api keys for example.
When configured, every action that Flagger takes during a canary deployment
will be sent as JSON via an HTTP POST request. The JSON payload has the following schema:
{
"name": "string (canary name)",
"namespace": "string (canary namespace)",
"phase": "string (canary phase)",
"metadata": {
"eventMessage": "string (canary event message)",
"eventType": "string (canary event type)",
"timestamp": "string (unix timestamp ms)"
}
}Example:
{
"name": "podinfo",
"namespace": "default",
"phase": "Progressing",
"metadata": {
"eventMessage": "New revision detected! Scaling up podinfo.default",
"eventType": "Normal",
"timestamp": "1578607635167"
}
}The event webhook can be overwritten at canary level with:
analysis:
webhooks:
- name: "send to Slack"
type: event
url: http://event-recevier.notifications/slackMetrics
Flagger exposes Prometheus metrics that can be used to determine
the canary analysis status and the destination weight values:
# Flagger version and mesh provider gauge
flagger_info{version="0.10.0", mesh_provider="istio"} 1
# Canaries total gauge
flagger_canary_total{namespace="test"} 1
# Canary promotion last known status gauge
# 0 - running, 1 - successful, 2 - failed
flagger_canary_status{name="podinfo" namespace="test"} 1
# Canary traffic weight gauge
flagger_canary_weight{workload="podinfo-primary" namespace="test"} 95
flagger_canary_weight{workload="podinfo" namespace="test"} 5
# Seconds spent performing canary analysis histogram
flagger_canary_duration_seconds_bucket{name="podinfo",namespace="test",le="10"} 6
flagger_canary_duration_seconds_bucket{name="podinfo",namespace="test",le="+Inf"} 6
flagger_canary_duration_seconds_sum{name="podinfo",namespace="test"} 17.3561329
flagger_canary_duration_seconds_count{name="podinfo",namespace="test"} 6
# Last canary metric analysis result per different metrics
flagger_canary_metric_analysis{metric="podinfo-http-successful-rate",name="podinfo",namespace="test"} 1
flagger_canary_metric_analysis{metric="podinfo-custom-metric",name="podinfo",namespace="test"} 0.918223108974359
# Canary successes total counter
flagger_canary_successes_total{name="podinfo",namespace="test",deployment_strategy="canary",analysis_status="completed"} 5
# Canary failures total counter
flagger_canary_failures_total{name="podinfo",namespace="test",deployment_strategy="canary",analysis_status="completed"} 1Gitbook Usage Webhooks (`docs/gitbook/usage/webhooks.md`)
Webhooks
The canary analysis can be extended with webhooks.
Flagger will call each webhook URL and determine from the response status code
(HTTP 2xx) if the canary is failing or not.
There are several types of hooks:
confirm-rollout hooks are executed before scaling up the canary deployment and can be used for manual approval.
The rollout is paused until the hook returns a successful HTTP status code.pre-rollout hooks are executed before routing traffic to canary.
The canary advancement is paused if a pre-rollout hook fails and if the number of failures reach the
threshold the canary will be rollback.rollout hooks are executed during the analysis on each iteration before the metric checks.
If a rollout hook call fails the canary advancement is paused and eventfully rolled back.confirm-traffic-increase hooks are executed right before the weight on the canary is increased. The canary
advancement is paused until this hook returns HTTP 200.confirm-promotion hooks are executed before the promotion step.
The canary promotion is paused until the hooks return HTTP 200.
While the promotion is paused, Flagger will continue to run the metrics checks and rollout hooks.post-rollout hooks are executed after the canary has been promoted or rolled back.
If a post rollout hook fails the error is logged.rollback hooks are executed while a canary deployment is in either Progressing or Waiting status.
This provides the ability to rollback during analysis or while waiting for a confirmation. If a rollback hook
returns a successful HTTP status code, Flagger will stop the analysis and mark the canary release as failed.event hooks are executed every time Flagger emits a Kubernetes event. When configured,
every action that Flagger takes during a canary deployment will be sent as JSON via an HTTP POST request.
Spec:
analysis:
webhooks:
- name: "start gate"
type: confirm-rollout
url: http://flagger-loadtester.test/gate/approve
retries: 5
- name: "helm test"
type: pre-rollout
url: http://flagger-helmtester.flagger/
timeout: 3m
metadata:
type: "helmv3"
cmd: "test podinfo -n test"
- name: "load test"
type: rollout
url: http://flagger-loadtester.test/
timeout: 15s
metadata:
cmd: "hey -z 1m -q 5 -c 2 http://podinfo-canary.test:9898/"
- name: "traffic increase gate"
type: confirm-traffic-increase
url: http://flagger-loadtester.test/gate/approve
- name: "promotion gate"
type: confirm-promotion
url: http://flagger-loadtester.test/gate/approve
- name: "notify"
type: post-rollout
url: http://telegram.bot:8080/
timeout: 5s
metadata:
some: "message"
- name: "rollback gate"
type: rollback
url: http://flagger-loadtester.test/rollback/check
- name: "send to Slack"
type: event
url: http://event-recevier.notifications/slack
retries: 3
metadata:
environment: "test"
cluster: "flagger-test"Note that the sum of all rollout webhooks timeouts should be lower than the analysis interval.
Webhook payload (HTTP POST):
{
"name": "podinfo",
"namespace": "test",
"phase": "Progressing",
"checksum": "85d557f47b",
"metadata": {
"test": "all",
"token": "16688eb5e9f289f1991c"
}
}The checksum field is hashed from the TrackedConfigs and LastAppliedSpec of the Canary, it can be used to identify a Canary for a specific configuration of the deployed resources.
Response status codes:
- 200-202 - advance canary by increasing the traffic weight
- timeout or non-2xx - halt advancement and increment failed checks
On a non-2xx response Flagger will include the response body (if any) in the failed checks log and Kubernetes events.
Event payload (HTTP POST):
{
"name": "string (canary name)",
"namespace": "string (canary namespace)",
"phase": "string (canary phase)",
"checksum": "string (canary checksum"),
"metadata": {
"eventMessage": "string (canary event message)",
"eventType": "string (canary event type)",
"timestamp": "string (unix timestamp ms)"
}
}The event receiver can create alerts based on the received phase
(possible values: Initialized, Waiting, Progressing, Promoting, Finalising, Succeeded or Failed).
Options:
retries: The webhook request can be retried by specifying a positive integer in the
retriesfield. This helps ensure reliability if the webhook fails due to transient network issues.disable TLS: Set
disableTLStotruein the webhook spec to bypass TLS verification. This is useful in cases where the target service uses self-signed certificates, or you need to connect to an insecure service for testing purposes.
Load Testing
For workloads that are not receiving constant traffic Flagger can be configured with a webhook,
that when called, will start a load test for the target workload.
If the target workload doesn't receive any traffic during the canary analysis,
Flagger metric checks will fail with "no values found for metric request-success-rate".
Flagger comes with a load testing service based on rakyll/hey
that generates traffic during analysis when configured as a webhook.
First you need to deploy the load test runner in a namespace with sidecar injection enabled:
kubectl apply -k https://github.com/fluxcd/flagger//kustomize/tester?ref=mainOr by using Helm:
helm repo add flagger https://flagger.app
helm upgrade -i flagger-loadtester flagger/loadtester \
--namespace=test \
--set cmd.timeout=1h \
--set cmd.namespaceRegexp=''When deployed the load tester API will be available at http://flagger-loadtester.test/.
Now you can add webhooks to the canary analysis spec:
webhooks:
- name: load-test-get
url: http://flagger-loadtester.test/
timeout: 5s
metadata:
type: cmd
cmd: "hey -z 1m -q 10 -c 2 http://podinfo-canary.test:9898/"
- name: load-test-post
url: http://flagger-loadtester.test/
timeout: 5s
metadata:
type: cmd
cmd: "hey -z 1m -q 10 -c 2 -m POST -d '{test: 2}' http://podinfo-canary.test:9898/echo"When the canary analysis starts, Flagger will call the webhooks and the load tester will
run the hey commands in the background, if they are not already running.
This will ensure that during the analysis, the podinfo-canary.test
service will receive a steady stream of GET and POST requests.
If your workload is exposed outside the mesh you can point hey to the public URL and use HTTP2.
webhooks:
- name: load-test-get
url: http://flagger-loadtester.test/
timeout: 5s
metadata:
type: cmd
cmd: "hey -z 1m -q 10 -c 2 -h2 https://podinfo.example.com/"For gRPC services you can use bojand/ghz which is a similar tool to Hey but for gRPC:
webhooks:
- name: grpc-load-test
url: http://flagger-loadtester.test/
timeout: 5s
metadata:
type: cmd
cmd: "ghz -z 1m -q 10 -c 2 --insecure podinfo.test:9898"ghz uses reflection to identify which gRPC method to call.
If you do not wish to enable reflection for your gRPC service you can implement a standardized
health check from the grpc-proto library.
To use this health check schema
without reflection you can pass a parameter to ghz like this
webhooks:
- name: grpc-load-test-no-reflection
url: http://flagger-loadtester.test/
timeout: 5s
metadata:
type: cmd
cmd: "ghz --insecure --proto=/tmp/ghz/health.proto --call=grpc.health.v1.Health/Check podinfo.test:9898"The load tester can run arbitrary commands as long as the binary is present in the container image.
For example if you want to replace hey with another CLI, you can create your own Docker image:
FROM weaveworks/flagger-loadtester:<VER>
RUN curl -Lo /usr/local/bin/my-cli https://github.com/user/repo/releases/download/ver/my-cli \
&& chmod +x /usr/local/bin/my-cliLoad Testing Delegation
The load tester can also forward testing tasks to external tools,
by now nGrinder is supported.
To use this feature, add a load test task of type 'ngrinder' to the canary analysis spec:
webhooks:
- name: load-test-post
url: http://flagger-loadtester.test/
timeout: 5s
metadata:
# type of this load test task, cmd or ngrinder
type: ngrinder
# base url of your nGrinder controller server
server: http://ngrinder-server:port
# id of the test to clone from, the test must have been defined.
clone: 100
# user name and base64 encoded password to authenticate against the nGrinder server
username: admin
passwd: YWRtaW4=
# the interval between between nGrinder test status polling, default to 1s
pollInterval: 5sWhen the canary analysis starts, the load tester will initiate a
clone_and_start request
to the nGrinder server and start a new performance test. the load tester will periodically
poll the nGrinder server for the status of the test,
and prevent duplicate requests from being sent in subsequent analysis loops.
K6 Load Tester
You can also delegate load testing to a third-party webhook. An example of this is the k6 webhook. This webhook uses k6, a very featureful load tester, to run load or smoke tests on canaries. For all features available, see the source repository.
Here's an example integrating this webhook as a pre-rollout step, to load test a service before any traffic is sent to it:
webhooks:
- name: k6-load-test
timeout: 5m
type: pre-rollout
url: http://k6-loadtester.flagger/launch-test
metadata:
script: |
import http from 'k6/http';
import { sleep } from 'k6';
export const options = {
vus: 2,
duration: '30s',
thresholds: {
http_req_duration: ['p(95)<50']
},
ext: {
loadimpact: {
name: '<cluster>/<your_service>',
projectID: <project id>,
},
},
};
export default function () {
http.get('http://<your_service>-canary.<namespace>:80/');
sleep(0.10);
}Integration Testing
Flagger comes with a testing service that can run Helm tests, Bats tests or Concord tests when configured as a webhook.
Deploy the Helm test runner in the kube-system namespace using the tiller service account:
helm repo add flagger https://flagger.app
helm upgrade -i flagger-helmtester flagger/loadtester \
--namespace=kube-system \
--set serviceAccountName=tillerWhen deployed the Helm tester API will be available at http://flagger-helmtester.kube-system/.
Now you can add pre-rollout webhooks to the canary analysis spec:
analysis:
webhooks:
- name: "smoke test"
type: pre-rollout
url: http://flagger-helmtester.kube-system/
timeout: 3m
metadata:
type: "helm"
cmd: "test {{ .Release.Name }} --cleanup"When the canary analysis starts, Flagger will call the pre-rollout webhooks before routing traffic to the canary.
If the helm test fails, Flagger will retry until the analysis threshold is reached and the canary is rolled back.
If you are using Helm v3,
you'll have to create a dedicated service account and add the release namespace to the test command:
analysis:
webhooks:
- name: "smoke test"
type: pre-rollout
url: http://flagger-helmtester.kube-system/
timeout: 3m
metadata:
type: "helmv3"
cmd: "test {{ .Release.Name }} --timeout 3m -n {{ .Release.Namespace }}"If the test hangs or logs error messages hinting to insufficient permissions it can be related to RBAC,
check the Troubleshooting section for an example configuration.
As an alternative to Helm you can use the
Bash Automated Testing System to run your tests.
analysis:
webhooks:
- name: "acceptance tests"
type: pre-rollout
url: http://flagger-batstester.default/
timeout: 5m
metadata:
type: "bash"
cmd: "bats /tests/acceptance.bats"Note that you should create a ConfigMap with your Bats tests and mount it inside the tester container.
You can also configure the test runner to start a Concord process.
analysis:
webhooks:
- name: "concord integration test"
type: pre-rollout
url: http://flagger-concordtester.default/
timeout: 60s
metadata:
type: "concord"
org: "your-concord-org"
project: "your-concord-project"
repo: "your-concord-repo"
entrypoint: "your-concord-entrypoint"
apiKeyPath: "/tmp/concord-api-key"
endpoint: "https://canary-endpoint/"
pollInterval: "5"
pollTimeout: "60"org, project, repo and entrypoint represents where your test process runs in Concord.
In order to authenticate to Concord, you need to set apiKeyPath
to a path of a file containing a valid Concord API key on the flagger-helmtester container.
This can be done via mounting a Kubernetes secret in the tester's Deployment.pollInterval represents the interval in seconds the web-hook will call Concord
to see if the process has finished (Default is 5s). pollTimeout represents the time in seconds
the web-hook will try to call Concord before timing out (Default is 30s).
If you need to start a Pod/Job to run tests, you can do so using kubectl.
analysis:
webhooks:
- name: "smoke test"
type: pre-rollout
url: http://flagger-kubectltester.kube-system/
timeout: 3m
metadata:
type: "kubectl"
cmd: "run test --image=alpine --overrides='{ "spec": { "serviceAccount": "default:default" } }'"Note that you need to setup RBAC for the load tester service account in order to run kubectl and helm commands.
Manual Gating
For manual approval of a canary deployment you can use the confirm-rollout and confirm-promotion webhooks.
The confirmation rollout hooks are executed before the pre-rollout hooks. For manually approving traffic weight increase,
you can use the confirm-traffic-increase webhook.
Flagger will halt the canary traffic shifting and analysis until the confirm webhook returns HTTP status 200.
For manual rollback of a canary deployment you can use the rollback webhook.
The rollback hook will be called during the analysis and confirmation states.
If a rollback webhook returns a successful HTTP status code,
Flagger will shift all traffic back to the primary instance and fail the canary.
Manual gating with Flagger's tester:
analysis:
webhooks:
- name: "gate"
type: confirm-rollout
url: http://flagger-loadtester.test/gate/haltThe /gate/halt returns HTTP 403 thus blocking the rollout.
If you have notifications enabled, Flagger will post a message to
Slack or MS Teams if a canary rollout is waiting for approval.
The notifications can be disabled with:
analysis:
webhooks:
- name: "gate"
type: confirm-rollout
url: http://flagger-loadtester.test/gate/halt
muteAlert: trueChange the URL to /gate/approve to start the canary analysis:
analysis:
webhooks:
- name: "gate"
type: confirm-rollout
url: http://flagger-loadtester.test/gate/approveManual gating can be driven with Flagger's tester API. Set the confirmation URL to /gate/check:
analysis:
webhooks:
- name: "ask for confirmation"
type: confirm-rollout
url: http://flagger-loadtester.test/gate/checkBy default the gate is closed, you can start or resume the canary rollout with:
kubectl -n test exec -it flagger-loadtester-xxxx-xxxx sh
curl -d '{"name": "podinfo","namespace":"test"}' http://localhost:8080/gate/openYou can pause the rollout at any time with:
curl -d '{"name": "podinfo","namespace":"test"}' http://localhost:8080/gate/closeIf a canary analysis is paused the status will change to waiting:
kubectl get canary/podinfo
NAME STATUS WEIGHT
podinfo Waiting 0The confirm-promotion hook type can be used to manually approve the canary promotion.
While the promotion is paused, Flagger will continue to run the metrics checks and load tests.
analysis:
webhooks:
- name: "promotion gate"
type: confirm-promotion
url: http://flagger-loadtester.test/gate/haltThe rollback hook type can be used to manually rollback the canary promotion.
As with gating, rollbacks can be driven with Flagger's tester API by setting the rollback URL to /rollback/check
analysis:
webhooks:
- name: "rollback"
type: rollback
url: http://flagger-loadtester.test/rollback/checkBy default, rollback is closed, you can rollback a canary rollout with:
kubectl -n test exec -it flagger-loadtester-xxxx-xxxx sh
curl -d '{"name": "podinfo","namespace":"test"}' http://localhost:8080/rollback/openYou can close the rollback with:
curl -d '{"name": "podinfo","namespace":"test"}' http://localhost:8080/rollback/closeIf you have notifications enabled, Flagger will post a message to Slack or MS Teams if a canary has been rolled back.
Troubleshooting
Manually check if helm test is running
To debug in depth any issues with helm tests, you can execute commands on the flagger-loadtester pod.
kubectl exec -it deploy/flagger-loadtester -- bash
helmv3 test <release> -n <namespace> --debugHelm tests hang during canary deployment
If test execution hangs or displays insufficient permissions, check your RBAC settings.
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: helm-smoke-tester
rules:
- apiGroups: [""]
resources: ["secrets"]
verbs: ["get", "watch", "list", "update"]
# choose the permission based on helm test type (Pod or Job)
- apiGroups: [""]
resources: ["pods", "pods/log"]
verbs: ["create", "list", "delete", "watch"]
- apiGroups: ["batch"]
resources: ["jobs", "jobs/log"]
verbs: ["create", "list", "delete", "watch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: helm-smoke-tester
# Don't forget to update accordingly
namespace: namespace-of-the-tested-release
subjects:
- kind: User
name: system:serviceaccount:linkerd:default
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: ClusterRole
name: helm-smoke-tester
apiGroup: rbac.authorization.k8s.io--- METRICS ---
- Files Extracted: 37
- Estimated Token Budget: ~83148 tokens
- Canonical Reference: https://codewiki.google/github.com/fluxcd/flagger