VirtualService Deep Dive
A VirtualService defines how requests are routed to a service within the Istio mesh. It operates at L7 and provides match conditions based on URI, headers, query parameters, and HTTP method.
Match Conditions
VirtualService match blocks support exact, prefix, and regex matching on multiple fields:
# virtualservice-match.yaml — route by URI and header
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
name: reviews-route
spec:
hosts:
- reviews
http:
- match:
- headers:
end-user:
exact: jason
route:
- destination:
host: reviews
subset: v2
- match:
- uri:
prefix: /api/v2
method:
exact: GET
route:
- destination:
host: reviews
subset: v3
- route:
- destination:
host: reviews
subset: v1
Weighted Routing for Traffic Splitting
The weight field distributes traffic across multiple destinations. Weights must sum to 100:
# virtualservice-split.yaml — 90/10 traffic split
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
name: reviews-canary
spec:
hosts:
- reviews
http:
- route:
- destination:
host: reviews
subset: v1
weight: 90
- destination:
host: reviews
subset: v2
weight: 10
Canary Deployments
Istio enables canary deployments by gradually shifting traffic from the stable version to the canary. Unlike Kubernetes-native canary (which requires scaling replicas), Istio decouples traffic routing from deployment scaling.
Header-Based Routing for Internal Testing
Route internal team traffic to the canary using a custom header, while production users still hit the stable version:
# virtualservice-header-canary.yaml
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
name: reviews-internal-canary
spec:
hosts:
- reviews
http:
# Internal testers get v2
- match:
- headers:
x-canary-test:
exact: "true"
route:
- destination:
host: reviews
subset: v2
# Everyone else gets v1
- route:
- destination:
host: reviews
subset: v1
# Test the canary with the custom header
kubectl exec deploy/sleep -c sleep -- \
curl -s -H "x-canary-test: true" http://reviews:9080/reviews/1
# Normal traffic hits v1
kubectl exec deploy/sleep -c sleep -- \
curl -s http://reviews:9080/reviews/1
DestinationRule
A DestinationRule defines policies applied after routing — it configures subsets (version labels), connection pools, and outlier detection (circuit breaking).
# destinationrule.yaml — define subsets + circuit breaking
apiVersion: networking.istio.io/v1beta1
kind: DestinationRule
metadata:
name: reviews-dr
spec:
host: reviews
trafficPolicy:
connectionPool:
tcp:
maxConnections: 100
http:
h2UpgradePolicy: DEFAULT
http1MaxPendingRequests: 100
http2MaxRequests: 1000
outlierDetection:
consecutive5xxErrors: 3
interval: 30s
baseEjectionTime: 60s
maxEjectionPercent: 50
subsets:
- name: v1
labels:
version: v1
- name: v2
labels:
version: v2
- name: v3
labels:
version: v3
outlierDetection config ejects pods returning 3 consecutive 5xx errors for 60 seconds. This prevents cascading failures by removing unhealthy upstream instances from the load balancer pool.
Fault Injection
Istio supports two types of fault injection for chaos testing — delays (simulating network latency) and aborts (simulating upstream failures):
# fault-injection.yaml — inject 5s delay for 50% of traffic
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
name: ratings-fault
spec:
hosts:
- ratings
http:
- fault:
delay:
percentage:
value: 50.0
fixedDelay: 5s
abort:
percentage:
value: 10.0
httpStatus: 503
route:
- destination:
host: ratings
subset: v1
# Apply fault injection and observe behavior
kubectl apply -f fault-injection.yaml
# Test — some requests will be delayed, some will return 503
for i in $(seq 1 20); do
kubectl exec deploy/sleep -c sleep -- \
curl -s -o /dev/null -w "status=%{http_code} time=%{time_total}s\n" \
http://ratings:9080/ratings/1
done
Retries & Timeouts
Configure automatic retries and request timeouts to improve resilience:
# retries-timeouts.yaml
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
name: reviews-resilience
spec:
hosts:
- reviews
http:
- timeout: 10s
retries:
attempts: 3
perTryTimeout: 3s
retryOn: gateway-error,connect-failure,refused-stream,5xx
route:
- destination:
host: reviews
subset: v1
timeout must be greater than attempts × perTryTimeout. Here, 3 retries × 3s = 9s fits within the 10s timeout. If the timeout is too short, retries will be cancelled.
Exercises
outlierDetection that ejects a pod after 2 consecutive 5xx errors. Deploy a pod that always returns 500. Verify it gets ejected by checking istioctl proxy-config cluster output.
Key Takeaways & Next Steps
- VirtualService provides L7 routing with match conditions on headers, URI, and method
- Traffic splitting via weights enables canary deployments decoupled from pod scaling
- DestinationRule defines subsets (versions) and connection-level policies like circuit breaking
- Fault injection (delay + abort) enables chaos testing within the mesh
- Retries and timeouts improve resilience but must be configured consistently
Next in the Series
In Part 3: mTLS & AuthorizationPolicy, we'll secure the mesh with mutual TLS for zero-trust communication and fine-grained access control using AuthorizationPolicy resources.