All Products
Search
Document Center

Alibaba Cloud Service Mesh:Enable zone-aware routing

Last Updated:Mar 11, 2026

Cross-zone traffic adds latency and incurs additional data transfer costs. Zone-aware routing keeps requests within the same availability zone whenever healthy endpoints exist, reducing both. When same-zone endpoints become unavailable, traffic automatically fails over to the next closest zone.

Service Mesh (ASM) supports zone-aware routing (also called intra-zone routing) through Istio locality load balancing. No application code changes are required. This topic walks through enabling zone-aware routing for a sample service deployed across two zones. In the following example, an ingress gateway is used to enable access to an HTTPBin application.

How it works

When a client initiates a request to access a service, the request is routed to the service on the same node or in the same zone as the client based on the topology information about the region and zone where the client resides. Each endpoint inherits the region, zone, and sub-zone of its node. When you enable locality load balancing through a DestinationRule, the sidecar proxy assigns priority levels to endpoints:

PriorityMeaning
0Same zone as the caller (highest priority)
1Different zone, same region

Traffic goes to priority-0 endpoints first. If those become unhealthy (detected through outlier detection), the proxy fails over to the next priority level.

Prerequisites

Before you begin, make sure that you have:

Step 1: Deploy sample applications

This walkthrough uses two applications:

  • sleep -- a curl-based client, deployed in cn-hongkong-b

  • helloworld -- the target service, with v1 in cn-hongkong-b and v2 in cn-hongkong-c

Note

Replace cn-hongkong-b and cn-hongkong-c with the zones where your cluster nodes reside.

Deploy the sleep client

  1. Create sleep.yaml:

    View sleep.yaml

    # Copyright Istio Authors
    #
    #   Licensed under the Apache License, Version 2.0 (the "License");
    #   you may not use this file except in compliance with the License.
    #   You may obtain a copy of the License at
    #
    #       http://www.apache.org/licenses/LICENSE-2.0
    #
    #   Unless required by applicable law or agreed to in writing, software
    #   distributed under the License is distributed on an "AS IS" BASIS,
    #   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    #   See the License for the specific language governing permissions and
    #   limitations under the License.
    
    ##################################################################################################
    # Sleep service
    ##################################################################################################
    apiVersion: v1
    kind: ServiceAccount
    metadata:
      name: sleep
    ---
    apiVersion: v1
    kind: Service
    metadata:
      name: sleep
      labels:
        app: sleep
        service: sleep
    spec:
      ports:
      - port: 80
        name: http
      selector:
        app: sleep
    ---
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: sleep
    spec:
      replicas: 1
      selector:
        matchLabels:
          app: sleep
      template:
        metadata:
          labels:
            app: sleep
        spec:
          affinity:
            nodeAffinity:
              requiredDuringSchedulingIgnoredDuringExecution:
                nodeSelectorTerms:
                - matchExpressions:
                  - key: failure-domain.beta.kubernetes.io/zone
                    operator: In
                    values:
                      - 'cn-hongkong-b'
          terminationGracePeriodSeconds: 0
          serviceAccountName: sleep
          containers:
          - name: sleep
            image: curlimages/curl
            command: ["/bin/sleep", "3650d"]
            imagePullPolicy: IfNotPresent
            volumeMounts:
            - mountPath: /etc/sleep/tls
              name: secret-volume
          volumes:
          - name: secret-volume
            secret:
              secretName: sleep-secret
              optional: true
  2. Apply the manifest:

    kubectl apply -f sleep.yaml

Deploy the helloworld service

  1. Create helloworld.yaml:

    View helloworld.yaml

    apiVersion: v1
    kind: Service
    metadata:
      name: helloworld
      labels:
        app: helloworld
        service: helloworld
    spec:
      ports:
      - port: 5000
        name: http
      selector:
        app: helloworld
    ---
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: helloworld-v1
      labels:
        app: helloworld
        version: v1
    spec:
      replicas: 1
      selector:
        matchLabels:
          app: helloworld
          version: v1
      template:
        metadata:
          labels:
            app: helloworld
            version: v1
        spec:
          affinity:
            nodeAffinity:
              requiredDuringSchedulingIgnoredDuringExecution:
                nodeSelectorTerms:
                - matchExpressions:
                  - key: failure-domain.beta.kubernetes.io/zone
                    operator: In
                    values:
                      - 'cn-hongkong-b'
          containers:
          - name: helloworld
            image: docker.io/istio/examples-helloworld-v1
            resources:
              requests:
                cpu: "100m"
            imagePullPolicy: IfNotPresent
            ports:
            - containerPort: 5000
    ---
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: helloworld-v2
      labels:
        app: helloworld
        version: v2
    spec:
      replicas: 1
      selector:
        matchLabels:
          app: helloworld
          version: v2
      template:
        metadata:
          labels:
            app: helloworld
            version: v2
        spec:
          affinity:
            nodeAffinity:
              requiredDuringSchedulingIgnoredDuringExecution:
                nodeSelectorTerms:
                - matchExpressions:
                  - key: failure-domain.beta.kubernetes.io/zone
                    operator: In
                    values:
                      - 'cn-hongkong-c'
          containers:
          - name: helloworld
            image: docker.io/istio/examples-helloworld-v2
            resources:
              requests:
                cpu: "100m"
            imagePullPolicy: IfNotPresent
            ports:
            - containerPort: 5000
  2. Apply the manifest:

    kubectl apply -f helloworld.yaml

Verify endpoint registration

Query the Envoy cluster information from the sleep pod to confirm that both helloworld endpoints are registered with zone metadata:

kubectl exec "$(kubectl get pod -l app=sleep -o jsonpath='{.items[0].metadata.name}')" \
  -c sleep -- curl -s localhost:15000/clusters | grep helloworld

In the output, look for zone and priority fields. Before zone-aware routing is enabled, both endpoints share the same priority (priority::0):

outbound|5000||helloworld.default.svc.cluster.local::172.28.32.49:5000::zone::cn-hongkong-b
outbound|5000||helloworld.default.svc.cluster.local::172.28.32.49:5000::priority::0
...
outbound|5000||helloworld.default.svc.cluster.local::172.28.33.155:5000::zone::cn-hongkong-c
outbound|5000||helloworld.default.svc.cluster.local::172.28.33.155:5000::priority::0

Equal priorities mean the proxy distributes traffic across both endpoints without zone preference.

Step 2: Enable zone-aware routing with a DestinationRule

Create a DestinationRule that enables locality load balancing and configures outlier detection for the helloworld service. Outlier detection is required for failover -- without it, the proxy cannot detect unhealthy endpoints or reroute traffic to another zone.

  1. Create helloworld-failover.yaml: Key fields:

    FieldPurpose
    localityLbSetting.enabledActivates zone-aware routing
    outlierDetectionDetects unhealthy endpoints and triggers failover to the next zone
    consecutive5xxErrors: 1Ejects an endpoint after a single 5xx error (aggressive, for demonstration only)
    baseEjectionTime: 1mKeeps ejected endpoints out for 1 minute
    interval: 1sChecks endpoint health every second
    maxRequestsPerConnection: 1Forces a new connection per request (for demonstration only)
    apiVersion: networking.istio.io/v1beta1
    kind: DestinationRule
    metadata:
      name: helloworld-failover
      namespace: default
    spec:
      host: helloworld.default.svc.cluster.local
      trafficPolicy:
        connectionPool:
          http:
            maxRequestsPerConnection: 1
        loadBalancer:
          localityLbSetting:
            enabled: true
          simple: ROUND_ROBIN
        outlierDetection:
          baseEjectionTime: 1m
          consecutive5xxErrors: 1
          interval: 1s
  2. Apply the DestinationRule:

    kubectl apply -f helloworld-failover.yaml
  3. Verify that endpoint priorities have changed: The same-zone endpoint (cn-hongkong-b) now has priority::0, and the cross-zone endpoint (cn-hongkong-c) has priority::1: Different priorities confirm that zone-aware routing is active. The proxy sends all traffic to the priority-0 (same-zone) endpoint first.

    kubectl exec "$(kubectl get pod -l app=sleep -o jsonpath='{.items[0].metadata.name}')" \
      -c sleep -- curl -s localhost:15000/clusters | grep helloworld
    outbound|5000||helloworld.default.svc.cluster.local::172.28.32.49:5000::zone::cn-hongkong-b
    outbound|5000||helloworld.default.svc.cluster.local::172.28.32.49:5000::priority::0
    ...
    outbound|5000||helloworld.default.svc.cluster.local::172.28.33.155:5000::zone::cn-hongkong-c
    outbound|5000||helloworld.default.svc.cluster.local::172.28.33.155:5000::priority::1

Step 3: Verify routing behavior

With zone-aware routing enabled, all requests from the sleep pod (cn-hongkong-b) route to helloworld-v1 in the same zone. The following steps confirm this behavior and test failover when the same-zone endpoint becomes unavailable.

Confirm same-zone routing

Run the following command several times:

kubectl exec -c sleep \
  "$(kubectl get pod -l app=sleep -o jsonpath='{.items[0].metadata.name}')" \
  -- curl -sSL helloworld:5000/hello

Expected output:

Hello version: v1, instance: helloworld-v1-6f88967849-sq2h2

Every response comes from v1 (the same-zone endpoint in cn-hongkong-b).

Test cross-zone failover

  1. Scale helloworld-v1 to zero replicas to simulate an outage in cn-hongkong-b:

    kubectl scale deploy helloworld-v1 --replicas=0
  2. Wait a few seconds, then send requests again: Expected output: Traffic now routes to v2 in cn-hongkong-c, confirming that cross-zone failover works when same-zone endpoints are unavailable.

    kubectl exec -c sleep \
      "$(kubectl get pod -l app=sleep -o jsonpath='{.items[0].metadata.name}')" \
      -- curl -sSL helloworld:5000/hello
    Hello version: v2, instance: helloworld-v2-75db5f978d-s7v4k

Restore the same-zone endpoint

  1. Scale helloworld-v1 back to one replica:

    kubectl scale deploy helloworld-v1 --replicas=1
  2. Wait a few seconds, then verify that traffic returns to v1: Expected output: Traffic routes back to the same-zone endpoint after it recovers.

    kubectl exec -c sleep \
      "$(kubectl get pod -l app=sleep -o jsonpath='{.items[0].metadata.name}')" \
      -- curl -sSL helloworld:5000/hello
    Hello version: v1, instance: helloworld-v1-6f88967849-sq2h2