| name | service-mesh |
| description | Invoke when designing, configuring, or troubleshooting service-to-service communication with Istio or Linkerd — including mTLS, traffic management, canary deployments, circuit breaking, and mesh observability. |
Service Mesh Expert (Istio + Linkerd)
What Is a Service Mesh
A service mesh is an infrastructure layer that handles service-to-service communication transparently — without changing application code. It intercepts all network traffic through sidecar proxies injected alongside each service pod (or via eBPF kernel-level interception in newer implementations).
WITHOUT MESH WITH MESH (Sidecar)
----------- -------------------
Service A ──HTTP──> Service B Service A ──> [Envoy] ──mTLS──> [Envoy] ──> Service B
sidecar sidecar
App handles: retry, timeout, Mesh handles: retry, timeout,
auth, tracing mTLS, tracing, metrics — app knows nothing
Core capabilities the mesh provides — zero app code changes:
- mTLS: automatic certificate rotation, encrypted + authenticated service traffic
- Traffic management: weighted routing, header-based routing, retries, timeouts
- Observability: RED metrics (Rate, Errors, Duration) per service pair; distributed tracing
- Policy enforcement: authorization policies (who can call whom)
- Fault injection: inject latency/errors for chaos testing in production-like conditions
Istio
Architecture
CONTROL PLANE (istiod)
+------------------------------------------+
| Pilot Citadel Galley |
| (routing) (certs/mTLS) (config) |
+------------------------------------------+
| |
xDS API (gRPC) cert distribution
| |
DATA PLANE (per pod)
+--------------------+ +--------------------+
| App Container | | App Container |
| [Service A :8080] | | [Service B :8080] |
| | | |
| [Envoy :15001] | | [Envoy :15001] |
+--------------------+ +--------------------+
sidecar sidecar
istiod components:
- Pilot: pushes routing config (xDS) to Envoy sidecars; service discovery
- Citadel: CA; issues and rotates X.509 certificates for workload identity
- Galley: config validation and distribution
Sidecar Injection
kubectl label namespace default istio-injection=enabled
kubectl get namespace default --show-labels
istioctl kube-inject -f deployment.yaml | kubectl apply -f -
kubectl get pod <pod-name> -o jsonpath='{.spec.containers[*].name}'
mTLS: STRICT Mode
apiVersion: security.istio.io/v1beta1
kind: PeerAuthentication
metadata:
name: default
namespace: production
spec:
mtls:
mode: STRICT
---
apiVersion: security.istio.io/v1beta1
kind: PeerAuthentication
metadata:
name: legacy-service-permissive
namespace: production
spec:
selector:
matchLabels:
app: legacy-service
mtls:
mode: PERMISSIVE
istioctl x authz check <pod-name>
istioctl proxy-config secret <pod-name> -n production
kubectl exec <pod> -c istio-proxy -- curl -s http://localhost:15000/config_dump | grep tls
Authorization Policy (who can call whom)
apiVersion: security.istio.io/v1beta1
kind: AuthorizationPolicy
metadata:
name: payment-service-authz
namespace: production
spec:
selector:
matchLabels:
app: payment-service
action: ALLOW
rules:
- from:
- source:
principals: ["cluster.local/ns/production/sa/order-service"]
to:
- operation:
methods: ["POST"]
paths: ["/pay"]
Traffic Management
VirtualService — routing rules
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
name: product-service
namespace: production
spec:
hosts:
- product-service
http:
- match:
- headers:
x-canary-user:
exact: "true"
route:
- destination:
host: product-service
subset: canary
weight: 100
- route:
- destination:
host: product-service
subset: stable
weight: 90
- destination:
host: product-service
subset: canary
weight: 10
timeout: 5s
retries:
attempts: 3
perTryTimeout: 2s
retryOn: 5xx,reset,connect-failure,retriable-4xx
---
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
name: product-service-chaos
namespace: staging
spec:
hosts:
- product-service
http:
- fault:
delay:
percentage:
value: 10
fixedDelay: 2s
abort:
percentage:
value: 5
httpStatus: 503
route:
- destination:
host: product-service
subset: stable
DestinationRule — load balancing + circuit breaker
apiVersion: networking.istio.io/v1beta1
kind: DestinationRule
metadata:
name: product-service
namespace: production
spec:
host: product-service
trafficPolicy:
loadBalancer:
simple: LEAST_CONN
connectionPool:
tcp:
maxConnections: 100
http:
http1MaxPendingRequests: 50
http2MaxRequests: 200
maxRequestsPerConnection: 10
outlierDetection:
consecutiveGatewayErrors: 5
consecutive5xxErrors: 5
interval: 30s
baseEjectionTime: 30s
maxEjectionPercent: 50
minHealthPercent: 30
subsets:
- name: stable
labels:
version: stable
- name: canary
labels:
version: canary
trafficPolicy:
connectionPool:
http:
http1MaxPendingRequests: 10
Gateway — TLS termination at mesh edge
apiVersion: networking.istio.io/v1beta1
kind: Gateway
metadata:
name: api-gateway
namespace: production
spec:
selector:
istio: ingressgateway
servers:
- port:
number: 443
name: https
protocol: HTTPS
tls:
mode: SIMPLE
credentialName: api-tls-cert
hosts:
- "api.example.com"
- port:
number: 80
name: http
protocol: HTTP
tls:
httpsRedirect: true
hosts:
- "api.example.com"
---
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
name: api-routes
namespace: production
spec:
hosts:
- "api.example.com"
gateways:
- api-gateway
http:
- match:
- uri:
prefix: /orders
route:
- destination:
host: order-service
port:
number: 8080
- match:
- uri:
prefix: /products
route:
- destination:
host: product-service
port:
number: 8080
Observability
istioctl dashboard kiali
istioctl dashboard jaeger
istioctl dashboard grafana
istioctl analyze --namespace production
istioctl proxy-config routes <pod-name> -n production
istioctl proxy-config clusters <pod-name> -n production
istioctl proxy-config listeners <pod-name> -n production
kubectl -n production exec -it <pod> -c istio-proxy -- pilot-agent request GET /stats/prometheus | grep istio_requests_total
Automatic metrics exposed (no instrumentation needed):
istio_requests_total — request count by source, destination, response code
istio_request_duration_milliseconds — latency histogram
istio_tcp_connections_opened_total / closed_total — TCP connection tracking
Linkerd
Architecture (lighter than Istio)
CONTROL PLANE
+------------------------------------------+
| linkerd-controller linkerd-identity |
| linkerd-destination linkerd-proxy-injector|
+------------------------------------------+
|
DATA PLANE (per pod)
+-----------------------------+
| App Container :8080 |
| linkerd2-proxy :4143/4191 | <- Rust, purpose-built (vs Envoy)
+-----------------------------+
Key differences from Istio:
| Feature | Linkerd | Istio |
|---|
| Proxy | linkerd2-proxy (Rust) | Envoy (C++) |
| Config complexity | Low | High |
| Resource footprint | ~50MB/pod | ~150MB/pod |
| Traffic API | SMI (simple) | VirtualService (powerful) |
| mTLS | Auto, zero-config | Requires PeerAuthentication |
| Ecosystem | Smaller | Large (plugins, addons) |
Install and Inject
curl --proto '=https' --tlsv1.2 -sSfL https://run.linkerd.io/install | sh
linkerd check --pre
linkerd install --crds | kubectl apply -f -
linkerd install | kubectl apply -f -
linkerd check
kubectl get deploy order-service -o yaml | linkerd inject - | kubectl apply -f -
kubectl annotate namespace production linkerd.io/inject=enabled
linkerd check --proxy -n production
mTLS — Automatic, Zero-Config
linkerd viz edges deployment -n production
linkerd viz tap deployment/order-service -n production
linkerd viz edges pod -n production
Traffic Split (Canary via SMI)
apiVersion: split.smi-spec.io/v1alpha1
kind: TrafficSplit
metadata:
name: order-service-split
namespace: production
spec:
service: order-service
backends:
- service: order-service-stable
weight: 900m
- service: order-service-canary
weight: 100m
Observability — Golden Metrics
linkerd viz stat deployments -n production
linkerd viz stat routes -n production deploy/order-service
linkerd viz tap deploy/order-service -n production --to deploy/payment-service
linkerd viz dashboard
Golden metrics per service (automatic, no SDK needed):
- Success rate (non-5xx %)
- Request rate (RPS)
- Latency (p50, p95, p99)
When to Use a Service Mesh
Use a Mesh When:
- More than 5 microservices communicating with each other
- Compliance requires encrypted service-to-service traffic (mTLS)
- Canary deployments or A/B testing needed without app changes
- Distributed tracing without instrumenting every service
- Fine-grained traffic control (retries, timeouts, fault injection) centrally
- Zero-trust network security model required
Skip the Mesh When:
- Monolith or fewer than 3 services — overhead is not justified
- Team lacks bandwidth to operate and maintain the control plane
- Latency budget is extremely tight — sidecar adds ~1-5ms per hop
- Kubernetes expertise is limited — Istio complexity is real
- Simple use case: a library solves it (Resilience4j, Polly)
Alternatives to a Full Mesh:
Need Alternative
---- -----------
Circuit breaker Resilience4j (Java), Polly (.NET), resilience (Go)
Distributed tracing OpenTelemetry SDK in each service
mTLS cert-manager + Kubernetes NetworkPolicy
Traffic splitting Kubernetes native: two Deployments + manual weight
Rate limiting API Gateway (Kong, nginx)
Anti-Patterns
- Enabling STRICT mTLS before all services are injected — breaks traffic to uninjected services; use PERMISSIVE during migration
- Using VirtualService without matching DestinationRule subsets — subset not found → 503s
- Istio on a 1-node dev cluster — control plane alone consumes ~500MB RAM
- Ignoring
istioctl analyze — it catches misconfigured resources before they cause issues
- Too many retries without idempotency — retrying non-idempotent POST requests causes duplicate operations
- Giant outlierDetection ejection windows — ejecting 100% of hosts causes full outage; always set
maxEjectionPercent < 100
- Not setting resource limits on Envoy sidecars — noisy neighbor problem; sidecars consume unbounded CPU during traffic spikes
Checklist: Mesh Rollout