> ## 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.

# Ambient Mode Traffic Management Demo

> Demo of Istio Ambient Mode showing differences from sidecar mode, using waypoint proxies and HTTPRoute for L7 split traffic and fault injection

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](https://learn.kodekloud.com/user/courses/istio-certified-associate) exam objectives — this is a practical demo to illustrate Ambient Mode behavior.

<Callout icon="lightbulb" color="#1CB2FE">
  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.
</Callout>

***

## Verify Ambient Mode installation

Assuming Istio Ambient Mode is installed, first confirm the control-plane and data-plane components:

```bash theme={null}
kubectl get pods -n istio-system
```

Example (trimmed):

```text theme={null}
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:

```bash theme={null}
kubectl get ns --show-labels
```

Example output (shows `test` labeled for Ambient Mode):

```text theme={null}
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:

```bash theme={null}
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):

```yaml theme={null}
helloworld.yaml
httpbin.yaml
```

Deploy the HelloWorld service and two deployments (v1 and v2):

```bash theme={null}
kubectl apply -f helloworld.yaml -n hello
```

Expected responses:

```text theme={null}
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:

```yaml theme={null}
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):

```yaml theme={null}
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:

```bash theme={null}
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:

```bash theme={null}
kubectl label namespace hello istio.io/dataplane-mode=ambient istio.io/use-waypoint=waypoint
```

Create the waypoint proxy for the namespace:

```bash theme={null}
istioctl waypoint apply -n hello
```

Verify pods in `hello` now include a waypoint proxy:

```bash theme={null}
kubectl get pods -n hello
```

Example output:

```text theme={null}
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:

```bash theme={null}
kubectl delete -f hello-vs.yaml
kubectl delete -f hello-dr.yaml
```

Confirm no VirtualService/DestinationRule resources remain:

```bash theme={null}
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):

```yaml theme={null}
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:

```bash theme={null}
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):

```yaml theme={null}
# 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:

```bash theme={null}
kubectl apply -f helloworld.yaml -n hello
```

Expected output:

```text theme={null}
service/helloworld unchanged
service/helloworld-v1 created
service/helloworld-v2 created
deployment.apps/helloworld-v1 unchanged
deployment.apps/helloworld-v2 unchanged
```

Confirm services:

```bash theme={null}
kubectl get svc -n hello
```

Example:

```text theme={null}
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:

```bash theme={null}
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):

```text theme={null}
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:

```bash theme={null}
kubectl label namespace httpbin istio.io/dataplane-mode=ambient istio.io/use-waypoint=waypoint
istioctl waypoint apply -n httpbin
```

Deploy httpbin:

```bash theme={null}
kubectl apply -f httpbin.yaml -n httpbin
```

Verify pods and services:

```bash theme={null}
kubectl get pods -n httpbin
kubectl get svc -n httpbin
```

Example service output:

```text theme={null}
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:

```bash theme={null}
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:

```yaml theme={null}
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:

```bash theme={null}
kubectl apply -f httpbin-vs-delay.yaml
```

Test the GET; responses should take \~3 seconds:

```bash theme={null}
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):

```yaml theme={null}
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:

```bash theme={null}
kubectl apply -f httpbin-vs-abort.yaml
```

Test with HEAD or GET to observe the configured status:

```bash theme={null}
kubectl exec curl -n test -- curl --head httpbin.httpbin.svc.cluster.local:8000/get
```

Example output for a 500 abort:

```text theme={null}
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)

|                                                    Feature | Sidecar mode                                      | Ambient Mode (with waypoint)                                                                      |
| ---------------------------------------------------------: | ------------------------------------------------- | ------------------------------------------------------------------------------------------------- |
| L7 subset routing via `VirtualService` + `DestinationRule` | Works (subset resolution enforced by sidecars)    | Not reliable — ztunnel handles L4; use HTTPRoute + waypoint                                       |
|                                    HTTPRoute (Gateway API) | Supported in cluster; typically used with ingress | Supported and recommended for split traffic in Ambient Mode (backendRefs -> per-version Services) |
|         Fault injection (delay/abort) via `VirtualService` | Works at L7 when sidecars present                 | Works when a waypoint is present (L7 path)                                                        |
|              Mirroring, retries, some advanced L7 features | Usually available                                 | May be limited—check Istio roadmap for details                                                    |

For more on the Gateway API: [Gateway API (sig-networking)](https://gateway-api.sigs.k8s.io/)

***

## 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](https://istio.io/latest/about/roadmap/) for updates.

<Callout icon="lightbulb" color="#1CB2FE">
  For the [Prep Course - Istio Certified Associate (ICA) Certification](https://learn.kodekloud.com/user/courses/istio-certified-associate), 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.
</Callout>

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:

* [Istio Documentation](https://istio.io/)
* [Gateway API](https://gateway-api.sigs.k8s.io/)
* [Kubernetes Networking Concepts](https://kubernetes.io/docs/concepts/services-networking/)

<CardGroup>
  <Card title="Watch Video" icon="video" cta="Learn more" href="https://learn.kodekloud.com/user/courses/istio-certified-associate/module/f3f5ca4b-b8d6-4788-9553-9ed765709933/lesson/d31069ab-dc8e-47ed-8e31-0fe4f66948da" />

  <Card title="Practice Lab" icon="flask-conical" cta="Learn more" href="https://learn.kodekloud.com/user/courses/istio-certified-associate/module/f3f5ca4b-b8d6-4788-9553-9ed765709933/lesson/bc7f3e4f-292c-45db-a5d3-fc8cc9b825e6" />
</CardGroup>
