skip to content

After Istio injection, a Kubernetes pod that declares a single application container shows READY 2/2. What is the second container, and how does the application's traffic end up flowing through it without any change to the application code?

level: juniorimportance: must knowfreq 70%

answer

  1. two containers, only one is yours
  2. added when the pod object is created
  3. rules live in the pod's network namespace
  4. app code and app config unchanged
  5. outbound 15001, inbound 15006

basics

~20 s

The second container is istio-proxy, an Envoy-based sidecar added by Istio's injection webhook when the pod is created. Injected rules in the pod's own network namespace redirect the pod's inbound and outbound TCP traffic through that proxy, so application code never changes.

solid answer

~50 s

The extra container is `istio-proxy` — an Envoy process plus Istio's `pilot-agent`, added to the pod spec by Istio's mutating admission webhook at pod-creation time, not by anything the application does. Alongside it Istio injects an `istio-init` container (or, with the Istio CNI plugin, does the equivalent from the node) that writes traffic-capture rules into the pod's network namespace. Those rules redirect outbound TCP connections from the app to the proxy's outbound port and inbound connections to its inbound port, so the app keeps dialling `http://orders:8080` and keeps listening on its normal port while every byte actually crosses Envoy. That is what makes routing, retries, telemetry and mTLS available without recompiling anything. The cost is one more container's memory and CPU in every pod, and a restart of every pod whenever the proxy is upgraded.

code

bash · 6 lines
bash
# List the containers Kubernetes actually created for a meshed pod
kubectl get pod my-app-5f7c9d8b6-2xk4t \
  -o jsonpath='{range .spec.initContainers[*]}init:{.name}{"\n"}{end}{range .spec.containers[*]}{.name}{"\n"}{end}'

# Read the sidecar's own logs when a meshed request misbehaves
kubectl logs my-app-5f7c9d8b6-2xk4t -c istio-proxy --tail=50

go deeper

for a junior

Be able to name the container as istio-proxy, say it was added automatically when the pod was created, and state plainly that the app's traffic is redirected into it without any code change.

for a middle

Explain the mechanism: a mutating admission webhook rewrites the pod spec, and capture rules in the pod's shared network namespace redirect inbound and outbound TCP to the proxy's ports. Mention that only TCP is captured.

for a senior

Show that you treat the proxy as a real hop in production — its logs and metrics are the first place you look, the peer address the app sees is local, and excluded ports exist for dependencies that break behind it.

for a principal

Frame the per-pod proxy as a cost model: memory multiplied by pod count, a mesh-wide restart per upgrade, and an extra failure domain in every request path. Be ready to say when that price is worth paying and when a shared gateway is enough.

## What the two containers are A meshed pod contains the container you wrote plus `istio-proxy`. `istio-proxy` runs two processes: **Envoy**, the actual proxy that moves bytes, and **pilot-agent**, a small supervisor that bootstraps Envoy, fetches its configuration and workload certificate from the `istiod` control plane, and exposes health and metrics endpoints. `kubectl get pod` counts both containers, so a one-container Deployment reports `2/2` once it is meshed. Most pods also briefly run a third container that is already finished by the time you look: an init container named `istio-init`, whose only job is to program the pod's traffic-capture rules. If the cluster runs the Istio CNI plugin instead, that init container is absent and the same rules are written from the node during pod network setup. ## How the container got there Nothing at runtime adds a container to a running pod. Istio registers a **mutating admission webhook** with the Kubernetes API server; when a pod object is created in a namespace that is in scope for the mesh, the API server calls the webhook, and the webhook returns a modified pod spec with `istio-proxy` (and `istio-init`) appended. That is why: - pods created *before* the namespace was labelled are not meshed until they are recreated; - you can see the sidecar in `kubectl get pod -o yaml` even though it appears in no manifest in your repository; - `istioctl kube-inject` can produce the same result offline, by rendering the injected spec for a manifest you then apply yourself. ## How traffic reaches the proxy The application is not configured with a proxy address, and it does not link an Istio library. Instead, capture rules are installed **in the pod's network namespace**, which every container in the pod shares: ``` outbound: app dials orders:8080 -> redirected to 127.0.0.1:15001 (Envoy) -> Envoy dials the real endpoint inbound: remote peer connects -> redirected to 127.0.0.1:15006 (Envoy) -> Envoy connects to the app's port ``` Because the redirection lives in the pod's network namespace, it affects only this pod — never other pods on the node, and never the node's own traffic. The proxy runs under its own UID, and traffic originating from that UID is exempted so Envoy's own outbound connections are not redirected back into itself in an infinite loop. Some traffic is deliberately left alone. Istio only captures TCP; UDP (including DNS, unless DNS capture is switched on) passes straight through. Ports and CIDR ranges can be excluded with pod annotations such as `traffic.sidecar.istio.io/excludeOutboundPorts` and `traffic.sidecar.istio.io/excludeInboundPorts`, which is the usual escape hatch for a database driver or an agent that misbehaves behind a proxy. ## What the application sees From the application's point of view, nothing changed: it resolves the same service name, dials the same port, and listens where it always listened. What changed is who is on the other end of the socket. Because Envoy is now a real hop, several things become visible: - Every request produces proxy access logs and metrics without the app emitting them. - The peer address the app sees for inbound requests is the local proxy, not the remote client — original client identity arrives as forwarded headers or as the mesh identity of the peer. - Timeouts, retries and failures can now originate in the proxy rather than the app, which is why reading proxy logs is the first step when a meshed service starts behaving oddly. ## Why interviewers ask this The answer separates people who have run Istio from people who have read about service meshes. It exposes the mesh's real, unglamorous cost: an extra container's memory and CPU multiplied by every pod, a mesh-wide pod restart on every proxy upgrade, an extra process that must be alive before your app's first request can succeed, and a second place to look during every incident. Those trade-offs — not the feature list — are what the follow-up questions are usually about.

  • If nothing in our manifest mentions istio-proxy, why does it appear in the running pod?
    Istio registers a mutating admission webhook with the API server. When the pod object is created, the API server calls that webhook, which returns a pod spec with the proxy container added. The stored object therefore differs from the manifest you applied — which is also why controllers such as Deployments must create new pods for injection to take effect.
  • Does the sidecar capture UDP traffic as well as TCP?
    No. Istio's capture applies to TCP. UDP — including ordinary DNS lookups — bypasses the proxy unless you explicitly enable Istio's DNS capture. That is why a UDP-based dependency gets no mesh telemetry, no mesh mTLS and no mesh routing, and why people are sometimes surprised that only part of a pod's traffic shows up in mesh dashboards.
  • What is the operational cost of one extra container per pod?
    Memory and CPU per pod rather than per node, so cost scales with pod count; an extra hop of latency on every call; a proxy upgrade that requires restarting every meshed pod; and a second component to inspect in every incident. At a few hundred pods it is noise; at tens of thousands it becomes a capacity line item.

It is like a mail room installed inside your office rather than a new address: you keep posting letters exactly as before, but everything now passes a desk that can log, stamp, reroute or refuse it.

saying these in an interview costs you the question

  • Says the application must be rebuilt against an Istio client library
  • Describes the sidecar as a separate pod beside the app pod
  • Claims the app is configured to dial the proxy's address
  • Thinks the sidecar is added when the container starts, not at admission
  • Believes the pod's capture rules affect other pods on the node

context