Skip to content
LinkState
Go back

Mesh locality failover or native zone hints

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:

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:

MechanismScopeWhat it doesMaturity (K8s 1.29)
endpointSlice topology hints (topology.kubernetes.io/zone)EndpointSlice objectsInforms kube‑proxy/IPVS to prefer endpoints sharing the client node’s zone label.GA (since 1.19)
service.spec.internalTrafficPolicy (Local | Cluster)Service‑level hintLocal forces traffic to endpoints on the same node; falls back to Cluster if none exist.GA
topologySpreadConstraints (pod placement)PodSpecInfluences where pods are scheduled, indirectly affecting traffic locality.GA
service.alpha.kubernetes.io/topology-aware-hints (deprecated)Service annotationEarly 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) |
+-------------------+        +-------------------+        +-------------------+

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) |
+-------------------+        +-------------------+        +-------------------+

Key Differences and Similarities

AspectService Mesh Locality FailoverNative Kubernetes Topology Hints
Decision pointSidecar proxy (data plane) – per‑request, statefulkube‑proxy/IPVS (data plane) – per‑connection, stateless
Health awarenessActive outlier detection, circuit breaking, health checksPassive – relies on pod Ready only
Retry/timeout semanticsConfigurable per‑route (retry budgets, per‑try timeout, hedging)None; must be added elsewhere
Policy granularityPer‑service, per‑subset, per‑route (VirtualService/DestinationRule)Service‑wide (internalTrafficPolicy) or endpoint‑slice label based
Operational overheadRequires sidecar deployment, control plane, xDS tuningNo extra components; uses core Kubernetes
Failure domain visibilityExplicit zone labels in load‑balancing weights; per‑zone metrics exposedZone label influences selection only; no per‑zone metrics in kube‑proxy
CompatibilityWorks with any CNI; requires Istio/Linkerd/Consul sidecarWorks with vanilla Kubernetes; no sidecar needed
Blast‑radius controlCan isolate failures to a zone via outlier ejection and connection limitsLimited – 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

ConsiderationService Mesh OwnershipNative Kubernetes Ownership
Failure detectionActive (health checks, outlier detection, continuous metrics)Passive (pod readiness only)
Policy enforcementCentralized in mesh CRDs; version‑controllable via GitOpsScattered across Service spec, EndpointSlice labels, and possibly application logic
Operational boundaryInside the service‑to‑service communication path; transparent to appsAt the kube‑proxy layer; visible to any client talking to the clusterIP
Failure domain granularityPer‑subset, per‑route; can differentiate by app version, traffic classPer‑service only (or per‑EndpointSlice if multiple Services)
Impact on other layersDoes not affect pod scheduling; only traffic routingInfluences 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:

ScenarioPotential ConflictSymptom
Mesh DestinationRule sets loadBalancerPolicy: ROUND_ROBIN with locality weighting, while Service internalTrafficPolicy: Local forces kube‑proxy to pick only local endpointsMesh 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 errorsIncreased 502/503 errors, spikes in connection‑reset metrics
Mesh outlier detection ejects a pod, but the pod remains Ready in Kubernetes → endpoint slice still advertisedMesh 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 restartUnidirectional 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 lagsRetries exhausted quickly, leading to higher latency and timeout errorsObserved latency spikes during zone degradation, despite mesh retry configuration

Mitigation strategy:

  1. Single source of truth for locality preference – choose either mesh weighting or Kubernetes internalTrafficPolicy, not both.
  2. If retaining both for redundancy, align policies: set internalTrafficPolicy: Cluster (default) when using mesh locality weighting, allowing the mesh to make the final decision.
  3. Implement admission webhooks that reject contradictory configurations (e.g., a DestinationRule with locality weight paired with a Service annotated internalTrafficPolicy: Local).
  4. Use mesh‑only metrics (Envoy stats, Istio telemetry) to drive decisions; disable kube‑proxy’s locality logic by setting service.spec.sessionAffinity: None and 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:

LayerTimeout sourceVisibilityInteraction with partial degradation
ApplicationApplication‑level timeouts (e.g., HTTP client)High – explicit in codeUnaffected by infrastructure; may mask underlying issues
Service MeshPer‑route timeout, retry budget, per‑try timeout, hedgingMedium – exposed via Envoy stats, PrometheusMesh 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
KubernetesNone built‑in; relies on application or sidecarLow – 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:

  1. Define a clear timeout hierarchy – application timeout ≥ mesh per‑try timeout ≥ mesh hedging timeout ≥ network RTT.
  2. Export mesh timeout metrics (envoy_cluster_upstream_rq_timeout, retry_budget_exceeded) alongside application latency histograms.
  3. Set alerting on rising retry‑budget consumption – an early indicator that the mesh is actively shielding the application from zone degradation.
  4. 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.


Share this post on:

Previous Post
The First Ping Fails Then Everything Looks Fine
Next Post
One virtual MAC typo and a week of weirdness