Canary releases let you shift traffic progressively to a new service version, keeping your production system stable during updates. Application Load Balancer (ALB) Ingresses support canary releases through annotations that route traffic based on headers, cookies, or weights. This topic walks through a full canary release: deploying a new version alongside the current one, splitting traffic, and then completing the cutover.
Prerequisites
Before you begin, ensure that you have:
An ACS cluster. See Create an ACS cluster.
The ALB Ingress controller installed in the cluster. See Manage the ALB Ingress controller.
An AlbConfig and an IngressClass created. See Create an AlbConfig and Create an IngressClass.
kubectl access to the cluster. See Obtain the kubeconfig file of a cluster and use kubectl to connect to the cluster and Use kubectl on Cloud Shell to manage ACS clusters.
Usage notes
Rule priority: Header > Cookie > Weight. When multiple canary rules are active, header-based rules take precedence. See ALB Ingress canary annotations for the full annotation reference.
Combining rule types: Header-based and cookie-based rules cannot be placed on the same Ingress as weight-based rules. Use two separate Ingresses — one for header/cookie routing, one for weight-based routing. For complex routing conditions, see Customize the routing rules of an ALB Ingress.
Routing order: When multiple canary Ingresses are active, the ALB Ingress controller applies routing rules in the lexicographical order of Ingress namespaces and names. Use the
alb.ingress.kubernetes.io/orderannotation (valid values: 1–1000, default: 10) to control priority explicitly. A lower value means higher priority.
Step 1: Deploy the current service version
Deploy a Deployment, Service, and Ingress for the current (old) version of your application.
Create
tea-deploy.yamlwith the following content and runkubectl apply -f tea-deploy.yaml:apiVersion: apps/v1 kind: Deployment metadata: name: tea spec: replicas: 1 selector: matchLabels: app: tea template: metadata: labels: app: tea spec: containers: - name: tea image: registry.cn-hangzhou.aliyuncs.com/acs-sample/old-nginx:latest ports: - containerPort: 80Create
tea-svc.yamlwith the following content and runkubectl apply -f tea-svc.yaml:apiVersion: v1 kind: Service metadata: name: tea-svc spec: ports: - port: 80 targetPort: 80 protocol: TCP selector: app: tea type: ClusterIPCreate
tea-ingress.yamlwith the following content and runkubectl apply -f tea-ingress.yaml:apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: tea-ingress spec: ingressClassName: alb rules: - host: demo.domain.ingress.top # Replace with your domain name, resolved to the ALB instance IP. http: paths: - path: / pathType: Prefix backend: service: name: tea-svc port: number: 80Verify the deployment. Run the following command to confirm the service is serving traffic:
curl -H Host:demo.domain.ingress.top http://alb-ny3ute4r8yevni****.cn-chengdu.alb.aliyuncs.comExpected output:
old
Step 2: Perform a canary release
Deploy the new service version alongside the current one. This step configures two canary Ingresses:
Header-based: Routes all requests with the
location: hzheader to the new version.Weight-based: Routes 50% of all other requests (requests with unmatched headers or no headers) to the new version.
Create
canary-deploy.yamlwith the following content and runkubectl apply -f canary-deploy.yaml:apiVersion: apps/v1 kind: Deployment metadata: name: canary spec: replicas: 1 selector: matchLabels: app: canary template: metadata: labels: app: canary spec: containers: - name: canary image: registry.cn-hangzhou.aliyuncs.com/acs-sample/new-nginx:latest ports: - containerPort: 80Create
canary-svc.yamlwith the following content and runkubectl apply -f canary-svc.yaml:apiVersion: v1 kind: Service metadata: name: canary-svc spec: ports: - port: 80 targetPort: 80 protocol: TCP selector: app: canary type: ClusterIPCreate a header-based canary Ingress. Save the following as
canary-header-ingress.yamland runkubectl apply -f canary-header-ingress.yaml:Annotation Value Effect alb.ingress.kubernetes.io/canary"true"Marks this Ingress as a canary Ingress alb.ingress.kubernetes.io/canary-by-header"location"The header key to match alb.ingress.kubernetes.io/canary-by-header-value"hz"Routes requests with location: hzto the new version. Requests with any other value fall through to the next applicable rule.apiVersion: networking.k8s.io/v1 kind: Ingress metadata: annotations: alb.ingress.kubernetes.io/canary: "true" alb.ingress.kubernetes.io/canary-by-header: "location" alb.ingress.kubernetes.io/canary-by-header-value: "hz" name: canary-header-ingress namespace: default spec: ingressClassName: alb rules: - host: demo.domain.ingress.top # Replace with your domain name, resolved to the ALB instance IP. http: paths: - backend: service: name: canary-svc port: number: 80 path: / pathType: PrefixKey annotations:
Create a weight-based canary Ingress. Save the following as
canary-weight-ingress.yamland runkubectl apply -f canary-weight-ingress.yaml:apiVersion: networking.k8s.io/v1 kind: Ingress metadata: annotations: alb.ingress.kubernetes.io/canary: "true" alb.ingress.kubernetes.io/canary-weight: "50" name: canary-weight-ingress namespace: default spec: ingressClassName: alb rules: - host: demo.domain.ingress.top # Replace with your domain name, resolved to the ALB instance IP. http: paths: - backend: service: name: canary-svc port: number: 80 path: / pathType: Prefixalb.ingress.kubernetes.io/canary-weight: "50"specifies the percentage of traffic routed to the new service version. In this example, the value is set to 50, which indicates that 50% of the traffic is routed to the new service version.Verify the canary release. The canary release is working:
location: hzrequests always reach the new version, while all other requests are split 50/50 between old and new.Confirm all three Ingresses are created and share the same ALB address:
kubectl get ingressExpected output:
NAME CLASS HOSTS ADDRESS PORTS AGE canary-header-ingress alb demo.domain.ingress.top alb-ny3ute4r8yevni****.cn-chengdu.alb.aliyuncs.com 80 8m23s canary-weight-ingress alb demo.domain.ingress.top alb-ny3ute4r8yevni****.cn-chengdu.alb.aliyuncs.com 80 8m16s tea-ingress alb demo.domain.ingress.top alb-ny3ute4r8yevni****.cn-chengdu.alb.aliyuncs.com 80 7m5sTest header-based routing. Requests with
location: hzmust always reach the new version:curl -H Host:demo.domain.ingress.top -H "location:hz" http://alb-ny3ute4r8yevni****.cn-chengdu.alb.aliyuncs.comExpected output:
newTest weight-based routing. Run the following loop to observe that approximately 50% of requests reach the new version:
for i in $(seq 1 10); do curl -s -H Host:demo.domain.ingress.top http://alb-ny3ute4r8yevni****.cn-chengdu.alb.aliyuncs.com; doneExpected output (order may vary):
old new new old new old new old new oldTest with a non-matching header. Requests with
location: bj(which does not matchhz) fall through to the weight-based rule, so approximately 50% reach the new version:for i in $(seq 1 10); do curl -s -H Host:demo.domain.ingress.top -H "location:bj" http://alb-ny3ute4r8yevni****.cn-chengdu.alb.aliyuncs.com; doneExpected output (approximately 50%
new, 50%old).
Step 3: Complete the cutover
After the new version runs stably, cut all traffic over to it and remove the canary Ingresses.
Update
tea-ingress.yamlto point tocanary-svcinstead oftea-svc:apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: tea-ingress spec: ingressClassName: alb rules: - host: demo.domain.ingress.top # Replace with your domain name, resolved to the ALB instance IP. http: paths: - path: / pathType: Prefix backend: service: name: canary-svc # Changed from tea-svc to canary-svc. port: number: 80Apply the updated Ingress:
kubectl apply -f tea-ingress.yamlVerify all traffic is routed to the new version. Run each of the following commands and confirm the output is
new:# Requests with location: hz header curl -H Host:demo.domain.ingress.top -H "location:hz" http://alb-ny3ute4r8yevni****.cn-chengdu.alb.aliyuncs.com # Requests without headers curl -H Host:demo.domain.ingress.top http://alb-ny3ute4r8yevni****.cn-chengdu.alb.aliyuncs.com # Requests with location: bj header curl -H Host:demo.domain.ingress.top -H "location:bj" http://alb-ny3ute4r8yevni****.cn-chengdu.alb.aliyuncs.comAll three commands return
new, confirming all traffic is now served by the new version.Delete the canary Ingresses:
kubectl delete ingress canary-weight-ingress canary-header-ingress
What's next
For more ALB Ingress configurations — including domain-based routing, HTTP-to-HTTPS redirects, and additional canary patterns — see Advanced ALB Ingress configurations.
If you encounter network issues after configuration, see ALB Ingress controller troubleshooting and ALB Ingress FAQ.