Note
Access to this page requires authorization. You can try signing in or changing directories.
Access to this page requires authorization. You can try changing directories.
Important
AKS preview features are available on a self-service, opt-in basis. Previews are provided "as is" and "as available," and they're excluded from the service-level agreements and limited warranty. AKS previews are partially covered by customer support on a best-effort basis. As such, these features aren't meant for production use. For more information, see the following support articles:
Azure Kubernetes Application Network traffic management uses Istio ambient mode and the Kubernetes Gateway API to enable precise control over service-to-service interactions within and across clusters. This solution offers a comprehensive set of traffic management capabilities, including:
- L4/L7 authorization policies
- JWT claim-based routing
- Traffic shifting
- Fault injection
This article provides step-by-step instructions on how to implement these traffic management use cases using Azure Kubernetes Application Network across multiple Azure Kubernetes Service (AKS) clusters.
Prerequisites
Before you begin, ensure you have the following prerequisites in place:
Two existing AKS clusters with network reachability between their east-west gateways. You can achieve this using Azure virtual network (VNet) peering, VNet to VNet VPN connection, or another supported connectivity model.
- Both clusters must be joined as members to your Azure Kubernetes Application Network.
The Istio CLI, istioctl, installed. To select the correct version of istioctl, refer to the supported Istio version for your Application Network. You can install istioctl using the following command:
curl -sL https://istio.io/downloadIstioctl | sh - export PATH=$HOME/.istioctl/bin:$PATHSet the following environment variables for use throughout the article:
export AKS_RG=<resource-group-name> export CLUSTER_NAME_1=<cluster-1-name> export CLUSTER_NAME_2=<cluster-2-name>Configure kubectl to connect to your AKS clusters using the
az aks get-credentialscommand:az aks get-credentials --resource-group $AKS_RG --name $CLUSTER_NAME_1 --overwrite-existing az aks get-credentials --resource-group $AKS_RG --name $CLUSTER_NAME_2 --overwrite-existing
Check Istio and ztunnel installation
Check Istio CRDs using the
kubectl get crdscommand.kubectl --context $CLUSTER_NAME_1 get crds | grep istioYour output should show the following 14 CRDs for Istio ambient mode:
authorizationpolicies.security.istio.io 2026-03-12T18:02:11Z destinationrules.networking.istio.io 2026-03-12T18:02:11Z envoyfilters.networking.istio.io 2026-03-12T18:02:11Z gateways.networking.istio.io 2026-03-12T18:02:11Z peerauthentications.security.istio.io 2026-03-12T18:02:11Z proxyconfigs.networking.istio.io 2026-03-12T18:02:11Z requestauthentications.security.istio.io 2026-03-12T18:02:11Z serviceentries.networking.istio.io 2026-03-12T18:02:11Z sidecars.networking.istio.io 2026-03-12T18:02:11Z telemetries.telemetry.istio.io 2026-03-12T18:02:11Z virtualservices.networking.istio.io 2026-03-12T18:02:11Z wasmplugins.extensions.istio.io 2026-03-12T18:02:11Z workloadentries.networking.istio.io 2026-03-12T18:02:11Z workloadgroups.networking.istio.io 2026-03-12T18:02:11ZCheck Istio CNI and ztunnel DaemonSet are running in the
applink-systemnamespace using thekubectl get daemonsetcommand.kubectl --context $CLUSTER_NAME_1 --namespace applink-system get daemonsetYour output should show the following DaemonSets with all pods in
READYstatus:NAME DESIRED CURRENT READY UP-TO-DATE AVAILABLE NODE SELECTOR AGE istio-cni-node 3 3 3 3 3 kubernetes.io/os=linux 108m ztunnel 3 3 3 3 3 kubernetes.io/os=linux 107m
Create sample application
Application architecture
This architecture extends the Bookinfo sample application to run across multiple AKS clusters.
- Cluster 1 hosts:
productpage-v1(Python)reviews-v1(Java)details-v1(Ruby)
- Cluster 2 hosts:
reviews-v2(Java)reviews-v3(Java)ratings-v1(Node.js)
The productpage service sends requests to reviews.default.svc.cluster.local. Since the reviews service is available in both AKS clusters with global service scope, requests can be routed to any available backends (reviews-v1, reviews-v2, or reviews-v3) across clusters. The reviews-v2 and reviews-v3 services also call the ratings service to retrieve rating data before returning the response to productpage.
This architecture demonstrates how a distributed application can span across multiple clusters while continuing to use a consistent service name and communication pattern, enabling seamless service interaction across cluster boundaries.
Deploy the application services
Deploy the
productpageservices to AKS cluster 1 using the followingkubectl applycommands:kubectl --context $CLUSTER_NAME_1 apply -f https://raw.githubusercontent.com/istio/istio/release-1.29/samples/bookinfo/platform/kube/bookinfo.yaml -l app=productpage kubectl --context $CLUSTER_NAME_1 apply -f https://raw.githubusercontent.com/istio/istio/release-1.29/samples/bookinfo/platform/kube/bookinfo.yaml -l account=productpageDeploy the
reviews-v1services to AKS cluster 1 using the followingkubectl applycommands:kubectl --context $CLUSTER_NAME_1 apply -f https://raw.githubusercontent.com/istio/istio/release-1.29/samples/bookinfo/platform/kube/bookinfo.yaml -l app=reviews,service=reviews kubectl --context $CLUSTER_NAME_1 apply -f https://raw.githubusercontent.com/istio/istio/release-1.29/samples/bookinfo/platform/kube/bookinfo.yaml -l account=reviews kubectl --context $CLUSTER_NAME_1 apply -f https://raw.githubusercontent.com/istio/istio/release-1.29/samples/bookinfo/platform/kube/bookinfo.yaml -l app=reviews,version=v1Deploy the
detailsservices to AKS cluster 1 using the followingkubectl applycommands:kubectl --context $CLUSTER_NAME_1 apply -f https://raw.githubusercontent.com/istio/istio/release-1.29/samples/bookinfo/platform/kube/bookinfo.yaml -l app=details kubectl --context $CLUSTER_NAME_1 apply -f https://raw.githubusercontent.com/istio/istio/release-1.29/samples/bookinfo/platform/kube/bookinfo.yaml -l account=detailsDeploy the curl service to AKS cluster 1 using the following
kubectl applycommand:kubectl --context $CLUSTER_NAME_1 apply -f https://raw.githubusercontent.com/istio/istio/release-1.29/samples/curl/curl.yamlDeploy the
reviews-v2andreviews-v3services to AKS cluster 2 using the followingkubectl applycommands:kubectl --context $CLUSTER_NAME_2 apply -f https://raw.githubusercontent.com/istio/istio/release-1.29/samples/bookinfo/platform/kube/bookinfo.yaml -l app=reviews,service=reviews kubectl --context $CLUSTER_NAME_2 apply -f https://raw.githubusercontent.com/istio/istio/release-1.29/samples/bookinfo/platform/kube/bookinfo.yaml -l account=reviews kubectl --context $CLUSTER_NAME_2 apply -f https://raw.githubusercontent.com/istio/istio/release-1.29/samples/bookinfo/platform/kube/bookinfo.yaml -l 'app=reviews,version in (v2,v3)'Deploy the
ratingsservices to AKS cluster 2 using the followingkubectl applycommands:kubectl --context $CLUSTER_NAME_2 apply -f https://raw.githubusercontent.com/istio/istio/release-1.29/samples/bookinfo/platform/kube/bookinfo.yaml -l app=ratings kubectl --context $CLUSTER_NAME_2 apply -f https://raw.githubusercontent.com/istio/istio/release-1.29/samples/bookinfo/platform/kube/bookinfo.yaml -l account=ratings
Deploy the ingress gateway
Deploy the ingress gateway to AKS cluster 1 using the following
kubectl applycommand:kubectl --context $CLUSTER_NAME_1 apply -f https://raw.githubusercontent.com/istio/istio/release-1.29/samples/bookinfo/gateway-api/bookinfo-gateway.yamlVerify the gateway is programmed using the following
kubectl get gatewaycommand:kubectl --context $CLUSTER_NAME_1 --namespace default get gatewayAfter about one minute, you should see output similar to the following, with
PROGRAMMEDshowing asTrue:NAME CLASS ADDRESS PROGRAMMED AGE bookinfo-gateway istio 20.112.40.27 True 21s
Add application to the mesh
Add the application to the mesh by labeling the
defaultnamespace in both AKS clusters withistio.io/dataplane-mode=ambient:kubectl --context $CLUSTER_NAME_1 label namespace default istio.io/dataplane-mode=ambient kubectl --context $CLUSTER_NAME_2 label namespace default istio.io/dataplane-mode=ambient
Install waypoint and mark services as global
Install
waypointgateways in both clusters using the followingistioctl waypoint applycommands:istioctl --context $CLUSTER_NAME_1 waypoint apply --enroll-namespace --wait istioctl --context $CLUSTER_NAME_2 waypoint apply --enroll-namespace --waitYour output should look similar to the following, indicating that the
waypointhas been applied and thedefaultnamespace has been labeled correctly in both clusters:✅ waypoint default/waypoint applied ✅ namespace default labeled with "istio.io/use-waypoint: waypoint" ✅ waypoint default/waypoint applied ✅ namespace default labeled with "istio.io/use-waypoint: waypoint"Verify the
waypointgateways are programmed using the followingkubectl get gateway waypointcommand in both clusters:kubectl --context $CLUSTER_NAME_1 get gateway waypoint kubectl --context $CLUSTER_NAME_2 get gateway waypointAfter about one minute, you should see output similar to the following in both clusters, with
PROGRAMMEDshowing asTrue:NAME CLASS ADDRESS PROGRAMMED AGE waypoint istio-waypoint 10.1.78.229 True 28sMark the
reviews-v1service in AKS cluster 1 andreviews-v2andreviews-v3services in AKS cluster 2 as global services by labeling them withistio.io/global="true":kubectl --context $CLUSTER_NAME_1 label service reviews istio.io/global="true" kubectl --context $CLUSTER_NAME_2 label service reviews istio.io/global="true"Mark the
waypointservices as global services by labeling them withistio.io/global="true"in both clusters:kubectl --context $CLUSTER_NAME_1 label service waypoint istio.io/global="true" kubectl --context $CLUSTER_NAME_2 label service waypoint istio.io/global="true"Verify global services from ztunnel configs in both clusters using the
istioctl zc servicecommand. You should see thereviewsandwaypointservices from both clusters with their respective VIPs underSERVICE VIPcolumn.istioctl --context "$CLUSTER_NAME_1" zc service -n applink-system | awk 'NR==1 || $2=="reviews" || $2=="waypoint"' istioctl --context "$CLUSTER_NAME_2" zc service -n applink-system | awk 'NR==1 || $2=="reviews" || $2=="waypoint"'Your output should look similar to the following in both clusters, indicating that the
reviewsandwaypointservices are recognized as global services with their VIPs:# Output from cluster 1 NAMESPACE SERVICE NAME SERVICE VIP WAYPOINT ENDPOINTS default reviews 10.1.36.199,10.1.164.195 waypoint 2/2 default waypoint 10.1.78.229,10.1.135.117 None 2/2 # Output from cluster 2 NAMESPACE SERVICE NAME SERVICE VIP WAYPOINT ENDPOINTS default reviews 10.1.36.199,10.1.164.195 waypoint 3/3 default waypoint 10.1.78.229,10.1.135.117 None 2/2
Verify application access
Note
If you can't access the application from the internet, make sure the network security group (NSG) associated with your cluster allows inbound internet traffic on TCP port 80 to the ingress gateway.
Access the application through the ingress gateway using the following commands:
export BOOK_INFO_GTW_IP=$(kubectl --context $CLUSTER_NAME_1 -n default get gateway bookinfo-gateway -o jsonpath="{.status.addresses[0].value}") echo $BOOK_INFO_GTW_IP curl -I http://$BOOK_INFO_GTW_IP/productpageYour output should look similar to the following, indicating that the application is accessible through the gateway:
20.112.40.27 HTTP/1.1 200 OK server: istio-envoy date: Fri, 13 Mar 2026 17:52:13 GMT content-type: text/html; charset=utf-8 content-length: 15070 vary: Cookie x-envoy-upstream-service-time: 571
Deploy L4/L7 authorization policies
Enforce L4 authorization policy
Apply an L4 authorization policy to allow traffic to the
productpageservice only from the ingress gateway using the followingkubectl applycommand:kubectl --context $CLUSTER_NAME_1 apply -f - <<EOF apiVersion: security.istio.io/v1 kind: AuthorizationPolicy metadata: name: productpage-ztunnel namespace: default spec: selector: matchLabels: app: productpage action: ALLOW rules: - from: - source: principals: - cluster.local/ns/default/sa/bookinfo-gateway-istio EOFVerify you can still access the application through the ingress gateway using the following command:
curl -I http://$BOOK_INFO_GTW_IP/productpageYour output should look similar to the following, indicating that the application is still accessible through the gateway:
HTTP/1.1 200 OK server: istio-envoy date: Fri, 13 Mar 2026 18:00:51 GMT content-type: text/html; charset=utf-8 content-length: 15068 vary: Cookie x-envoy-upstream-service-time: 19Verify that access to the
productpageservice is blocked from other sources by trying to access it from the curl pod using the following command:kubectl --context $CLUSTER_NAME_1 exec deploy/curl -- curl -s "http://productpage:9080/productpage"Your output should look similar to the following, indicating that access is denied due to the L4 authorization policy:
upstream connect error or disconnect/reset before headers. reset reason: connection termination
Enforce L7 authorization policy
Apply an L7 authorization policy to allow only GET requests to the
productpageservice from the curl service account using the followingkubectl applycommand:kubectl --context $CLUSTER_NAME_1 apply -f - <<EOF apiVersion: security.istio.io/v1 kind: AuthorizationPolicy metadata: name: productpage-waypoint namespace: default spec: targetRefs: - kind: Service group: "" name: productpage action: ALLOW rules: - from: - source: principals: - cluster.local/ns/default/sa/curl to: - operation: methods: ["GET"] EOFUpdate the L4 authorization policy to allow traffic from the waypoint to ztunnel by adding the
waypointservice account as an allowed principal using the followingkubectl applycommand:kubectl --context $CLUSTER_NAME_1 apply -f - <<EOF apiVersion: security.istio.io/v1 kind: AuthorizationPolicy metadata: name: productpage-ztunnel namespace: default spec: selector: matchLabels: app: productpage action: ALLOW rules: - from: - source: principals: - cluster.local/ns/default/sa/bookinfo-gateway-istio - cluster.local/ns/default/sa/waypoint EOFVerify
DELETEoperations to theproductpageservice are blocked from the curl service account using the followingkubectl execcommand:kubectl --context $CLUSTER_NAME_1 exec deploy/curl -- curl -s "http://productpage:9080/productpage" -X DELETEYour output should look similar to the following, indicating that access is denied due to the L7 authorization policy:
RBAC: access deniedVerify service accounts other than curl are blocked from accessing the
productpageservice using the followingkubectl execcommand:kubectl --context $CLUSTER_NAME_1 exec deploy/reviews-v1 -- curl -s http://productpage:9080/productpageYour output should look similar to the following, indicating that access is denied due to the L7 authorization policy:
RBAC: access deniedVerify that
GETrequests to theproductpageservice are allowed from the curl service account using the followingkubectl execcommand:kubectl --context $CLUSTER_NAME_1 exec deploy/curl -- curl -s http://productpage:9080/productpage | grep -o "<title>.*</title>"Your output should look similar to the following, indicating that GET requests are allowed and the
productpageis accessible:<title>Simple Bookstore App</title>Verify the
productpageis still accessible through the ingress gateway using the following command:curl -I http://$BOOK_INFO_GTW_IP/productpageYour output should look similar to the following, indicating that the
productpageis still accessible through the gateway:HTTP/1.1 200 OK server: istio-envoy date: Fri, 13 Mar 2026 18:07:02 GMT content-type: text/html; charset=utf-8 content-length: 15070 vary: Cookie x-envoy-upstream-service-time: 45
Implement JWT claim-based routing
Create ingress gateway and secure it with JWT authentication
Create a new namespace for the ingress gateway using the
kubectl create namespacecommand.kubectl --context $CLUSTER_NAME_1 create namespace applink-ingressCreate an ingress gateway using the Kubernetes Gateway API and allow only routes from the same namespace using the following
kubectl applycommand:kubectl --context $CLUSTER_NAME_1 apply -f - <<EOF apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: applink-ingressgateway namespace: applink-ingress labels: app: applink-ingressgateway istio: applink-ingressgateway spec: gatewayClassName: istio listeners: - name: http port: 80 protocol: HTTP allowedRoutes: namespaces: from: Same EOFVerify the gateway is programmed using the following
kubectl get gatewaycommand:kubectl --context $CLUSTER_NAME_1 -n applink-ingress get gatewayAfter about one minute, you should see output similar to the following, with
PROGRAMMEDshowing asTrue:NAME CLASS ADDRESS PROGRAMMED AGE applink-ingressgateway istio 20.99.227.197 True 30sGet the OIDC issuer URL from the AKS cluster and set it as an environment variable.
export OIDC_ISSUER_URL=$(az aks show --resource-group $AKS_RG --name $CLUSTER_NAME_1 --query "oidcIssuerProfile.issuerUrl" -o tsv) echo $OIDC_ISSUER_URLApply a RequestAuthentication policy to validate JWT tokens issued by the AKS cluster OIDC issuer and forward the original token to the
reviewsservice, and a VirtualService to route traffic to thereviewsservice based on thesubclaim in the JWT token using the followingkubectl applycommand:envsubst <<EOF | kubectl --context $CLUSTER_NAME_1 apply -f - apiVersion: security.istio.io/v1 kind: RequestAuthentication metadata: name: bookinfo-ingress-jwt-istio namespace: applink-ingress spec: selector: matchLabels: app: applink-ingressgateway jwtRules: - issuer: "${OIDC_ISSUER_URL}" audiences: - "${OIDC_ISSUER_URL}" jwksUri: "${OIDC_ISSUER_URL}openid/v1/jwks" forwardOriginalToken: true --- apiVersion: networking.istio.io/v1 kind: Gateway metadata: name: bookinfo-gateway-istio namespace: applink-ingress spec: selector: app: applink-ingressgateway servers: - hosts: - "*" port: name: http number: 80 protocol: HTTP --- apiVersion: networking.istio.io/v1 kind: VirtualService metadata: name: bookinfo-vs-istio namespace: applink-ingress spec: hosts: - "*" gateways: - bookinfo-gateway-istio http: - name: reviews match: - uri: prefix: / headers: "@request.auth.claims.sub": exact: "system:serviceaccount:default:curl" route: - destination: host: reviews.default.svc.cluster.local port: number: 9080 EOFGet the IP address of the new ingress gateway and set it as an environment variable.
export NO_REV_GTW_IP=$(kubectl --context $CLUSTER_NAME_1 -n applink-ingress get gateway applink-ingressgateway -o jsonpath="{.status.addresses[0].value}") echo $NO_REV_GTW_IP
Verify JWT authentication and claim-based routing
Verify requests without JWT token are rejected by the gateway using the following
kubectl execcommand:kubectl --context $CLUSTER_NAME_1 exec -it $(kubectl --context $CLUSTER_NAME_1 get pod -l app=curl -o jsonpath='{.items[0].metadata.name}') -- curl -v http://$NO_REV_GTW_IP/reviews/1Your output should look similar to the following, indicating that the request is rejected with a 404 Not Found error because the JWT token is missing:
* Trying 20.99.227.197:80... * Established connection to 20.99.227.197 (20.99.227.197 port 80) from 10.244.2.0 port 36736 * using HTTP/1.x > GET /reviews/1 HTTP/1.1 > Host: 20.99.227.197 > User-Agent: curl/8.16.0 > Accept: */* > * Request completely sent off < HTTP/1.1 404 Not Found < date: Fri, 13 Mar 2026 18:30:34 GMT < server: istio-envoy < content-length: 0 < * Connection #0 to host 20.99.227.197:80 left intactVerify requests with invalid JWT token are rejected by the gateway using the following
kubectl execcommand:kubectl --context $CLUSTER_NAME_1 exec -it $(kubectl --context $CLUSTER_NAME_1 get pod -l app=curl -o jsonpath='{.items[0].metadata.name}') -- curl -v -H "Authorization: Bearer 123.456.789" http://$NO_REV_GTW_IP/reviews/1Your output should look similar to the following, indicating that the request is rejected with a 401 Unauthorized error because the JWT token is invalid:
* Trying 20.99.227.197:80... * Established connection to 20.99.227.197 (20.99.227.197 port 80) from 10.244.2.0 port 33240 * using HTTP/1.x > GET /reviews/1 HTTP/1.1 > Host: 20.99.227.197 > User-Agent: curl/8.16.0 > Accept: */* > Authorization: Bearer 123.456.789 > * Request completely sent off < HTTP/1.1 401 Unauthorized < www-authenticate: Bearer realm="http://20.99.227.197/reviews/1", error="invalid_token" < content-length: 29 < content-type: text/plain < date: Fri, 13 Mar 2026 18:31:40 GMT < server: istio-envoy < * Connection #0 to host 20.99.227.197:80 left intact Jwt header is an invalid JSONVerify requests with valid JWT token are allowed by the gateway and routed to the
reviewsservice using the followingkubectl execcommand. The JWT token is obtained from the service account token mounted in the curl pod, which is issued by the AKS cluster OIDC issuer.kubectl --context $CLUSTER_NAME_1 exec -it $(kubectl --context $CLUSTER_NAME1 get pod -l app=curl -o jsonpath='{.items[0].metadata.name}') -- sh -c "export NO_REV_GTW_IP=$NO_REV_GTW_IP; exec sh"Run the following command in the interactive shell to send a request with the valid JWT token to the gateway:
curl -H "Authorization: Bearer $(cat /var/run/secrets/kubernetes.io/serviceaccount/token)" http://$NO_REV_GTW_IP/health; echoYour output should look similar to the following, indicating that the request is allowed and the
reviewsservice is healthy:{"status": "Reviews is healthy"}Run the following command in the interactive shell to send a request with the valid JWT token to the gateway to access the
reviewsservice. The request should be allowed and routed to thereviewsservice because thesubclaim in the JWT token matches the value specified in the VirtualService route match condition.curl -H "Authorization: Bearer $(cat /var/run/secrets/kubernetes.io/serviceaccount/token)" http://$NO_REV_GTW_IP/reviews/1; echoYour output should look similar to the following, indicating that the request is allowed and routed to the
reviewsservice, returning the review for product 1:{"id": "1","podname": "reviews-v1-12ab34c5de-fg6hi","clustername": "null","reviews": [{ "reviewer": "Reviewer1", "text": "An extremely entertaining play by Shakespeare. The slapstick humour is refreshing!"},{ "reviewer": "Reviewer2", "text": "Absolutely fun and entertaining. The play lacks thematic depth when compared to other plays by Shakespeare."}]}
Implement cross-cluster traffic shifting
We implement cross-cluster traffic shifting using the following setup across the two AKS clusters:
- AKS cluster 1: The
reviews-localservices selects onlyreviews-v1pods, and thereviews-remoteservice intentionally selects no local pods. - AKS cluster 2: The
reviews-localservice intentionally selects no local pods, and thereviews-remoteservice selects bothreviews-v2andreviews-v3pods.
In this section, the terms local and remote are defined from the perspective of AKS cluster 1.
Apply the following Service resources in AKS cluster 1 to set up the
reviews-localandreviews-remoteservices with the appropriate selectors using thekubectl applycommand:kubectl --context $CLUSTER_NAME_1 apply -f - <<EOF apiVersion: v1 kind: Service metadata: name: reviews-local namespace: default labels: app: reviews service: reviews-local istio.io/global: "true" istio.io/use-waypoint: waypoint spec: ports: - name: http port: 9080 targetPort: 9080 selector: app: reviews version: v1 --- apiVersion: v1 kind: Service metadata: name: reviews-remote namespace: default labels: app: reviews service: reviews-remote istio.io/global: "true" istio.io/use-waypoint: waypoint spec: ports: - name: http port: 9080 targetPort: 9080 selector: app: reviews version: remote EOFApply the following Service resources in AKS cluster 2 to set up the
reviews-localandreviews-remoteservices with the appropriate selectors using thekubectl applycommand:kubectl --context $CLUSTER_NAME_2 apply -f - <<EOF apiVersion: v1 kind: Service metadata: name: reviews-local namespace: default labels: app: reviews service: reviews-local istio.io/global: "true" istio.io/use-waypoint: waypoint spec: ports: - name: http port: 9080 targetPort: 9080 selector: app: reviews version: local --- apiVersion: v1 kind: Service metadata: name: reviews-remote namespace: default labels: app: reviews service: reviews-remote istio.io/global: "true" istio.io/use-waypoint: waypoint spec: ports: - name: http port: 9080 targetPort: 9080 selector: app: reviews EOFSplit traffic for the
reviewsservice20/80between the local and remote clusters using the followingkubectl applycommand:kubectl --context $CLUSTER_NAME1 apply -f - <<EOF apiVersion: networking.istio.io/v1 kind: VirtualService metadata: name: reviews namespace: default spec: hosts: - reviews http: - route: - destination: host: reviews-local.default.svc.cluster.local port: number: 9080 weight: 20 - destination: host: reviews-remote.default.svc.cluster.local port: number: 9080 weight: 80 EOFVerify the settings using the following script (feel free to change the weights and test it yourself).
The
Connection: closeheader only closes the client-side HTTP/1.1 connection. In ambient multicluster, the east-west gateway and waypoint can still reuse HBONE connections, so requests sent tocluster-2might stay pinned to eitherreviews-v2orreviews-v3within a single run. Usecluster-1versuscluster-2as the authoritative verification signal for the20/80split, and treat the star color as an illustrative detail only.results=() for i in $(seq 1 20); do html=$(curl -sS --http1.1 -H 'Connection: close' http://$BOOKINFO_GTW_IP/productpage) pod=$(printf '%s' "$html" | grep -oE 'reviews-v[0-9]-[a-z0-9-]+' | head -n1) if printf '%s' "$pod" | grep -q '^reviews-v1-'; then target=cluster-1 else target=cluster-2 fi if printf '%s' "$html" | grep -q 'text-red-500'; then star=red elif printf '%s' "$html" | grep -q 'text-black-500'; then star=black else star=no-star fi line=$(printf '%02d %-10s %-8s %s' "$i" "$target" "$star" "$pod") echo "$line" results+=("$target $star") done echo echo "Summary:" printf '%s\n' "${results[@]}" | sort | uniq -cExample output:
... Summary: 3 cluster-1 no-star 17 cluster-2 black
Implement fault injection
Set up virtual services
Important
In this section, we reuse the reviews-local and reviews-remote services created in the traffic shifting section.
Set up virtual services for
productpageanddetailsservices in AKS cluster 1 using the followingkubectl applycommand:kubectl --context $CLUSTER_NAME_1 apply -f - <<EOF apiVersion: networking.istio.io/v1 kind: VirtualService metadata: name: productpage spec: hosts: - productpage http: - route: - destination: host: productpage --- apiVersion: networking.istio.io/v1 kind: VirtualService metadata: name: details spec: hosts: - details http: - route: - destination: host: details EOFSet up a virtual service for the
ratingsservice in AKS cluster 2 using the followingkubectl applycommand:kubectl --context $CLUSTER_NAME_2 apply -f - <<EOF apiVersion: networking.istio.io/v1 kind: VirtualService metadata: name: ratings spec: hosts: - ratings http: - route: - destination: host: ratings EOFUpdate
reviews-remotein AKS cluster 2 to selectreviews-v2only using the followingkubectl applycommand:kubectl --context $CLUSTER_NAME_2 apply -f - <<EOF apiVersion: v1 kind: Service metadata: name: reviews-remote namespace: default labels: app: reviews service: reviews-remote istio.io/global: "true" istio.io/use-waypoint: waypoint spec: ports: - name: http port: 9080 targetPort: 9080 selector: app: reviews version: v2 EOFModify the virtual service for AKS cluster 1 to route user
jason's requests toreviews-remoteusing the followingkubectl applycommand:kubectl --context $CLUSTER_NAME_1 apply -f - <<EOF apiVersion: networking.istio.io/v1 kind: VirtualService metadata: name: reviews namespace: default spec: hosts: - reviews http: - match: - headers: end-user: exact: jason route: - destination: host: reviews-remote.default.svc.cluster.local port: number: 9080 - route: - destination: host: reviews-local.default.svc.cluster.local port: number: 9080 EOF
Inject HTTP delay
Inject a seven second HTTP delay for user
jasonto theratingsservice in AKS cluster 2 using the followingkubectl applycommand:kubectl --context $CLUSTER_NAME2 apply -f - <<EOF apiVersion: networking.istio.io/v1 kind: VirtualService metadata: name: ratings spec: hosts: - ratings http: - match: - headers: end-user: exact: jason fault: delay: percentage: value: 100.0 fixedDelay: 7s route: - destination: host: ratings - route: - destination: host: ratings EOFTo see the impact of this delay, open a browser and navigate to
http://$BOOK_INFO_GTW_IP/productpage. Initially, you shouldn't see any rating stars under the "Book Reviews" section since all the traffic goes toreviews-v1.Sign in using username
jasonand any password you want.
You should notice the webpage becomes very slow, with each refresh taking around six seconds. The "Book Reviews" section should show an error.
The traffic pattern once logged into user
jasonis:[(AKS cluster 1) productpage-v1] --(1)--> [(AKS cluster 2) reviews-v2] --(2)--> [(AKS cluster 2) ratings-v1].When logged in as
jason, we add a seven second delay in the connection (2):reviews-v2has a 10 second timeout when fetching ratings, which exceeds the delay fromratings-v1, so it can wait for ratings to load.productpage-v1has a three second timeout with two retries, which equals a six second timeout.reviews-v2is still waiting for the response from ratings, andproductpage-v1hits the timeout and returns an error on the webpage.
Reduce HTTP delay and timeout
In this section, we bring back the "Book Reviews" section. To reduce the webpage load time, we can change the traffic pattern for user jason to route to reviews-v3, which has a two and a half second timeout.
Update
reviews-remotein AKS cluster 2 to selectreviews-v3only using the followingkubectl applycommand:kubectl --context $CLUSTER_NAME_2 apply -f - <<EOF apiVersion: v1 kind: Service metadata: name: reviews-remote namespace: default labels: app: reviews service: reviews-remote istio.io/global: "true" istio.io/use-waypoint: waypoint spec: ports: - name: http port: 9080 targetPort: 9080 selector: app: reviews version: v3 EOFUpdated traffic pattern:
[(AKS cluster 1) productpage-v1] --(1)--> [(AKS cluster 2) reviews-v3] --(2)--> [(AKS cluster 2) ratings-v1]Now the "Book Reviews" section shows up, but ratings are still missing since the
reviews-v3waits only two and a half seconds before giving up on fetching them.
Next, let's bring back the "Ratings" section. Reduce the delay for
ratings-v1to one second using the followingkubectl applycommand:kubectl --context $CLUSTER_NAME_2 apply -f - <<EOF apiVersion: networking.istio.io/v1 kind: VirtualService metadata: name: ratings spec: hosts: - ratings http: - match: - headers: end-user: exact: jason fault: delay: percentage: value: 100.0 fixedDelay: 1s route: - destination: host: ratings subset: v1 - route: - destination: host: ratings subset: v1 EOFThe rating stars now show up since the ratings take one second to send back the response, which is within the
reviews-v3's two and a half seconds timeout which, in turn, is within the product page's six seconds timeout.
Inject HTTP abort
Ensure
reviews-remotein AKS cluster 2 selectsreviews-v2again using the followingkubectl applycommand:kubectl --context $CLUSTER_NAME_2 apply -f - <<EOF apiVersion: v1 kind: Service metadata: name: reviews-remote namespace: default labels: app: reviews service: reviews-remote istio.io/global: "true" istio.io/use-waypoint: waypoint spec: ports: - name: http port: 9080 targetPort: 9080 selector: app: reviews version: v2 EOFLet all requests from AKS cluster 1 hit
reviews-remoteusing the followingkubectl applycommand:kubectl --context $CLUSTER_NAME_1 apply -f - <<EOF apiVersion: networking.istio.io/v1 kind: VirtualService metadata: name: reviews spec: hosts: - reviews http: - route: - destination: host: reviews-remote.default.svc.cluster.local port: number: 9080 EOFInject a 503 HTTP abort for user
jasonto theratingsservice in AKS cluster 2 using the followingkubectl applycommand:kubectl --context $CLUSTER_NAME_2 apply -f - <<EOF apiVersion: networking.istio.io/v1 kind: VirtualService metadata: name: ratings spec: hosts: - ratings http: - fault: abort: httpStatus: 503 percentage: value: 100 route: - destination: host: ratings EOFWhether you log in or not, rating stars aren't able to display since all requests to
ratingsservice return 503:
Related content
To learn more about Azure Kubernetes Application Network, see the following articles: