During service upgrades, use canary releases to maintain system stability. ALB Ingress supports canary annotations by header, cookie, or weight (priority: header > cookie > weight). Route a portion of traffic to the new Service version in production, validate it, then promote it when ready.
Prerequisites
Complete the following:
-
Two vSwitches in different zones in the same VPC as the ACK cluster. See Create and manage a vSwitch.
-
ALB Ingress controller installed in the cluster. See Manage the ALB Ingress controller.
For ACK dedicated clusters, grant the cluster permissions to access ALB Ingress. See Authorize an ACK dedicated cluster to access the ALB Ingress controller.
-
AlbConfig created. See Create an AlbConfig.
-
kubectl connected to the ACK cluster. See Obtain the kubeconfig file of a cluster and use kubectl to connect to the cluster.
Canary annotations
Every canary Ingress requires the alb.ingress.kubernetes.io/canary: "true" annotation. Without it, the canary Ingress conflicts with production instead of routing alongside it.
These annotations control traffic splitting:
| Annotation | Description |
|---|---|
alb.ingress.kubernetes.io/canary |
Marks the Ingress as canary. Set to "true". Required on every canary Ingress. |
alb.ingress.kubernetes.io/canary-by-header |
Request header key to match. Matching requests route to the canary Service. |
alb.ingress.kubernetes.io/canary-by-header-value |
Header value to match. Requires canary-by-header; ignored if that annotation is unset. |
alb.ingress.kubernetes.io/canary-weight |
Traffic percentage (0–100) for the canary Service when no header or cookie rule matches. 0 sends none; 100 sends all. |
alb.ingress.kubernetes.io/order |
Routing rule evaluation order. Valid values: 1–1000. Default: 10. Lower values have higher priority. Without this annotation, order follows Ingress namespace and name lexicographically. |
Rule priority: header-based > cookie-based > weight-based.
Usage notes
-
Header-based and cookie-based rules cannot share an Ingress with weight-based rules. Use separate Ingresses, or custom routing rules for complex conditions.
-
The canary Ingress host must exactly match the production Ingress host.
-
Set explicit priority with
alb.ingress.kubernetes.io/orderwhen multiple canary Ingresses share a host instead of relying on lexicographical ordering.
Step 1: Deploy the production Service
Deploy the production Deployment, Service, and Ingress.
-
Create
tea-deploy.yamland apply it: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: 80kubectl apply -f tea-deploy.yaml -
Create
tea-svc.yamland apply it:apiVersion: v1 kind: Service metadata: name: tea-svc spec: ports: - port: 80 targetPort: 80 protocol: TCP selector: app: tea type: NodePortkubectl apply -f tea-svc.yaml -
Create
tea-ingress.yamland apply it: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 load balancer IP. http: paths: - path: / pathType: Prefix backend: service: name: tea-svc port: number: 80kubectl apply -f tea-ingress.yaml
Step 2: Deploy the canary Service and routing rules
Deploy the canary Service with two canary Ingresses: one routes by header, the other splits remaining traffic by weight.
Before applying canary Ingresses:
-
Set
alb.ingress.kubernetes.io/canary: "true"on every canary Ingress. Without it, the canary Ingress conflicts with production. -
The
hostfield must exactly match the production Ingress host (demo.domain.ingress.topin this example). -
Header-based and weight-based rules must be on separate Ingresses.
-
Create
canary-deploy.yamland apply it: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: 80kubectl apply -f canary-deploy.yaml -
Create
canary-svc.yamland apply it:apiVersion: v1 kind: Service metadata: name: canary-svc spec: ports: - port: 80 targetPort: 80 protocol: TCP selector: app: canary type: NodePortkubectl apply -f canary-svc.yaml -
Create a header-based canary Ingress. Requests with
location: hzroute tocanary-svc; other headers fall through to the next rule. Createcanary-header-ingress.yamland apply it: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 # Must match the production Ingress host exactly. http: paths: - backend: service: name: canary-svc port: number: 80 path: / pathType: Prefixkubectl apply -f canary-header-ingress.yaml -
Create a weight-based canary Ingress. Non-matching requests split 50/50 between production and canary Services. Create
canary-weight-ingress.yamland apply it: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 # Must match the production Ingress host exactly. http: paths: - backend: service: name: canary-svc port: number: 80 path: / pathType: Prefixkubectl apply -f canary-weight-ingress.yaml
Verify the canary release
-
Get the ALB instance address:
kubectl get ingExpected 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 7m5s -
Verify that requests carrying
location: hzalways reach the canary:for i in $(seq 1 10); do curl -s -H "Host:demo.domain.ingress.top" -H "location:hz" http://alb-ny3ute4r8yevni****.cn-chengdu.alb.aliyuncs.com; doneAll 10 responses return
new. The header rule has highest priority and routes all matching requests to the canary. -
Verify that requests without a matching header are split ~50/50:
for i in $(seq 1 10); do curl -s -H "Host:demo.domain.ingress.top" http://alb-ny3ute4r8yevni****.cn-chengdu.alb.aliyuncs.com; doneResponses alternate between
new(canary) andold(production), roughly half each. Unmatched headers (for example,location: bj) follow the same weight-based split.
Step 3: Promote the new version
After the canary stabilizes, redirect all production traffic to the new Service and delete the canary Ingresses.
-
Update
tea-ingress.yamlto point tocanary-svc:apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: tea-ingress spec: ingressClassName: alb rules: - host: demo.domain.ingress.top http: paths: - path: / pathType: Prefix backend: service: name: canary-svc # Changed from tea-svc. port: number: 80kubectl apply -f tea-ingress.yaml -
Delete the canary Ingresses:
kubectl delete ing canary-weight-ingress canary-header-ingress -
Verify that all traffic reaches 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; doneAll 10 responses return
new. The old Service version is fully deprecated.
Next steps
-
See Advanced ALB Ingress configurations for HTTP-to-HTTPS redirects, custom routing rules, and more.
-
See Ingress FAQ for troubleshooting.