Why Pod IPs Are Useless for Service Discovery
Pod IP addresses are ephemeral. When a Pod crashes and restarts, it gets a new IP. When you scale a Deployment, new Pods appear with new IPs. Hardcoding IP addresses into application configuration is a recipe for constant breakage.
Kubernetes solves this with two mechanisms working together: Services (which provide a stable virtual IP) and CoreDNS (which maps human-readable names to those stable IPs).
How CoreDNS Works
Every Kubernetes cluster runs CoreDNS as a Deployment in the kube-system namespace. It is the cluster's internal DNS server. Every Pod is automatically configured (via /etc/resolv.conf) to use CoreDNS for DNS resolution.
When you create a Service, CoreDNS automatically creates DNS records for it. The naming convention follows a predictable pattern:
<service-name>.<namespace>.svc.cluster.local
For example, a Service named payments-api in the billing namespace gets the DNS name:
payments-api.billing.svc.cluster.local → 10.96.45.12 (ClusterIP)
DNS Search Domains: The Shortcut
Pods don't need to type the full FQDN every time. CoreDNS configures each Pod's /etc/resolv.conf with search domains:
search default.svc.cluster.local svc.cluster.local cluster.local
This means a Pod in the default namespace can reach a Service in the same namespace by just using its short name (e.g., http://payments-api). The DNS resolver automatically tries appending each search domain until it finds a match.
To reach a Service in a different namespace, you must include at least the namespace: http://payments-api.billing.
Headless Services and DNS
A special case: if you create a Service with clusterIP: None (called a Headless Service), CoreDNS does not return a single virtual IP. Instead, it returns the individual IP addresses of all matching Pods. This is used by StatefulSets (databases, message queues) where clients need to connect to specific Pod instances by name, not a random one behind a load balancer.
Defining one is the same as an ordinary Service, just with the cluster IP explicitly set to none. With a headless Service, individual StatefulSet Pods become addressable as:
postgres-0.postgres.default.svc.cluster.local
postgres-1.postgres.default.svc.cluster.local
A common interview question is: "How does a Pod find a Service?" Walk through the full chain: the application makes a DNS query → the Pod's /etc/resolv.conf points to CoreDNS → CoreDNS returns the Service's ClusterIP → the packet is sent to that ClusterIP → kube-proxy (running on every node) intercepts it via iptables/IPVS rules and rewrites the destination to one of the healthy Pod IPs. This end-to-end answer demonstrates you understand all four layers.