backend / microservices / 05_service_mesh.md

Service Mesh

5 interview angles 4 min read source

Service Mesh

A service mesh moves “cross-cutting service-to-service concerns” out of your application code and into a sidecar proxy that runs next to every pod. Mutual TLS, retries, timeouts, circuit breaking, traffic shaping, metrics — all configured at the mesh layer, transparently to the app.

What problem it solves

Without a mesh, every service has to reimplement (or import a library for):

  • mTLS between services
  • retries with backoff + jitter
  • circuit breaking
  • per-request timeouts
  • canary / weighted traffic splits
  • per-call metrics + tracing
  • rate limiting

In a polyglot stack, you write that 5 times — Python, Go, Java, Node, Rust. The mesh does it once at the network layer.

The architecture

+--------------+       +------------------+
|   Pod A      |       |   Pod B          |
|  +--------+  |       |  +--------+      |
|  | app    |  |       |  | app    |      |
|  +---+----+  |       |  +---+----+      |
|      | localhost                        |
|  +---v----+  |  mTLS  |  +---v----+     |
|  | proxy  +--+--------+->+ proxy  |     |
|  | (envoy)|  |        |  | (envoy)|     |
|  +--------+  |        |  +--------+     |
+--------------+        +------------------+
       ▲                        ▲
       │                        │
       └──── control plane (Istio, Linkerd) ────┘
            pushes config: mTLS certs, routes,
            traffic policies, telemetry
  • Data plane — the sidecar proxies (Envoy, Linkerd-proxy). Intercept all inbound/outbound traffic of the pod.
  • Control plane — pushes configuration to all proxies (Istio’s istiod, Linkerd’s controller).

The app code calls http://payments like before. The local proxy intercepts, terminates TLS, picks an instance, retries, records metrics, then forwards.

The big two

Istio Linkerd
Data plane Envoy linkerd2-proxy (Rust)
Complexity high lower
CRDs many (VirtualService, DestinationRule, AuthorizationPolicy, …) fewer
mTLS yes, configurable yes, default-on
Use cases large complex platforms simpler/smaller teams

Others: Consul Connect, Cilium Service Mesh (eBPF-based, no sidecar), AWS App Mesh (deprecated 2024 — AWS pivoting to VPC Lattice).

What you actually configure

mTLS

Default Istio config: every service talks to every service over mTLS, certificates rotated automatically by istiod. Your app code says http://; the proxy upgrades to https:// and presents the pod’s identity.

apiVersion: security.istio.io/v1beta1
kind: PeerAuthentication
metadata: { name: default, namespace: prod }
spec:
  mtls: { mode: STRICT }   # reject non-mTLS traffic

Retry + timeout

apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata: { name: payments }
spec:
  hosts: [payments]
  http:
  - timeout: 2s
    retries:
      attempts: 3
      perTryTimeout: 500ms
      retryOn: 5xx,connect-failure,reset
    route:
    - destination: { host: payments, subset: v1 }

Same retry rules you’d put in tenacity or httpx-retry, but applied uniformly across every language.

Traffic split (canary)

spec:
  http:
  - route:
    - destination: { host: payments, subset: v1 }
      weight: 90
    - destination: { host: payments, subset: v2 }
      weight: 10

90/10 traffic split between deployments. Combine with metrics-based rollouts (Argo Rollouts, Flagger) for automated canary analysis.

Authorization

apiVersion: security.istio.io/v1beta1
kind: AuthorizationPolicy
metadata: { name: payments-allow-orders }
spec:
  selector: { matchLabels: { app: payments } }
  rules:
  - from:
    - source: { principals: ["cluster.local/ns/prod/sa/orders"] }
    to:
    - operation: { methods: ["POST"], paths: ["/v1/charge"] }

“Only the orders service account in prod namespace may POST to /v1/charge on payments.” Cryptographically enforced by mTLS identities, not just IP/network rules.

What the mesh does NOT solve

  • Business logic retries. The mesh retries 5xx and timeouts. It doesn’t know about your payment semantics. App-level idempotency keys are still your problem.
  • Async messaging. Meshes intercept HTTP/gRPC, not Kafka/SQS. Kafka clients live inside the app.
  • Data consistency. Meshes don’t do sagas, outbox, distributed transactions.
  • Application observability. Metrics like “orders placed per second” are domain-specific and must be emitted by the app.

Costs

  • Latency — each hop adds a sidecar round-trip (sub-ms in practice; still real).
  • Memory — Envoy is ~50-150MB per pod. Multiply by pod count.
  • Operational complexity — you now run a control plane. Upgrades are non-trivial.
  • Debugging — when “the call failed”, is it the app, the local proxy, the remote proxy, or the control plane?

For < ~10 services, the mesh is usually overkill. For 50+ services and a polyglot stack, it’s the lowest-friction way to enforce mTLS + observability.

Alternatives at smaller scale

  • Library-basedhttpx + tenacity + manual mTLS via cert-manager. Works in a single-language shop.
  • API gateway only — for north-south traffic (client → cluster). East-west (service → service) is on you.
  • Cilium without sidecars — eBPF kernel-level networking; mTLS without sidecar memory cost. Newer; rapidly maturing.

Interview angle

  • “What is a service mesh?” — sidecar proxies + control plane that handle mTLS, retries, timeouts, traffic policy, and telemetry transparently to the application code.
  • “What does Istio give you that you couldn’t do with libraries?” — uniform policy across languages without code changes (polyglot wins), centralized config you can change at runtime, mTLS with automatic cert rotation, traffic shifting for canary without app awareness.
  • “What’s the cost?” — latency hop, memory per pod (~100MB), operational burden of running the control plane, harder debugging. For small fleets it’s not worth it.
  • “Does a mesh solve distributed transactions?” — no. It moves networking concerns out of your code; data consistency stays an application problem (sagas, outbox, idempotency).
  • “Sidecar vs sidecar-less mesh (Cilium)?” — sidecar adds per-pod memory and an extra hop. eBPF-based meshes (Cilium) push enforcement into the kernel; less overhead but newer tooling.