When you adopt Alibaba Cloud Service Mesh (ASM), you need to move ingress traffic handling from NGINX Ingress Controller to the ASM ingress gateway. This guide walks you through a zero-downtime migration that reuses your existing Classic Load Balancer (CLB) instance and IP address, so DNS records stay unchanged. If issues occur at any point, reset weights to route all traffic back to NGINX Ingress immediately.
How it works
Both NGINX Ingress Controller and the ASM ingress gateway run side by side behind the same CLB instance. Traffic distribution between them is controlled by CLB backend weights. You start with all traffic on NGINX Ingress, gradually shift it to the ASM gateway, and decommission NGINX Ingress after the migration is complete.

The migration follows five steps:
Detach the CLB from NGINX Ingress -- Make the existing CLB instance reusable so that both NGINX Ingress and the ASM gateway can share it.
Create an ASM gateway -- Deploy the ASM ingress gateway and attach it to the same CLB with an initial weight of 0 (no traffic).
Convert Ingress resources to Istio configurations -- Translate your NGINX Ingress rules into Istio Gateway and VirtualService resources.
Verify the configuration -- Send test traffic to confirm that the ASM gateway routes requests correctly.
Shift traffic -- Gradually increase the ASM gateway weight until it handles all traffic.
Because both gateways share the same CLB, your external IP address and DNS records remain unchanged throughout the migration. NGINX Ingress continues to serve production traffic until you explicitly shift weights. To roll back at any point, set the ASM gateway weight to 0 and the NGINX Ingress weight to 100.
Prerequisites
Before you begin, make sure that you have:
An ASM Enterprise Edition or Ultimate Edition instance of the latest version. For more information, see Create an ASM instance
A Container Service for Kubernetes (ACK) cluster added to the ASM instance. For more information, see Add a cluster to an ASM instance
Step 1: Make the NGINX Ingress CLB instance reusable
By default, ACK manages the CLB instance for NGINX Ingress and prevents manual changes. To share this CLB with the ASM gateway, detach it from ACK management first.
Get the CLB instance ID
Run the following command to retrieve the CLB instance ID:
kubectl -n kube-system get svc nginx-ingress-lb -o yaml | grep service.k8s.alibaba/loadbalancer-idExpected output:
service.k8s.alibaba/loadbalancer-id: lb-bp1gts52ced2vgaw1ni78Record the CLB instance ID (for example, lb-bp1gts52ced2vgaw1ni78). You need it in later steps.
Update CLB settings in the console
Open the Server Load Balancer console.
Find your CLB instance and make the following changes:
Disable configuration read-only mode.
Delete the
kubernetes.do.not.deleteandack.aliyun.comtags.Rename the vServer groups that start with
k8s/to theshared-<port>format. For example, renamek8s/80/nginx-ingress-lb/kube-system/c553a74e6ad13423aa839c8e5********toshared-80.
Add annotations to the NGINX Ingress Service
Add the following annotations to the NGINX Ingress Service so that it references the CLB explicitly instead of relying on ACK-managed discovery:
metadata:
annotations:
service.beta.kubernetes.io/alibaba-cloud-loadbalancer-id: "<your-clb-instance-id>"
service.beta.kubernetes.io/alibaba-cloud-loadbalancer-force-override-listeners: "false"
service.beta.kubernetes.io/alibaba-cloud-loadbalancer-vgroup-port: "<your-vgroup-id-1>:80,<your-vgroup-id-2>:443"
service.beta.kubernetes.io/alibaba-cloud-loadbalancer-weight: "100"Replace the following placeholders with actual values:
| Placeholder | Description | Example |
|---|---|---|
<your-clb-instance-id> | CLB instance ID from the previous step | lb-bp1gts52ced2vgaw1ni78 |
<your-vgroup-id-1> | vServer group ID for port 80 | rsp-bp1k5xxxxxxx |
<your-vgroup-id-2> | vServer group ID for port 443 | rsp-bp1k5yyyyyyy |
Use the vServer group ID (not the display name such as shared-80). To find the ID, check the vServer group details in the Server Load Balancer console.
Step 2: Create an ASM gateway
Create an ASM gateway by using YAML. To generate a base YAML file, open the ASM gateway creation page in the ASM console, configure the visual form, and click the preview button.
Edit the serviceAnnotations section to reuse the same CLB instance. The annotations are identical to Step 1 except that the weight is "0", which prevents the ASM gateway from receiving traffic initially.
serviceAnnotations:
service.beta.kubernetes.io/alibaba-cloud-loadbalancer-id: "<your-clb-instance-id>"
service.beta.kubernetes.io/alibaba-cloud-loadbalancer-force-override-listeners: "false"
service.beta.kubernetes.io/alibaba-cloud-loadbalancer-vgroup-port: "<your-vgroup-id>:80"
service.beta.kubernetes.io/alibaba-cloud-loadbalancer-weight: "0"The following table explains each annotation:
| Annotation | Value | Purpose |
|---|---|---|
alibaba-cloud-loadbalancer-id | Your CLB instance ID | Reuses the existing CLB |
alibaba-cloud-loadbalancer-force-override-listeners | "false" | Prevents Istio from overriding existing CLB listeners (Istio overrides listeners by default) |
alibaba-cloud-loadbalancer-vgroup-port | vServer group ID and port mapping | Routes traffic to the correct backend group |
alibaba-cloud-loadbalancer-weight | "0" | Starts with no traffic on the ASM gateway |
Step 3: Convert Ingress resources to Istio configurations
Translate each NGINX Ingress resource into an Istio Gateway and VirtualService pair. The following example shows how to convert a basic Ingress rule with URL rewriting.
NGINX Ingress (before)
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: helloworld
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /$2
spec:
rules:
- http:
paths:
- backend:
serviceName: helloworld
servicePort: 80
path: /helloworld(/|$)(.*)
host: example.comIstio VirtualService (after)
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
name: example-vs
spec:
gateways:
- istio-system/ingressgateway # Replace with your ASM gateway name
hosts:
- example.com
http:
- name: route-helloworld
match:
- uri:
prefix: /helloworld/
- uri:
prefix: /helloworld
rewrite:
uri: /
route:
- destination:
host: helloworld
port:
number: 80Key differences between Ingress and VirtualService
The following table summarizes the mapping between NGINX Ingress fields and their Istio VirtualService equivalents:
| Aspect | NGINX Ingress | Istio VirtualService |
|---|---|---|
| Routing target | serviceName and servicePort | destination.host and destination.port.number |
| URL rewriting | rewrite-target annotation | rewrite.uri field |
| Host matching | rules[].host | hosts[] |
| Gateway binding | Implicit (the Ingress controller) | Explicit (gateways[] field) |
Common NGINX Ingress annotation equivalents
If you use advanced NGINX Ingress annotations, refer to the following table for Istio equivalents:
| NGINX Ingress annotation | Istio equivalent | Resource type |
|---|---|---|
nginx.ingress.kubernetes.io/rewrite-target | http[].rewrite.uri | VirtualService |
nginx.ingress.kubernetes.io/ssl-redirect | tls section in Gateway | Gateway |
nginx.ingress.kubernetes.io/proxy-read-timeout | timeout field | VirtualService |
nginx.ingress.kubernetes.io/upstream-hash-by | consistentHash in trafficPolicy | DestinationRule |
nginx.ingress.kubernetes.io/cors-enable | corsPolicy field | VirtualService |
Cross-namespace routing
If a VirtualService and its target Service are in the same namespace, use the short service name (for example, helloworld). If they are in different namespaces, use the Fully Qualified Domain Name (FQDN) format:
<service-name>.<namespace>.svc.cluster.localPlace the VirtualService and DestinationRule in the same namespace as the target Service Deployment whenever possible. This simplifies routing configuration and avoids FQDN requirements.
Step 4: Verify the configuration
Before shifting production traffic, verify that the ASM gateway handles requests correctly.
Option A: Test through a temporary CLB
Create a new CLB instance and point it to the ASM ingress gateway. Send test requests to this CLB:
curl -s -I -H "Host: example.com" http://<test-clb-ip>/helloworld/Expected output (key headers):
HTTP/1.1 200 OK
server: istio-envoyThe server: istio-envoy header confirms that traffic is flowing through the ASM gateway.
Option B: Test from inside the cluster
Send requests directly to the ASM ingress gateway Service within the cluster:
# Get the ASM ingress gateway ClusterIP
GATEWAY_IP=$(kubectl -n istio-system get svc istio-ingressgateway -o jsonpath='{.spec.clusterIP}')
# Send a test request
curl -s -I -H "Host: example.com" http://$GATEWAY_IP/helloworld/Confirm that the response matches what NGINX Ingress returns for the same request. The server: istio-envoy header in the response indicates that traffic is flowing through the ASM gateway.
Step 5: Shift traffic from NGINX Ingress to the ASM gateway
After verification, gradually shift production traffic to the ASM gateway by adjusting CLB backend weights.
Recommended migration schedule
Increase the ASM gateway weight incrementally and monitor at each stage:
| Stage | ASM gateway weight | NGINX Ingress weight | Action |
|---|---|---|---|
| 1 | 1 | 99 | Verify with minimal production traffic |
| 2 | 10 | 90 | Monitor error rates and latency |
| 3 | 50 | 50 | Confirm steady-state behavior |
| 4 | 100 | 0 | Complete migration |
Adjust weights
ASM gateway weight: Update the service.beta.kubernetes.io/alibaba-cloud-loadbalancer-weight annotation in the IstioGateway serviceAnnotations:
serviceAnnotations:
service.beta.kubernetes.io/alibaba-cloud-loadbalancer-weight: "50" # Adjust this valueNGINX Ingress weight: Update the service.beta.kubernetes.io/alibaba-cloud-loadbalancer-weight annotation on the NGINX Ingress Service. If this annotation is not configured, adjust the weight directly in the CLB console.
Rollback
If issues occur during traffic migration, set the ASM gateway weight back to "0" and the NGINX Ingress weight back to "100":
# ASM gateway: stop receiving traffic
serviceAnnotations:
service.beta.kubernetes.io/alibaba-cloud-loadbalancer-weight: "0"# NGINX Ingress Service: restore full traffic
metadata:
annotations:
service.beta.kubernetes.io/alibaba-cloud-loadbalancer-weight: "100"All traffic returns to NGINX Ingress immediately. No DNS changes are needed because both gateways share the same CLB instance.