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:
| Priority | Meaning |
|---|---|
| 0 | Same zone as the caller (highest priority) |
| 1 | Different 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:
An ACK cluster added to your ASM instance. For more information, see Add a cluster to an ASM instance
Cluster nodes that span at least two availability zones. To verify, check the zone of each Elastic Compute Service (ECS) instance in the Container Service Management Console. For more information, see Regions and zones
Step 1: Deploy sample applications
This walkthrough uses two applications:
sleep -- a curl-based client, deployed in
cn-hongkong-bhelloworld -- the target service, with v1 in
cn-hongkong-band v2 incn-hongkong-c
Replace cn-hongkong-b and cn-hongkong-c with the zones where your cluster nodes reside.
Deploy the sleep client
Create
sleep.yaml:Apply the manifest:
kubectl apply -f sleep.yaml
Deploy the helloworld service
Create
helloworld.yaml: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 helloworldIn 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::0Equal 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.
Create
helloworld-failover.yaml: Key fields:Field Purpose 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: 1sApply the DestinationRule:
kubectl apply -f helloworld-failover.yamlVerify that endpoint priorities have changed: The same-zone endpoint (
cn-hongkong-b) now haspriority::0, and the cross-zone endpoint (cn-hongkong-c) haspriority::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 helloworldoutbound|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/helloExpected output:
Hello version: v1, instance: helloworld-v1-6f88967849-sq2h2Every response comes from v1 (the same-zone endpoint in cn-hongkong-b).
Test cross-zone failover
Scale helloworld-v1 to zero replicas to simulate an outage in
cn-hongkong-b:kubectl scale deploy helloworld-v1 --replicas=0Wait 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/helloHello version: v2, instance: helloworld-v2-75db5f978d-s7v4k
Restore the same-zone endpoint
Scale helloworld-v1 back to one replica:
kubectl scale deploy helloworld-v1 --replicas=1Wait 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/helloHello version: v1, instance: helloworld-v1-6f88967849-sq2h2