Istio on AKS doesn’t just add observability; it fundamentally shifts how your services communicate from network-level TCP to application-level, intent-based routing.

Let’s get Istio running on an Azure Kubernetes Service (AKS) cluster. We’ll assume you have an AKS cluster already provisioned and kubectl configured to point to it.

First, we need to install the Istio CLI (istioctl) and then use it to install Istio onto your cluster.

# Download the latest Istio release
curl -L https://istio.io/downloadIstio | sh -

# Navigate to the downloaded directory
cd istio-<version> # Replace <version> with the actual downloaded version

# Install Istio with the default profile
./bin/istioctl install --set profile=default

This command applies a set of Kubernetes manifests that configure the Istio control plane components (like istiod) and inject necessary sidecar proxies into your pods.

Now, let’s deploy a sample application to see Istio in action. We’ll use the bookinfo sample application that comes with Istio.

# Apply the bookinfo sample application
kubectl apply -f samples/bookinfo/platform/kube/bookinfo.yaml

This deploys several microservices: productpage, details, reviews, and ratings. The productpage service calls details and reviews. The reviews service can return different versions of reviews (v1, v2, v3), each potentially calling the ratings service differently.

To enable Istio’s features for this application, we need to "mesh" it. This is done by labeling the namespace where your application resides.

# Label the default namespace to enable Istio injection
kubectl label namespace default istio-injection=enabled

After labeling the namespace, any new pods created in it will automatically have an Istio sidecar proxy (Envoy) injected. For existing pods, you’ll need to delete and recreate them.

# Delete and recreate the bookinfo pods to pick up the sidecar
kubectl delete pod -l app=productpage,version=v1
kubectl delete pod -l app=details-v1
kubectl delete pod -l app=reviews-v1
kubectl delete pod -l app=reviews-v2
kubectl delete pod -l app=reviews-v3
kubectl delete pod -l app=ratings-v1

You can verify the sidecar injection by checking the istio-proxy container in your pods:

kubectl get pods -o jsonpath='{.items[*].spec.containers[*].name}'

You should see istio-proxy listed alongside your application containers.

Now, let’s expose the bookinfo application. We create an Istio Gateway resource to manage ingress traffic and a VirtualService to define routing rules.

# gateway.yaml
apiVersion: networking.istio.io/v1beta1
kind: Gateway
metadata:
  name: bookinfo-gateway
spec:
  selector:
    istio: ingressgateway # use Istio's default ingress gateway
  servers:
  - port:
      number: 80
      name: http
      protocol: HTTP
    hosts:
    - "*" # Listen on all hosts
---
# virtualservice.yaml
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
  name: bookinfo
spec:
  hosts:
  - "*" # Matches the host in the Gateway
  gateways:
  - bookinfo-gateway
  http:
  - route:
    - destination:
        host: productpage
        port:
          number: 9080

Apply these resources:

kubectl apply -f gateway.yaml
kubectl apply -f virtualservice.yaml

To get the external IP address of your Istio ingress gateway:

kubectl get service istio-ingressgateway -n istio-system

Look for the EXTERNAL-IP. Once you have it, you can access the bookinfo application in your browser by navigating to http://<EXTERNAL-IP>/productpage.

The most surprising thing about Istio’s traffic management is that it doesn’t actually modify your application code. The Envoy sidecar intercepts all incoming and outgoing network traffic for your pod, allowing Istio to enforce routing, security, and observability policies without touching your application’s dependencies or business logic.

You can experiment with routing rules. For example, to send 50% of traffic to reviews version 2 and 50% to version 3:

# virtualservice-reviews-split.yaml
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
  name: reviews
spec:
  hosts:
    - reviews
  http:
  - route:
    - destination:
        host: reviews
        subset: v2
      weight: 50
    - destination:
        host: reviews
        subset: v3
      weight: 50

To make this work, you’ll also need to define DestinationRules for the subsets:

# destinationrule-reviews.yaml
apiVersion: networking.istio.io/v1beta1
kind: DestinationRule
metadata:
  name: reviews
spec:
  host: reviews
  subsets:
  - name: v1
    labels:
      version: v1
  - name: v2
    labels:
      version: v2
  - name: v3
    labels:
      version: v3

Apply these:

kubectl apply -f destinationrule-reviews.yaml
kubectl apply -f virtualservice-reviews-split.yaml

Refresh your productpage URL multiple times, and you’ll see the "The Book Reviews" section change between "Good," "Great," and "Cool" as traffic is split.

Istio’s power lies in its declarative configuration. You define the desired state of your network traffic, and Istio, through its control plane and sidecar proxies, works to achieve and maintain that state. This includes sophisticated fault injection, request retries, circuit breaking, and fine-grained traffic shifting for canary deployments, all without application code changes.

A key detail often overlooked is how Istio’s ServiceEntry resource allows you to manage external services. If your application needs to call an external API (e.g., a third-party payment gateway), you can define a ServiceEntry to bring that external service into Istio’s mesh. This allows you to apply Istio’s policies (like mTLS, retries, or routing) to traffic destined for services outside your Kubernetes cluster, treating them as if they were internal services.

Once you have traffic management working, the next logical step is to secure it with mutual TLS.

Want structured learning?

Take the full Aks course →