All Products
Search
Document Center

Container Service for Kubernetes:Perform canary releases with ALB Ingresses in an ACK cluster

Last Updated:Jun 23, 2026

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:

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/order when 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.

  1. Create tea-deploy.yaml and 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: 80
    kubectl apply -f tea-deploy.yaml
  2. Create tea-svc.yaml and apply it:

    apiVersion: v1
    kind: Service
    metadata:
      name: tea-svc
    spec:
      ports:
      - port: 80
        targetPort: 80
        protocol: TCP
      selector:
        app: tea
      type: NodePort
    kubectl apply -f tea-svc.yaml
  3. Create tea-ingress.yaml and 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: 80
    kubectl 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 host field must exactly match the production Ingress host (demo.domain.ingress.top in this example).

  • Header-based and weight-based rules must be on separate Ingresses.

  1. Create canary-deploy.yaml and 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: 80
    kubectl apply -f canary-deploy.yaml
  2. Create canary-svc.yaml and apply it:

    apiVersion: v1
    kind: Service
    metadata:
      name: canary-svc
    spec:
      ports:
      - port: 80
        targetPort: 80
        protocol: TCP
      selector:
        app: canary
      type: NodePort
    kubectl apply -f canary-svc.yaml
  3. Create a header-based canary Ingress. Requests with location: hz route to canary-svc; other headers fall through to the next rule. Create canary-header-ingress.yaml and 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: Prefix
    kubectl apply -f canary-header-ingress.yaml
  4. Create a weight-based canary Ingress. Non-matching requests split 50/50 between production and canary Services. Create canary-weight-ingress.yaml and 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: Prefix
    kubectl apply -f canary-weight-ingress.yaml

Verify the canary release

  1. Get the ALB instance address:

    kubectl get ing

    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. Verify that requests carrying location: hz always 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; done

    All 10 responses return new. The header rule has highest priority and routes all matching requests to the canary.

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

    Responses alternate between new (canary) and old (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.

  1. Update tea-ingress.yaml to point to canary-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: 80
    kubectl apply -f tea-ingress.yaml
  2. Delete the canary Ingresses:

    kubectl delete ing canary-weight-ingress canary-header-ingress
  3. 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; done

    All 10 responses return new. The old Service version is fully deprecated.

Next steps

References