TT Lab
Get started
Learn Learning paths Courses

Istio Deep Dive — Why It Flows That Way

What Is Responsible for What

Continue in TT Lab

In one line

A VirtualService decides "where to send", and a DestinationRule decides "how to treat it after sending". That is why a canary deployment needs both.

Why this was needed

When most people first use Istio, they make this mistake.

# VirtualService 만 쓴 카나리 — 동작하지 않는다
http:
  - route:
      - destination: { host: shop, subset: v1 }
        weight: 90
      - destination: { host: shop, subset: v2 }
        weight: 10

Nobody knows what subset: v1 points to. A subset is defined in a DestinationRule.

# DestinationRule — 서브셋의 정의
spec:
  host: shop
  subsets:
    - name: v1
      labels: { version: v1 }
    - name: v2
      labels: { version: v2 }

Only now does v1 mean the Pods carrying the label version: v1. Translated into the Envoy terms from the earlier course — the subsets of the DestinationRule create clusters (outbound|8080|v1|shop...), and the weights of the VirtualService become the weights of routes.

Four resources

Resource What it does In Envoy terms
Gateway The entrance for traffic coming from outside the mesh (ports, hosts, TLS) Listener
VirtualService Which requests go where (matching, weights, retries, timeouts) Route
DestinationRule How to treat the destination (subsets, LB, connection pool, outlier detection, TLS) Cluster
ServiceEntry Registers an external service the mesh does not know about in the list Cluster + Endpoint

A Gateway only opens the entrance. A Gateway alone does not make traffic flow — routing appears only when there is a VirtualService that references that Gateway in gateways:. This is another common trap.

When mTLS applies

PeerAuthentication sets the mode.

mode Meaning
PERMISSIVE Accepts both plain text and mTLS (the default, for migration)
STRICT Accepts only mTLS
DISABLE Does not use mTLS

The default is PERMISSIVE because Pods without a sidecar may remain. So the claim "we installed Istio, so it is encrypted" is wrong. Until you switch to STRICT, plain text gets through as well.

What commonly breaks when you switch to STRICT: Pods without a sidecar (for example Jobs), the kubelet sending health checks, and monitoring outside the mesh. The kubelet is handled because Istio reroutes the probe paths, but check scripts you wrote yourself break.

Confirming the configuration actually reached the proxy

The most common situation in Istio is the YAML is applied but the behavior does not change. What the control plane accepted and what the sidecar holds can differ, so you check on the proxy side.

istioctl proxy-status                 # 각 프록시가 최신 설정과 동기인지
istioctl analyze -n prod              # 설정끼리의 모순을 정적으로 찾는다
istioctl proxy-config routes deploy/frontend -n prod
istioctl proxy-config cluster deploy/frontend -n prod --fqdn api.prod.svc.cluster.local

If proxy-status is STALE, that proxy is running on old configuration. If it is SYNCED but the behavior is still different, the configuration itself differs from what was intended, so read routes and cluster directly.

A VirtualService needs a place to attach. It is common that the name written in hosts differs from the actual service, or that gateways was not written so it applies only inside the mesh. To apply it to traffic coming in through a gateway, you must state that gateway's name, and if it is a gateway in another namespace, write 네임스페이스/이름 (the placeholders are the namespace and the name).

A DestinationRule subset is a label, not a deployment. subset: v2 only points to the Pods that carry the label of that name. If you create a new deployment and do not attach the label, the subset becomes an empty destination and requests fall to 503. Checking with proxy-config endpoint how many actual addresses there are is the fastest way.

istioctl proxy-config endpoint deploy/frontend -n prod | grep api

There is an order of application. If there are several VirtualServices for the same host, the rules are not merged and the one created first wins. If each team makes one, another team's rules get quietly ignored. Make it a principle to have one resource per host, and if you have to split by path, split inside one resource.

Before you tighten mTLS, look at what comes in as plain text. The moment you switch PeerAuthentication to STRICT, workloads without a sidecar and some of Kubernetes' status checks get cut. Leave it at PERMISSIVE first, confirm in telemetry that the plain-text ratio reaches 0, and only then tighten.

Common misconceptions

"Putting in Istio gives you observability" — the metrics a sidecar emits are L7 request metrics (request count, latency, status code). You still do not know what happened inside the application. The same goes for traces: the sidecar creates spans, but if the application does not carry the headers along, the chain breaks. It is not true that you do not need to change any code.

What really matters in practice

You ask the proxy whether the configuration went as intended.

istioctl analyze                     # 정적 검사 — 서브셋 미정의 같은 것을 잡는다
istioctl proxy-status                # 각 사이드카가 최신 설정을 받았나(SYNCED)
istioctl proxy-config route <pod>    # 실제로 받은 라우트
istioctl proxy-config cluster <pod> --fqdn shop.default.svc.cluster.local

If proxy-status is STALE, the configuration has not gone yet, and looking at the YAML as much as you like is useless then.