How do VirtualService and DestinationRule control Istio traffic?
Quick Answer
VirtualService decides how requests get routed to a service -- URL matching, traffic splitting, retries. DestinationRule decides what happens after routing -- load balancing, connection pools, version subsets. Together they form Istio's declarative traffic management layer on top of Envoy.
Detailed Answer
Think of a large airport. A VirtualService is like the flight control tower that decides which runway each incoming plane should use based on the airline, destination, or time of day. A DestinationRule is like the ground crew instructions for each runway -- how planes are parked, how many gates are available, and what maintenance checks happen on arrival. The tower routes the traffic, the ground crew handles the details at the destination.
VirtualService and DestinationRule are Istio Custom Resource Definitions (CRDs) that together form the heart of Istio's traffic management. A VirtualService intercepts traffic heading for a particular hostname and applies routing rules before the request reaches any backend pod. You can match on HTTP headers, URI paths, query parameters, ports, and methods. Once a match is found, the VirtualService can send traffic to different destinations, apply retries, inject faults, add timeouts, or mirror traffic. DestinationRule picks up where VirtualService leaves off by defining policies that apply after routing is done. It sets up subsets (named groups of pods identified by labels), load balancing algorithms, connection pool settings, and outlier detection rules. Without a DestinationRule defining subsets, a VirtualService has no way to reference version-based routing targets.
Under the hood, when you apply a VirtualService or DestinationRule, istiod translates these high-level configs into Envoy-native xDS resources. A VirtualService becomes a set of Envoy Route configurations, while a DestinationRule turns into Cluster and Endpoint configurations. Istiod pushes these through the Aggregated Discovery Service (ADS) protocol to every Envoy sidecar in the mesh. The sidecars then enforce these rules at the proxy level, meaning no application code changes are needed. The translation pipeline runs through several validation steps in Galley before distribution, keeping configuration consistent across potentially thousands of sidecars. When multiple VirtualServices target the same host, Istio merges them, but ordering and conflict resolution can produce surprising results.
In production, teams use VirtualService and DestinationRule together for canary deployments, A/B testing, blue-green deployments, and gradual rollouts. A typical pattern is creating a DestinationRule with subsets for v1 and v2 of a service, then using a VirtualService to split traffic between them. You can also build resilience patterns: retries with specific conditions (only on 5xx errors), per-route timeouts, circuit breaking through outlier detection, and rate limiting with connection pool settings. Header-based routing is commonly used to send internal test traffic to a canary version while production users keep hitting the stable version. Fault injection through VirtualService is great for chaos engineering -- you can test how services handle delays and errors without touching any code.
A critical gotcha is that DestinationRule subsets must exactly match pod labels, and a mismatch silently drops traffic instead of throwing an error. Another common mistake is applying a VirtualService without the matching DestinationRule, which makes Istio fall back to round-robin across all pods regardless of version labels. Always validate configs with istioctl analyze before applying them to production. Also keep in mind that VirtualService routing rules are evaluated in order -- first match wins -- so putting a catch-all route before specific routes effectively hides all the rules that follow it.