All Products
Search
Document Center

Container Compute Service:Use ALB Ingresses to perform canary releases

Last Updated:Mar 26, 2026

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:

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/order annotation (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.

  1. Create tea-deploy.yaml with the following content and run kubectl 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: 80
  2. Create tea-svc.yaml with the following content and run kubectl 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: ClusterIP
  3. Create tea-ingress.yaml with the following content and run kubectl 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: 80
  4. Verify 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.com

    Expected 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: hz header to the new version.

  • Weight-based: Routes 50% of all other requests (requests with unmatched headers or no headers) to the new version.

  1. Create canary-deploy.yaml with the following content and run kubectl 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: 80
  2. Create canary-svc.yaml with the following content and run kubectl 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: ClusterIP
  3. Create a header-based canary Ingress. Save the following as canary-header-ingress.yaml and run kubectl apply -f canary-header-ingress.yaml:

    AnnotationValueEffect
    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: hz to 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: Prefix

    Key annotations:

  4. Create a weight-based canary Ingress. Save the following as canary-weight-ingress.yaml and run kubectl 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: Prefix

    alb.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.

  5. Verify the canary release. The canary release is working: location: hz requests always reach the new version, while all other requests are split 50/50 between old and new.

    1. Confirm all three Ingresses are created and share the same ALB address:

      kubectl get ingress

      Expected 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
    2. Test header-based routing. Requests with location: hz must always reach the new version:

      curl -H Host:demo.domain.ingress.top -H "location:hz" http://alb-ny3ute4r8yevni****.cn-chengdu.alb.aliyuncs.com

      Expected output:

      new
    3. Test 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; done

      Expected output (order may vary):

      old
      new
      new
      old
      new
      old
      new
      old
      new
      old
    4. Test with a non-matching header. Requests with location: bj (which does not match hz) 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; done

      Expected 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.

  1. Update tea-ingress.yaml to point to canary-svc instead of tea-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: 80
  2. Apply the updated Ingress:

    kubectl apply -f tea-ingress.yaml
  3. Verify 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.com

    All three commands return new, confirming all traffic is now served by the new version.

  4. Delete the canary Ingresses:

    kubectl delete ingress canary-weight-ingress canary-header-ingress

What's next