Service networking internals

Kubernetes · Expert · 14 min

Debugging odd latency, connection resets or lost source IPs requires knowing what the packets really do.

The Kubernetes network model requires every pod to have its own IP and reach every other pod without NAT. The CNI plugin implements this: overlays (VXLAN, Geneve), routed networks (BGP with Calico), or cloud-native VPC IPs per pod (AWS VPC CNI, Azure CNI).

A ClusterIP isn't bound to any interface. kube-proxy on each node watches Services and EndpointSlices and programs the kernel so that packets to ClusterIP:port are DNATed to one ready backend pod:

  • iptables mode: a chain per Service picking a backend at random; rule updates get slow with many thousands of Services.
  • IPVS mode: kernel load balancer with hash tables and more algorithms.
  • nftables mode (GA since 1.33): the modern replacement for iptables mode, with better update performance.

eBPF dataplanes such as Cilium can replace kube-proxy entirely, doing the translation at socket or TC level.

Consequences:

  • Load balancing is per connection, not per request: long-lived HTTP/2 or gRPC connections stick to one pod. Use client-side balancing (headless Service) or a mesh/L7 proxy.
  • Traffic from outside through NodePort/LoadBalancer may hop nodes and be SNATed, hiding the client IP. externalTrafficPolicy: Local preserves it and avoids the hop, but only nodes with local pods receive traffic.
  • internalTrafficPolicy and topology-aware routing can keep traffic in-zone to save cross-zone costs.

Example

# On a node using kube-proxy in iptables mode:
iptables -t nat -L KUBE-SERVICES -n | grep shop/api
# KUBE-SVC-XYZ  tcp -- 0.0.0.0/0  10.96.112.7  /* shop/api:http cluster IP */ tcp dpt:80
iptables -t nat -L KUBE-SVC-XYZ -n
# KUBE-SEP-AAA  statistic mode random probability 0.33333
# KUBE-SEP-BBB  statistic mode random probability 0.50000
# KUBE-SEP-CCC
kubectl -n kube-system get cm kube-proxy -o yaml | grep -m1 'mode:'

# preserve client source IPs for an external Service
kubectl patch svc web -p '{"spec":{"externalTrafficPolicy":"Local"}}'

Remember

  • CNI gives pods routable IPs; kube-proxy or eBPF implements ClusterIPs
  • Balancing is per connection: watch out with gRPC/HTTP2
  • externalTrafficPolicy: Local keeps the client IP and avoids an extra hop

Watch out for

  • Expecting a ClusterIP to spread gRPC requests across pods when clients hold one long connection.

Check yourself

  1. A gRPC client sends all requests to one of five backend pods behind a ClusterIP. Why?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: kubernetes.io/docs/reference/networking/virtual-ips/

Builds on

Unlocks

Nothing builds on this yet.