🕸️ What a Service Mesh Provides
A service mesh injects a proxy sidecar into every pod. All network traffic flows through the proxy — giving the mesh control over encryption (mTLS), load balancing, retries, timeouts, circuit breaking, and telemetry without any application code changes.
🔒 mTLS Zero-Trust
Every pod-to-pod call is mutually authenticated and encrypted. No app changes needed — the proxy intercepts all traffic.
🔀 Traffic Management
Canary releases, A/B testing, weighted routing, retries, timeouts, fault injection — all via CRDs.
📊 Golden Signals
Latency, error rate, and throughput metrics for every service pair — automatically, without instrumentation.
🔌 Circuit Breaking
Detect unhealthy upstreams and stop sending traffic before cascading failures propagate.
🔀 Traffic Management
Istio uses VirtualService and DestinationRule CRDs to define routing logic. Traffic decisions happen in the Envoy sidecars — not in kube-proxy or the application.
Canary Release — Weighted Routing
apiVersion: networking.istio.io/v1beta1
kind: DestinationRule
metadata:
name: my-app
namespace: my-app
spec:
host: my-app
subsets:
- name: v1
labels: { version: v1 }
- name: v2
labels: { version: v2 }
---
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
name: my-app
namespace: my-app
spec:
hosts: [my-app]
http:
- route:
- destination:
host: my-app
subset: v1
weight: 90 # 90% to stable
- destination:
host: my-app
subset: v2
weight: 10 # 10% canary
Header-Based Routing (A/B Testing)
# Route internal testers to v2 via header
spec:
http:
- match:
- headers:
x-canary:
exact: "true"
route:
- destination: { host: my-app, subset: v2 }
- route:
- destination: { host: my-app, subset: v1 }
Retries & Timeouts
spec:
http:
- timeout: 10s # global request timeout
retries:
attempts: 3
perTryTimeout: 3s
retryOn: "5xx,gateway-error,connect-failure,retriable-4xx"
route:
- destination: { host: my-app }
mTLS — Zero-Trust Network
# Enforce strict mTLS for all pods in a namespace
apiVersion: security.istio.io/v1beta1
kind: PeerAuthentication
metadata:
name: default
namespace: my-app
spec:
mtls:
mode: STRICT # reject all plaintext traffic
# Permissive mode — accept both mTLS and plaintext (migration phase)
# mode: PERMISSIVE
# Verify mTLS is working
istioctl x authz check <pod-name>.my-app
AuthorizationPolicy — L7 RBAC
# Only allow frontend to call backend on /api paths
apiVersion: security.istio.io/v1beta1
kind: AuthorizationPolicy
metadata:
name: backend-policy
namespace: my-app
spec:
selector:
matchLabels: { app: backend }
rules:
- from:
- source:
principals: ["cluster.local/ns/my-app/sa/frontend"]
to:
- operation:
methods: ["GET", "POST"]
paths: ["/api/*"]
PERMISSIVE mTLS. This lets non-meshed pods still communicate while you roll out the sidecar injection. Switch to STRICT once all pods have sidecars.
📊 Observability & Circuit Breaking
Automatic Golden Signals
Every Envoy sidecar emits Prometheus metrics for every service-to-service call — no instrumentation required. Key metrics:
| Metric | Meaning | Alert threshold |
|---|---|---|
istio_requests_total | Request count by source, destination, response code | Error rate > 1% |
istio_request_duration_milliseconds | Latency histogram per service pair | P99 > SLO |
istio_tcp_connections_opened_total | Active TCP connections | Sudden spike |
envoy_cluster_upstream_cx_active | Active upstream connections per cluster | Approaching circuit breaker limit |
Circuit Breaking (Outlier Detection)
apiVersion: networking.istio.io/v1beta1
kind: DestinationRule
metadata:
name: my-app-cb
namespace: my-app
spec:
host: my-app
trafficPolicy:
connectionPool:
tcp:
maxConnections: 100
http:
http1MaxPendingRequests: 50
maxRequestsPerConnection: 10
outlierDetection:
consecutive5xxErrors: 5 # eject after 5 consecutive errors
interval: 10s # check every 10s
baseEjectionTime: 30s # keep ejected for 30s minimum
maxEjectionPercent: 50 # never eject more than 50% of endpoints
Fault Injection for Chaos Testing
# Inject 5s delay for 10% of requests — test timeout handling
spec:
http:
- fault:
delay:
percentage: { value: 10 }
fixedDelay: 5s
- fault:
abort:
percentage: { value: 5 }
httpStatus: 503 # inject 503 errors for 5% of requests
route:
- destination: { host: my-app }
Production Operations
Sidecar Injection
Enable per-namespace: kubectl label ns my-app istio-injection=enabled. Existing pods need restarting to get sidecars injected.
Upgrade Safely
Use Istio's canary control-plane upgrade. Run two Istio versions simultaneously; migrate namespaces one at a time via the istio.io/rev label.
Exclude From Mesh
Add annotation sidecar.istio.io/inject: "false" to pods that must not have a sidecar (e.g. etcd, databases).
Debug with istioctl
istioctl proxy-status shows sync state. istioctl proxy-config routes <pod> dumps Envoy routing rules. istioctl analyze checks for config errors.
# Useful istioctl commands
# Check all proxies are in sync with control plane
istioctl proxy-status
# Analyse namespace for misconfigurations
istioctl analyze -n my-app
# Dump Envoy config for a pod
istioctl proxy-config all <pod>.<namespace>
# Check effective AuthorizationPolicies
istioctl x authz check <pod>.<namespace>
⚖️ Istio vs Linkerd
🔵 Istio
- CNCF Graduated — Google/IBM/Lyft origins
- Envoy proxy sidecar (~60 MB RAM)
- Rich traffic management (VirtualService, DestinationRule)
- L7 AuthorizationPolicy, JWT, OAuth2 support
- Multi-cluster support (east-west gateway)
- Ambient mode (no sidecar) — GA in 1.22+
- Higher complexity, more features
- Best for: enterprises needing full L7 control
🟢 Linkerd
- CNCF Graduated — Buoyant origins
- Rust-based micro-proxy (~10 MB RAM)
- Simpler config — less CRD surface area
- Automatic mTLS with zero config
- HTTPRoute-based traffic splitting (Gateway API)
- Lower latency overhead than Envoy
- No ambient mode (sidecar-only)
- Best for: teams wanting simplicity and low overhead
| Feature | Istio | Linkerd |
|---|---|---|
| Proxy | Envoy (C++) | linkerd2-proxy (Rust) |
| RAM per sidecar | ~50–100 MB | ~10–20 MB |
| Added latency | ~5–15ms (p99) | ~1–3ms (p99) |
| Automatic mTLS | Yes (PERMISSIVE default) | Yes (on by default) |
| Traffic shifting | VirtualService (powerful) | HTTPRoute (Gateway API) |
| JWT/OIDC auth | Yes (RequestAuthentication) | No |
| gRPC support | Yes | Yes |
| Sidecar-less mode | Ambient (GA 1.22) | No |
| Learning curve | Steep | Gentle |
When NOT to Use a Service Mesh
- Clusters with < 10 services — NetworkPolicy + TLS at the app layer is simpler
- Teams without dedicated platform engineers to operate the mesh
- Latency-critical paths where adding 5–15ms per hop is unacceptable
- Edge / resource-constrained environments (sidecar overhead is significant)
📝 Knowledge Check
PeerAuthentication with mode: STRICT in a namespace that still has some pods without Istio sidecars. What happens to traffic from those non-meshed pods?PERMISSIVE mode during migration so both meshed and non-meshed callers can coexist, then switch to STRICT once all pods have sidecars.outlierDetection in an Istio DestinationRule implement, and what triggers an endpoint ejection?