Headless Service
What does a Service with clusterIP None give you instead of a virtual IP? It gets no kube-proxy rules and no load balancing from the platform. A lookup of the Service name returns one A record per ready Pod, and the client connects to a Pod IP it picks itself. Under a StatefulSet each Pod also gets its own name, such as web-0.nginx.default.svc.cluster.local, which follows that replica even when it comes back with a new IP.
Play the animation 6 steps, animated, best on a wide screen
Headless Service, step by step
-
StatefulSet web runs three Pods behind Service nginx, whose clusterIP is None. There is no virtual IP, so kube-proxy writes no rules for it. Under the default ClusterFirst DNS policy the lookup of nginx.default.svc.cluster.local still goes to CoreDNS through the kube-dns Service.
Play from step 1 -
For a headless Service with a selector the control plane still keeps an EndpointSlice, and CoreDNS answers from it: one A record for every ready Pod, three addresses instead of one virtual IP. Nothing balances the traffic, and the whole set comes back for the client to choose from.
Play from step 2 -
The client connects straight to one address from that set, here web-1 at 10.244.3.4. No ClusterIP is in the path and no Service rule rewrites the destination, so the client chooses from the set itself and kube-proxy has no say.
Play from step 3 -
Pod web-2 fails its readiness probe. Its endpoint turns ready=false in the EndpointSlice and CoreDNS stops publishing it, so the same lookup now returns two addresses. A Service with publishNotReadyAddresses set would keep web-2 in the answer.
Play from step 4 -
Each Pod also gets a name of its own, the Pod name under the governing Service that the StatefulSet serviceName points at: web-0.nginx.default.svc.cluster.local. That lookup returns web-0 alone, which lets a client reach one specific replica, such as a primary.
Play from step 5 -
Delete web-0 and the StatefulSet recreates it under the same name, here with a new IP. Once it is ready the same lookup returns 10.244.3.8: the name is stable, the address is not. A client that cached the answer can hold it until the record TTL expires, 30 seconds under kubeadm.
Play from step 6