🕸️ 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.

Control Plane Istiod / Linkerd Control Plane Pod A App container Proxy Envoy/proxy2 Pod B Proxy Envoy/proxy2 App container mTLS encrypted lo lo xDS config xDS config

🔒 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.

⚠️ Service meshes add operational complexity Sidecars increase pod count, add ~10–50ms latency (Envoy proxy overhead), consume ~50–100MB RAM per pod, and make debugging harder. Only adopt if you need the capabilities — don't add a mesh just because it's popular.

🔀 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/*"]
💡 Use PERMISSIVE mode during rollout When adding Istio to an existing cluster, start with 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:

MetricMeaningAlert threshold
istio_requests_totalRequest count by source, destination, response codeError rate > 1%
istio_request_duration_millisecondsLatency histogram per service pairP99 > SLO
istio_tcp_connections_opened_totalActive TCP connectionsSudden spike
envoy_cluster_upstream_cx_activeActive upstream connections per clusterApproaching 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
ℹ️ Circuit breaking happens at the proxy, not the app When an upstream pod crosses the outlier threshold, Envoy stops routing requests to it for the ejection period. The app pod is not killed — it just receives no traffic. This prevents a slow/erroring backend from cascading failures to callers.

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
FeatureIstioLinkerd
ProxyEnvoy (C++)linkerd2-proxy (Rust)
RAM per sidecar~50–100 MB~10–20 MB
Added latency~5–15ms (p99)~1–3ms (p99)
Automatic mTLSYes (PERMISSIVE default)Yes (on by default)
Traffic shiftingVirtualService (powerful)HTTPRoute (Gateway API)
JWT/OIDC authYes (RequestAuthentication)No
gRPC supportYesYes
Sidecar-less modeAmbient (GA 1.22)No
Learning curveSteepGentle

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

Q1. You enable PeerAuthentication with mode: STRICT in a namespace that still has some pods without Istio sidecars. What happens to traffic from those non-meshed pods?
  • A) Their traffic is automatically encrypted by the kernel
  • B) Their plaintext traffic is rejected — STRICT mode requires mTLS from all callers
  • C) They bypass the policy since they have no sidecar
  • D) They are automatically restarted with a sidecar injected
B) Plaintext traffic is rejected. STRICT mode means the receiving pod's Envoy sidecar will only accept mTLS connections. Any caller without a sidecar sending plaintext will get a connection reset. Use PERMISSIVE mode during migration so both meshed and non-meshed callers can coexist, then switch to STRICT once all pods have sidecars.
Q2. You want to send 5% of production traffic to a new version (v2) while keeping 95% on v1 — without changing any application code or Kubernetes Service. Which Istio resources do you need?
  • A) Two separate Kubernetes Services with different selectors
  • B) A DestinationRule defining v1/v2 subsets + a VirtualService with weighted routing
  • C) A HorizontalPodAutoscaler targeting v2 pods
  • D) An Ingress resource with canary annotations
B) DestinationRule + VirtualService. The DestinationRule defines subsets (v1/v2) by pod label. The VirtualService specifies weights (95/5). The existing Kubernetes Service is unchanged — Istio's routing operates at a layer above kube-proxy, splitting traffic in the Envoy sidecar based on the VirtualService rules.
Q3. What does outlierDetection in an Istio DestinationRule implement, and what triggers an endpoint ejection?
  • A) Rate limiting — ejects when too many requests per second are received
  • B) Circuit breaking — ejects an endpoint when it returns consecutive errors, stopping traffic to it temporarily
  • C) Load balancing — removes the least-loaded endpoint to rebalance
  • D) mTLS enforcement — ejects endpoints that don't present valid certificates
B) Circuit breaking via outlier detection. When a backend pod returns N consecutive 5xx errors (or connection failures) within the detection interval, Envoy ejects it from the load-balancing pool for the base ejection time. This prevents a degraded pod from receiving traffic and causing cascading failures to its callers.