Skip to main content

Documentation Index

Fetch the complete documentation index at: https://notes.kodekloud.com/llms.txt

Use this file to discover all available pages before exploring further.

This lesson demonstrates Istio Ambient Mode and how common traffic-management primitives behave differently from sidecar mode. You’ll learn:
  • What works and what doesn’t in Istio Ambient Mode
  • When to use Istio VirtualService/DestinationRule vs. Kubernetes HTTPRoute (Gateway API)
  • A 95/5 split-traffic example using HTTPRoute + waypoint proxies
  • L7 fault injection (delay/abort) with VirtualService + waypoint
Note: Ambient Mode is not part of the Prep Course - Istio Certified Associate (ICA) Certification exam objectives — this is a practical demo to illustrate Ambient Mode behavior.
Ambient Mode uses a different data plane (ztunnel + namespace waypoint proxies). Some features available in sidecar mode require a waypoint or different APIs, and a few are not yet supported in Ambient Mode.

Verify Ambient Mode installation

Assuming Istio Ambient Mode is installed, first confirm the control-plane and data-plane components:
kubectl get pods -n istio-system
Example (trimmed):
NAME                       READY   STATUS    RESTARTS   AGE
istio-cni-node-vdt82       1/1     Running   0          2m49s
istiod-6b854648cc-nnfk4    1/1     Running   0          2m57s
ztunnel-qgtj5              1/1     Running   0          2m45s
Check namespaces and labels to see which namespaces are configured for Ambient Mode:
kubectl get ns --show-labels
Example output (shows test labeled for Ambient Mode):
NAME              STATUS    AGE     LABELS
default           Active    8m31s  kubernetes.io/metadata.name=default
hello             Active    4m4s   kubernetes.io/metadata.name=hello
httpbin           Active    4m1s   kubernetes.io/metadata.name=httpbin
istio-system      Active    3m12s  kubernetes.io/metadata.name=istio-system
test              Active    4m7s   istio.io/dataplane-mode=ambient,istio.io/use-waypoint=waypoint,kubernetes.io/metadata.name=test
Use a simple curl pod in the test namespace to exercise services:
kubectl get pods -n test
# NAME   READY   STATUS    RESTARTS   AGE
# curl   1/1     Running   0          5m5s

Deploy the HelloWorld app (two versions)

Files used in this demo (present in the working directory):
helloworld.yaml
httpbin.yaml
Deploy the HelloWorld service and two deployments (v1 and v2):
kubectl apply -f helloworld.yaml -n hello
Expected responses:
service/helloworld created
deployment.apps/helloworld-v1 created
deployment.apps/helloworld-v2 created

Attempting VirtualService + DestinationRule split in Ambient Mode

In sidecar mode, you typically create a DestinationRule with subsets and a VirtualService to split L7 traffic. Example DestinationRule:
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
  name: hello-world-dr
  namespace: hello
spec:
  host: helloworld
  subsets:
  - name: v1
    labels:
      version: v1
  - name: v2
    labels:
      version: v2
Example VirtualService (95/5 split):
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: hello-world-vs
  namespace: hello
spec:
  hosts:
  - helloworld
  http:
  - match:
    - uri:
        prefix: /
    route:
    - destination:
        host: helloworld
        port:
          number: 5000
        subset: v1
      weight: 95
    - destination:
        host: helloworld
        port:
          number: 5000
        subset: v2
      weight: 5
Apply them in the hello namespace:
kubectl apply -f hello-dr.yaml
kubectl apply -f hello-vs.yaml
Important: In sidecar mode the VirtualService host must match the DestinationRule host (short name or FQDN) so subset resolution works. However, in Ambient Mode a plain VirtualService + DestinationRule subset split will not reliably perform L7 routing. ztunnel routes L4 traffic (cluster-wide) and cannot enforce VirtualService subset routing without a waypoint. You will likely observe incorrect or seemingly random distribution. Therefore, for split traffic in Ambient Mode we must use a waypoint proxy plus the Gateway API (HTTPRoute) instead of relying on VirtualService subsets.

Label the namespace and apply a waypoint proxy

Label the hello namespace to enable Ambient Mode waypoint behavior:
kubectl label namespace hello istio.io/dataplane-mode=ambient istio.io/use-waypoint=waypoint
Create the waypoint proxy for the namespace:
istioctl waypoint apply -n hello
Verify pods in hello now include a waypoint proxy:
kubectl get pods -n hello
Example output:
NAME                                 READY   STATUS    RESTARTS   AGE
helloworld-v1-7459d7b54b-pkhgf       1/1     Running   0          4m28s
helloworld-v2-654d97458-bj962        1/1     Running   0          4m28s
waypoint-795d979b85-tr7q9            1/1     Running   0          9s
Remember the roles:
  • ztunnel: routes L4 traffic cluster-wide (runs in istio-system)
  • waypoint proxy: performs L7 routing for workloads in the namespace and attaches to the backend Service

Remove the VirtualService/DestinationRule and switch to HTTPRoute

Delete the VirtualService and DestinationRule you created earlier; they don’t provide reliable L7 split routing in Ambient Mode:
kubectl delete -f hello-vs.yaml
kubectl delete -f hello-dr.yaml
Confirm no VirtualService/DestinationRule resources remain:
kubectl get vs -A
kubectl get destinationrules.networking.istio.io -A
Use the Gateway API HTTPRoute to perform split routing. HTTPRoute backendRefs must reference actual Service names (not DestinationRule subsets). The HTTPRoute attaches to the ClusterIP helloworld service and routes to backend services (one per version): HTTPRoute (gateway.networking.k8s.io/v1):
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: hello-http-split-traffic
  namespace: hello
spec:
  parentRefs:
  - group: ""
    kind: Service
    name: helloworld
    port: 5000
  rules:
  - backendRefs:
    - name: helloworld-v1
      port: 5000
      weight: 95
    - name: helloworld-v2
      port: 5000
      weight: 5
Apply the HTTPRoute:
kubectl apply -f hello-httproute-split-traffic.yaml
# httproute.gateway.networking.k8s.io/hello-http-split-traffic created
Important: HTTPRoute backendRefs reference the per-version Service names (for example, helloworld-v1), not VirtualService subsets. That means you must create individual Services for each deployment.

Create per-version Services for v1 and v2

Add two ClusterIP Services—one per deployment. Example Service manifests (add to helloworld.yaml or separate files):
# Service for v1
apiVersion: v1
kind: Service
metadata:
  name: helloworld-v1
  namespace: hello
  labels:
    app: helloworld
    version: v1
spec:
  ports:
  - port: 5000
    name: http
  selector:
    app: helloworld
    version: v1
---
# Service for v2
apiVersion: v1
kind: Service
metadata:
  name: helloworld-v2
  namespace: hello
  labels:
    app: helloworld
    version: v2
spec:
  ports:
  - port: 5000
    name: http
  selector:
    app: helloworld
    version: v2
Apply (or reapply) the helloworld manifest to create those services:
kubectl apply -f helloworld.yaml -n hello
Expected output:
service/helloworld unchanged
service/helloworld-v1 created
service/helloworld-v2 created
deployment.apps/helloworld-v1 unchanged
deployment.apps/helloworld-v2 unchanged
Confirm services:
kubectl get svc -n hello
Example:
NAME             TYPE       CLUSTER-IP       EXTERNAL-IP   PORT(S)                    AGE
helloworld       ClusterIP  10.109.201.209   <none>        5000/TCP                   9m26s
helloworld-v1    ClusterIP  10.106.112.114   <none>        5000/TCP                   2s
helloworld-v2    ClusterIP  10.106.42.104    <none>        5000/TCP                   2s
waypoint         ClusterIP  10.108.208.214   <none>        15021/TCP,15008/TCP        5m7s

Test split traffic via the ClusterIP + waypoint

From the curl pod in the test namespace, call the ClusterIP helloworld service:
kubectl exec curl -n test -- curl helloworld.hello.svc.cluster.local:5000/hello
You should see mostly v1 responses (approx. 95%) and occasional v2 responses (approx. 5%). Example output (truncated):
Hello version: v1, instance: helloworld-v1-7459d7b54b-pkhgf
# ... occasional:
Hello version: v2, instance: helloworld-v2-654d97458-bj962
This confirms the HTTPRoute + waypoint approach implements L7 split routing in Ambient Mode.

httpbin demo: fault injection (delay / abort)

Next we demonstrate L7 fault injection using the httpbin app. First label the namespace and apply a waypoint:
kubectl label namespace httpbin istio.io/dataplane-mode=ambient istio.io/use-waypoint=waypoint
istioctl waypoint apply -n httpbin
Deploy httpbin:
kubectl apply -f httpbin.yaml -n httpbin
Verify pods and services:
kubectl get pods -n httpbin
kubectl get svc -n httpbin
Example service output:
NAME       TYPE        CLUSTER-IP       PORT(S)
httpbin    ClusterIP   10.109.106.235   8000/TCP
waypoint   ClusterIP   10.111.106.152   15021/TCP,15008/TCP
Test a normal GET:
kubectl exec curl -n test -- curl httpbin.httpbin.svc.cluster.local:8000/get
You should receive a standard JSON response from httpbin.

Inject a fixed delay using VirtualService

Create a VirtualService to delay 100% of requests by 3 seconds:
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: httpbin-vs-delay
  namespace: httpbin
spec:
  hosts:
  - httpbin.httpbin.svc.cluster.local
  http:
  - fault:
      delay:
        percentage:
          value: 100.0
        fixedDelay: 3s
    route:
    - destination:
        host: httpbin.httpbin.svc.cluster.local
        port:
          number: 8000
Apply it:
kubectl apply -f httpbin-vs-delay.yaml
Test the GET; responses should take ~3 seconds:
kubectl exec curl -n test -- curl httpbin.httpbin.svc.cluster.local:8000/get
You will observe the injected delay.

Inject an abort (HTTP status)

Create a VirtualService that aborts all requests with a specific HTTP status (example: 500):
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: httpbin-vs-abort
  namespace: httpbin
spec:
  hosts:
  - httpbin.httpbin.svc.cluster.local
  http:
  - fault:
      abort:
        percentage:
          value: 100.0
        httpStatus: 500
    route:
    - destination:
        host: httpbin.httpbin.svc.cluster.local
        port:
          number: 8000
Apply it:
kubectl apply -f httpbin-vs-abort.yaml
Test with HEAD or GET to observe the configured status:
kubectl exec curl -n test -- curl --head httpbin.httpbin.svc.cluster.local:8000/get
Example output for a 500 abort:
HTTP/1.1 500 Internal Server Error
content-length: 18
content-type: text/plain
server: istio-envoy
x-envoy-decorator-operation: httpbin.httpbin.svc.cluster.local:8000/*
To change the abort code, update the httpStatus in the VirtualService and reapply.

Feature comparison: Sidecar vs Ambient Mode (with waypoint)

FeatureSidecar modeAmbient Mode (with waypoint)
L7 subset routing via VirtualService + DestinationRuleWorks (subset resolution enforced by sidecars)Not reliable — ztunnel handles L4; use HTTPRoute + waypoint
HTTPRoute (Gateway API)Supported in cluster; typically used with ingressSupported and recommended for split traffic in Ambient Mode (backendRefs -> per-version Services)
Fault injection (delay/abort) via VirtualServiceWorks at L7 when sidecars presentWorks when a waypoint is present (L7 path)
Mirroring, retries, some advanced L7 featuresUsually availableMay be limited—check Istio roadmap for details
For more on the Gateway API: Gateway API (sig-networking)

Summary and limitations

  • Ambient Mode uses ztunnel (L4 proxy) for cluster-wide routing and waypoint proxies (per-namespace) for L7 routing.
  • VirtualService + DestinationRule subset-based split routing does not reliably work in Ambient Mode because ztunnel is L4. Instead:
    • Add a waypoint proxy for the namespace and
    • Use HTTPRoute (Gateway API) with backendRefs that point to per-version Services.
  • Fault injection (delay/abort) via VirtualService works when a waypoint is present.
  • Some L7 features (mirroring, certain retries/timeouts, etc.) may not be fully supported yet in Ambient Mode — consult the Istio roadmap for updates.
For the Prep Course - Istio Certified Associate (ICA) Certification, you only need to know that Ambient Mode exists and that namespaces may require labeling; deep HTTPRoute or waypoint configuration is not required for exam objectives. Practice the steps above in a lab to gain hands-on experience.
Now you’re ready to try these steps in a live lab to experience Ambient Mode L7 traffic management and fault injection. For further reading:

Watch Video

Practice Lab