DNS Records and FQDN
What decides the shape of the DNS answer a cluster gives back? A Service name is really web.default.svc.cluster.local, built from the Service, its namespace, the literal svc that marks a Service record, and the cluster domain. It returns an A record holding the ClusterIP, or one A record per ready Pod when headless, while a named port publishes SRV under a _http._tcp prefix. With pods enabled in CoreDNS, a Pod is reachable at 10-244-2-7.default.pod.cluster.local.
Play the animation 5 steps, animated, best on a wide screen
DNS Records and FQDN, step by step
-
The full name is web.default.svc.cluster.local: the Service, its namespace, the literal svc, then the cluster domain. Under the default ClusterFirst policy a Pod resolv.conf carries search domains and ndots:5, so a short name like web is expanded to this fully qualified form before it leaves the Pod. Every record below is a variation on these four segments.
Play from step 1 -
Ask for the name itself and you get an A record, or AAAA on IPv6, pointing at the Service ClusterIP, 10.96.0.20. This is the common case: a name in, the stable virtual IP out, which the Service rules kube-proxy writes then send on to one Pod. The address answered is the web Service, not the kube-dns ClusterIP the question was sent to.
Play from step 2 -
A named port also publishes an SRV record. The name grows a prefix, _http._tcp, naming the port and the protocol, and the answer carries the port number and the target host. It lets a client discover which port a Service exposes without that port number being hard-coded in the client.
Play from step 3 -
If the Service is headless, the name does not change at all: the client asks exactly what it asked for the A record. What changes is the answer, one A record per ready Pod instead of a single virtual IP, here three of them, and the client chooses an endpoint itself.
Play from step 4 -
Finally a Pod has a record of its own, a form that predates the DNS spec, and here the name changes twice: the Pod IP written with dashes takes the place of the Service, and the subdomain flips from svc to pod. CoreDNS only serves it when the kubernetes plugin has pods enabled, which kubeadm sets to insecure by default, and in that mode it answers from the name without checking that such a Pod exists. A stable way to reach one specific replica is a StatefulSet Pod hostname under a headless Service.
Play from step 5