When multiple microservices are released together, each service version must be tested as a complete call chain without affecting production traffic. Traffic lanes isolate versioned service call chains into independent runtime environments, so a request entering a lane stays within that version end to end. If a version in a lane becomes unavailable, traffic shifting redirects requests to a designated fallback version to prevent disruptions.
The following steps cover the full setup in Service Mesh (ASM): defining version subsets with DestinationRules, routing inter-service traffic by version with VirtualServices, directing ingress traffic to specific lanes via HTTP headers, and configuring fallback when a lane's target is unavailable.
How traffic lanes work
A traffic lane is built from three Istio traffic management primitives:
| Primitive | Role in traffic lanes |
|---|---|
| DestinationRule | Declares version subsets (v1, v2, v3) for each service based on pod labels. Defines *which versions exist*. |
| VirtualService | Routes inter-service traffic based on sourceLabels, keeping requests within the same version lane. Defines *where traffic goes*. |
| Gateway + VirtualService | Routes ingress traffic to the correct lane based on an HTTP header (x-asm-prefer-tag). Serves as the *entry point* into a lane. |
The following diagram shows three lanes, each containing a complete v1, v2, or v3 call chain:
┌─────────────────────────────────────┐
x-asm-prefer-tag: │ Lane v1: mocka v1 → mockb v1 → mockc v1 │
v1 └─────────────────────────────────────┘
┌─────────────────────────────────────┐
x-asm-prefer-tag: │ Lane v2: mocka v2 → mockb v2 → mockc v2 │
v2 └─────────────────────────────────────┘
┌─────────────────────────────────────┐
x-asm-prefer-tag: │ Lane v3: mocka v3 → mockb v3 → mockc v3 │
v3 └─────────────────────────────────────┘Traffic shifting adds a fallback mechanism: if a service version in a lane is unavailable, ASM redirects traffic to a designated fallback version (typically v1).
Prerequisites
Before you begin, make sure that you have:
An ASM instance of Enterprise Edition or Ultimate Edition, version 1.17 or later. See Create an ASM instance or Update an ASM instance
A Kubernetes cluster added to the ASM instance. See Add a cluster to an ASM instance
An ASM gateway named
ingressgateway. See Create an ingress gateway
Step 1: Deploy sample services
This tutorial uses three sample services -- mocka, mockb, and mockc -- each deployed in three versions (v1, v2, v3). The call chain flows from mocka to mockb to mockc.
Enable automatic sidecar proxy injection for the
defaultnamespace. For details, see the "Enable automatic sidecar injection" section of the Manage global namespaces topic, or see Enable automatic sidecar proxy injection.Deploy the sample services using the kubeconfig file of the cluster on the data plane:
kubectl apply -f https://alibabacloudservicemesh.oss-cn-beijing.aliyuncs.com/asm-labs/swimlane/v1/application-v1.yaml kubectl apply -f https://alibabacloudservicemesh.oss-cn-beijing.aliyuncs.com/asm-labs/swimlane/v2/application-v2.yaml kubectl apply -f https://alibabacloudservicemesh.oss-cn-beijing.aliyuncs.com/asm-labs/swimlane/v3/application-v3.yaml
Step 2: Define version subsets with DestinationRules
DestinationRules declare the version subsets available for each service. Each subset maps to pods with a matching version label.
Create a file named
dr-mock.yamlwith the following content: Key fields:Field Description namespace: istio-systemApplied in the ASM control plane namespace, not the application namespace. hostFully qualified service name, such as mocka.default.svc.cluster.local.subsets[].labels.versionMatches the versionlabel on pods deployed in Step 1.Apply the DestinationRule using the kubeconfig file of the ASM instance:
kubectl apply -f dr-mock.yaml
Step 3: Route traffic within lanes using VirtualServices
VirtualServices route inter-service traffic based on sourceLabels, keeping requests within the same version lane. When mocka v2 calls mockb, the VirtualService matches the source label version: v2 and routes the request to mockb v2.
Create a file named
vs-mock.yamlwith the following content: Key fields: Routing rules are evaluated top to bottom. The first match wins.Field Description sourceLabelsMatches the versionlabel of the *calling* pod. A v2 caller reaches only v2 destinations, keeping traffic inside the lane.subsetReferences the subset name defined in the DestinationRule (Step 2). Apply the VirtualService using the kubeconfig file of the ASM instance:
kubectl apply -f vs-mock.yaml
Step 4: Route ingress traffic to lanes via HTTP headers
A Gateway and a gateway-level VirtualService direct incoming requests to the correct lane based on the x-asm-prefer-tag HTTP header. During canary testing, a QA engineer or CI/CD pipeline adds x-asm-prefer-tag: v2 to test requests, routing them to the v2 lane while production traffic continues flowing to v1.
Create a file named
gw-mock.yamlwith the following content: Key fields:Field Description selector: istio: ingressgatewayBinds the Gateway to the ASM ingress gateway pods. x-asm-prefer-tagDetermines which lane receives the request. The value must exactly match a version (v1, v2, or v3). uri: exact: /mockBoth header and URI must match for the route to apply. Apply the Gateway and routing rules using the kubeconfig file of the ASM instance:
kubectl apply -f gw-mock.yaml
Step 5: Verify the traffic lanes
Get the public IP address of the ASM gateway. For details, see Step 2 in Integrate KServe with ASM.
Set the gateway IP as an environment variable. Replace
xxx.xxx.xxx.xxxwith the actual IP address:export ASM_GATEWAY_IP=xxx.xxx.xxx.xxxSend test requests to each lane and verify that traffic stays within the correct version. Test the v1 lane: Expected output: All three services respond with version v1, confirming that traffic stays within the v1 lane. Test the v2 lane: Expected output: Test the v3 lane: Expected output: Each lane routes traffic exclusively to services of the matching version, confirming that lane isolation works correctly.
for i in {1..100}; do curl -H 'x-asm-prefer-tag: v1' http://${ASM_GATEWAY_IP}/mock ; echo ''; sleep 1; done;-> mocka(version: v1, ip: 172.17.0.54)-> mockb(version: v1, ip: 172.17.0.129)-> mockc(version: v1, ip: 172.17.0.130)for i in {1..100}; do curl -H 'x-asm-prefer-tag: v2' http://${ASM_GATEWAY_IP}/mock ; echo ''; sleep 1; done;-> mocka(version: v2, ip: 172.17.0.9)-> mockb(version: v2, ip: 172.17.0.126)-> mockc(version: v2, ip: 172.17.0.128)for i in {1..100}; do curl -H 'x-asm-prefer-tag: v3' http://${ASM_GATEWAY_IP}/mock ; echo ''; sleep 1; done;-> mocka(version: v3, ip: 172.17.0.132)-> mockb(version: v3, ip: 172.17.0.127)-> mockc(version: v3, ip: 172.17.0.69)
Step 6: Add traffic shifting with fallback
Traffic shifting adds resilience to traffic lanes. With a fallback configuration, ASM redirects traffic to a stable version (v1) when the target version is unavailable, preventing service disruptions during canary releases.
Replace the content of
vs-mock.yamlwith the following configuration. This adds afallbackblock to the v2 and v3 routes, directing traffic to the v1 subset when the target version is unreachable: Key fields:Field Description fallback.target.hostandfallback.target.subsetThe fallback destination. Here, both mockbandmockcfall back to v1.v1 route (no fallback) v1 serves as the stable baseline, so it does not need a fallback target. Per-route fallback Each version's fallback target is configured independently. Apply the updated VirtualService using the kubeconfig file of the ASM instance:
kubectl apply -f vs-mock.yaml
Step 7: Verify traffic shifting
Simulate a service failure to confirm that fallback routing works.
Log on to the ACK console. In the left-side navigation pane, click Clusters.
On the Clusters page, click the name of the target cluster, then choose Workloads > Deployments in the left-side navigation pane.
On the Deployments page, find the mockb-v2 workload and click Scale in the Actions column. In the Scale dialog box, set Desired Number of Pods to 1 and click OK. Click Confirm to apply. This simulates a failure of the
mockbv2 service.Send test requests to the v2 lane: Expected output: The request enters through
mocka v2(matching thex-asm-prefer-tag: v2header). Becausemockb v2is unavailable, traffic shifts tomockb v1, which then routes tomockc v1through the v1 lane. Traffic shifting works as expected.for i in {1..100}; do curl -H 'x-asm-prefer-tag: v2' http://${ASM_GATEWAY_IP}/mock ; echo ''; sleep 1; done;-> mocka(version: v2, ip: 172.17.0.9)-> mockb(version: v1, ip: 172.17.0.126)-> mockc(version: v1, ip: 172.17.0.128)
Clean up resources
To remove all resources created in this tutorial:
# Delete traffic rules (using ASM kubeconfig)
kubectl delete -f gw-mock.yaml
kubectl delete -f vs-mock.yaml
kubectl delete -f dr-mock.yaml
# Delete sample services (using cluster kubeconfig)
kubectl delete -f https://alibabacloudservicemesh.oss-cn-beijing.aliyuncs.com/asm-labs/swimlane/v1/application-v1.yaml
kubectl delete -f https://alibabacloudservicemesh.oss-cn-beijing.aliyuncs.com/asm-labs/swimlane/v2/application-v2.yaml
kubectl delete -f https://alibabacloudservicemesh.oss-cn-beijing.aliyuncs.com/asm-labs/swimlane/v3/application-v3.yaml