Compare Service‑Mesh Locality Failover against Native Kubernetes Topology Hints for Multi‑Zone East‑West Traffic
Design review memo – Sophia Lin, Cloud & Automation Architect
Introduction
Service Mesh Locality Failover
Service‑mesh data planes (Istio, Linkerd, Consul Connect) prefer endpoints in the same failure domain as the source pod. When local endpoints become unhealthy or exhausted, the mesh fails over to remote zones while preserving connection‑level semantics (retries, timeouts, circuit breakers). Decisions are made inside the sidecar proxy using:
- Locality load‑balancing – weight‑based routing favoring same‑zone endpoints.
- Outlier detection / circuit breaking – automatic ejection of misbehaving hosts.
- Retry budgets & timeout propagation – fine‑grained control over request latency for remote fallback.
Configuration is via mesh‑specific CRDs (Istio DestinationRule/VirtualService, Linkerd Server/ServiceProfile, Consul ServiceDefaults).
Native Kubernetes Topology Hints
Kubernetes provides control‑plane‑only mechanisms that influence traffic direction without a sidecar:
| Mechanism | Scope | What it does | Maturity (K8s 1.29) |
|---|---|---|---|
endpointSlice topology hints (topology.kubernetes.io/zone) | EndpointSlice objects | Informs kube‑proxy/IPVS to prefer endpoints sharing the client node’s zone label. | GA (since 1.19) |
service.spec.internalTrafficPolicy (Local | Cluster) | Service‑level hint | Local forces traffic to endpoints on the same node; falls back to Cluster if none exist. | GA |
topologySpreadConstraints (pod placement) | PodSpec | Influences where pods are scheduled, indirectly affecting traffic locality. | GA |
service.alpha.kubernetes.io/topology-aware-hints (deprecated) | Service annotation | Early zone‑aware routing attempt; removed in 1.24. | Deprecated/removed |
The native approach is stateless from the application’s view: kube‑proxy/IPVS consults endpoint‑slice labels and applies a simple “same‑zone first” algorithm. No retries, circuit breaking, or timeout propagation are built in; those must be added at the application or service‑mesh layer.
Architecture Comparison
Service Mesh Locality Failover
+-------------------+ +-------------------+ +-------------------+
| Client Pod A | | Client Pod B | | Client Pod C |
| (zone us-east-1a) | | (zone us-east-1b) | | (zone us-east-1c) |
+--------+----------+ +--------+----------+ +--------+----------+
| | |
| sidecar (Envoy) | sidecar (Envoy) | sidecar (Envoy)
v v v
+-------------------+ +-------------------+ +-------------------+
| Istio Proxy | | Istio Proxy | | Istio Proxy |
| (locality LB) | | (locality LB) | | (locality LB) |
+--------+----------+ +--------+----------+ +--------+----------+
| | |
| xDS (ADS) | xDS (ADS) | xDS (ADS)
v v v
+-------------------+ +-------------------+ +-------------------+
| Control Plane |<------>| Control Plane |<------>| Control Plane |
| (Istiod) | | (Istiod) | | (Istiod) |
+-------------------+ +-------------------+ +-------------------+
^ ^ ^
| EndpointSlice (zone) | EndpointSlice (zone) | EndpointSlice (zone)
| | |
+-------------------+ +-------------------+ +-------------------+
| Backend Pods | | Backend Pods | | Backend Pods |
| (zone us-east-1a) | | (zone us-east-1b) | | (zone us-east-1c) |
+-------------------+ +-------------------+ +-------------------+
- Data plane – Envoy sidecars perform locality‑aware load balancing (weighted round‑robin with zone‑local priority).
- Control plane – Istiod watches Kubernetes EndpointSlices, aggregates zone labels, and pushes locality metadata via xDS.
- Failover – When all local endpoints are marked unhealthy (via outlier detection or health checks), Envoy falls back to the next‑best locality (same‑region, then any zone).
- Policy scope – Retries, timeouts, circuit breakers, and TLS are defined in mesh CRDs and applied uniformly regardless of underlying topology.
Native Kubernetes Topology Hints
+-------------------+ +-------------------+ +-------------------+
| Client Pod A | | Client Pod B | | Client Pod C |
| (zone us-east-1a) | | (zone us-east-1b) | | (zone us-east-1c) |
+--------+----------+ +--------+----------+ +--------+----------+
| | |
| kube‑proxy (IPVS) | kube‑proxy (IPVS) | kube‑proxy (IPVS)
v v v
+-------------------+ +-------------------+ +-------------------+
| Service VIP | | Service VIP | | Service VIP |
| (clusterIP) | | (clusterIP) | | (clusterIP) |
+--------+----------+ +--------+----------+ +--------+----------+
| | |
| EndpointSlice (zone) | EndpointSlice (zone) | EndpointSlice (zone)
v v v
+-------------------+ +-------------------+ +-------------------+
| Backend EP A | | Backend EP B | | Backend EP C |
| (zone us-east-1a) | | (zone us-east-1b) | | (zone us-east-1c) |
+-------------------+ +-------------------+ +-------------------+
- Control plane only – Scheduler places pods; EndpointSlice controller annotates each endpoint with its node’s zone label (
topology.kubernetes.io/zone). - Data plane – kube‑proxy (or IPVS) performs a simple “same‑zone first” selection when load‑balancing traffic to a Service’s clusterIP. No per‑request state; if no local endpoints exist, traffic spreads to any available endpoint.
- Failover – Implicit: if the local zone has zero ready endpoints, kube‑proxy selects a non‑local endpoint. No active health‑based ejection; readiness relies solely on pod
Readycondition. - Policy scope – Only
internalTrafficPolicy(LocalvsCluster) and endpoint‑slice hints influence routing. Retries, timeouts, and circuit breaking must be implemented by the application or an external proxy (e.g., a sidecar without full mesh features).
Key Differences and Similarities
| Aspect | Service Mesh Locality Failover | Native Kubernetes Topology Hints |
|---|---|---|
| Decision point | Sidecar proxy (data plane) – per‑request, stateful | kube‑proxy/IPVS (data plane) – per‑connection, stateless |
| Health awareness | Active outlier detection, circuit breaking, health checks | Passive – relies on pod Ready only |
| Retry/timeout semantics | Configurable per‑route (retry budgets, per‑try timeout, hedging) | None; must be added elsewhere |
| Policy granularity | Per‑service, per‑subset, per‑route (VirtualService/DestinationRule) | Service‑wide (internalTrafficPolicy) or endpoint‑slice label based |
| Operational overhead | Requires sidecar deployment, control plane, xDS tuning | No extra components; uses core Kubernetes |
| Failure domain visibility | Explicit zone labels in load‑balancing weights; per‑zone metrics exposed | Zone label influences selection only; no per‑zone metrics in kube‑proxy |
| Compatibility | Works with any CNI; requires Istio/Linkerd/Consul sidecar | Works with vanilla Kubernetes; no sidecar needed |
| Blast‑radius control | Can isolate failures to a zone via outlier ejection and connection limits | Limited – if a zone’s endpoints become unready, traffic shifts wholesale to other zones |
Both approaches aim to prefer same‑zone endpoints to reduce latency and cross‑zone data transfer costs. The mesh adds rich traffic‑management capabilities at the cost of operational complexity; the native hints provide a low‑overhead, best‑effort locality preference.
Design Review and Considerations
Ownership of Failover: Service Mesh vs Kubernetes
| Consideration | Service Mesh Ownership | Native Kubernetes Ownership |
|---|---|---|
| Failure detection | Active (health checks, outlier detection, continuous metrics) | Passive (pod readiness only) |
| Policy enforcement | Centralized in mesh CRDs; version‑controllable via GitOps | Scattered across Service spec, EndpointSlice labels, and possibly application logic |
| Operational boundary | Inside the service‑to‑service communication path; transparent to apps | At the kube‑proxy layer; visible to any client talking to the clusterIP |
| Failure domain granularity | Per‑subset, per‑route; can differentiate by app version, traffic class | Per‑service only (or per‑EndpointSlice if multiple Services) |
| Impact on other layers | Does not affect pod scheduling; only traffic routing | Influences nothing beyond traffic routing; scheduling remains independent |
Recommendation: For mission‑critical, latency‑sensitive east‑west traffic requiring deterministic behavior under partial zone degradation (e.g., financial trading, real‑time gaming), own failover in the service mesh. The mesh provides active health‑based ejection, retry budgets, and observable per‑zone metrics—essential for controlling blast radius and preventing cascading timeouts.
For best‑effort, low‑latency workloads where simplicity and low operational overhead are paramount (e.g., internal CRUD APIs, batch jobs), native topology hints may suffice, provided the application tolerates occasional cross‑zone latency and implements its own retries/timeouts.
Policy Duplication and Contradictory Behavior
Duplication occurs when both mesh and Kubernetes influence the same traffic flow:
| Scenario | Potential Conflict | Symptom |
|---|---|---|
Mesh DestinationRule sets loadBalancerPolicy: ROUND_ROBIN with locality weighting, while Service internalTrafficPolicy: Local forces kube‑proxy to pick only local endpoints | Mesh may send traffic to a remote endpoint (due to locality weight) but kube‑proxy drops it because Local forbids cross‑zone forwarding → connection resets or No healthy upstream errors | Increased 502/503 errors, spikes in connection‑reset metrics |
Mesh outlier detection ejects a pod, but the pod remains Ready in Kubernetes → endpoint slice still advertised | Mesh stops sending traffic to the pod, but kube‑proxy may still consider it a valid endpoint for Local policy if no other local endpoints exist → traffic black‑holed until pod restart | Unidirectional latency increase, failed health checks from mesh side |
| Mesh retry budget (e.g., 2 retries per request) combined with kube‑proxy’s stateless load balancing may cause retries to hit the same failed endpoint repeatedly if the mesh view lags | Retries exhausted quickly, leading to higher latency and timeout errors | Observed latency spikes during zone degradation, despite mesh retry configuration |
Mitigation strategy:
- Single source of truth for locality preference – choose either mesh weighting or Kubernetes
internalTrafficPolicy, not both. - If retaining both for redundancy, align policies: set
internalTrafficPolicy: Cluster(default) when using mesh locality weighting, allowing the mesh to make the final decision. - Implement admission webhooks that reject contradictory configurations (e.g., a
DestinationRulewith locality weight paired with a Service annotatedinternalTrafficPolicy: Local). - Use mesh‑only metrics (Envoy stats, Istio telemetry) to drive decisions; disable kube‑proxy’s locality logic by setting
service.spec.sessionAffinity: Noneand rely on the mesh for all load‑balancing.
Timeout Handling and Partial Zone Degradation
Partial zone degradation (increased latency, packet loss, intermittent pod unreadiness) challenges timeout legibility:
| Layer | Timeout source | Visibility | Interaction with partial degradation |
|---|---|---|---|
| Application | Application‑level timeouts (e.g., HTTP client) | High – explicit in code | Unaffected by infrastructure; may mask underlying issues |
| Service Mesh | Per‑route timeout, retry budget, per‑try timeout, hedging | Medium – exposed via Envoy stats, Prometheus | Mesh can actively short‑circuit requests to a degraded zone after outlier detection triggers; timeout values stay constant, but effective latency drops because failing endpoints are ejected |
| Kubernetes | None built‑in; relies on application or sidecar | Low – only connection‑level timeouts from kube‑proxy (TCP idle timeout) | kube‑proxy keeps trying to connect to an endpoint until the underlying TCP connection times out (default ~2 min). No fast‑fail mechanism; degraded zone leads to long‑tail latency spikes |
Design implication:
When using native topology hints, application‑level timeouts must be conservative (e.g., 200‑500 ms for RPC) because the infrastructure will not fast‑fail a misbehaving endpoint.
When using a service mesh, you can delegate timeout enforcement to the mesh, allowing the application to use relatively generous timeouts (e.g., 1 s) while the mesh enforces tighter per‑try limits and hedging, keeping latency observable and controllable.
To keep timeout handling legible during partial zone degradation:
- Define a clear timeout hierarchy – application timeout ≥ mesh per‑try timeout ≥ mesh hedging timeout ≥ network RTT.
- Export mesh timeout metrics (
envoy_cluster_upstream_rq_timeout,retry_budget_exceeded) alongside application latency histograms. - Set alerting on rising retry‑budget consumption – an early indicator that the mesh is actively shielding the application from zone degradation.
- Avoid duplicating timeout logic across application, mesh, and Kubernetes layers; let the mesh be the single authority for timeout and retry behavior when it is in the data path.
End of memo.