15.3 DNS Records for Pods
Alright, let’s pull back the curtain on one of Kubernetes’ neater party tricks: giving every Pod its own DNS name. You might be thinking, “It’s 2024, why is this impressive?” Because honestly, it should be table stakes. But the way Kubernetes does it is both elegant and, in places, a bit quirky. This isn’t just some load balancer magic; it’s a core part of the system’s service discovery fabric.
Here’s the deal: For a Pod to be discoverable via DNS, it needs an identity more stable than its fleeting IP address. Kubernetes provides this by creating a DNS record for each Pod that has one crucial feature: a defined hostname. This doesn’t happen for every Pod automatically. You have to opt-in, and you do it the Kubernetes way: by setting fields in the Pod spec.
How a Pod Gets Its DNS Name
A Pod earns its DNS entry by having a hostname set in its spec (spec.hostname). If you don’t set it, it defaults to the Pod’s name (the metadata.name). But here’s the kicker—for the Pod to actually be addressable within the DNS cluster, you almost always need to set a subdomain as well (spec.subdomain). This subdomain must match the name of a Headless Service (a Service with clusterIP: None) that exists in the same namespace.
Why the ceremony with the Headless Service? Because without it, the DNS subsystem has no idea what parent domain to attach your Pod’s hostname to. The Headless Service acts as that anchor point. It’s less of a “service” in the traditional sense here and more of a DNS domain declarator.
Let’s make this concrete. Say you have a stateful application, like a cache node, that needs a stable network identity.
# First, we define the Headless Service that provides our domain anchor.
apiVersion: v1
kind: Service
metadata:
name: pod-anchor # This will be the subdomain
namespace: my-app-ns
spec:
clusterIP: None # This makes it headless!
ports:
- port: 6379
---
# Then, a Pod that opts into DNS via that service.
apiVersion: v1
kind: Pod
metadata:
name: redis-node-1
namespace: my-app-ns
spec:
hostname: redis-node-1 # This is optional, as it defaults to metadata.name
subdomain: pod-anchor # This MUST match the headless service name
containers:
- name: redis
image: redis:7-alpine
ports:
- containerPort: 6379
With this setup, your Pod gets a fully qualified domain name (FQDN) following this pattern:
<pod-hostname>.<pod-subdomain>.<pod-namespace>.svc.<cluster-domain>
So for our example, assuming the standard cluster domain (cluster.local), the Pod’s FQDN becomes:
redis-node-1.pod-anchor.my-app-ns.svc.cluster.local
Any other Pod in the cluster can now ping or connect to redis-node-1.pod-anchor.my-app-ns.svc.cluster.local, and it will resolve directly to the Pod’s IP. No Service load-balancing in the way. It’s a straight shot.
The Nitty-Gritty: A Records and Readyness
This is where the designers made a choice you need to understand. The DNS record for the Pod isn’t created just because the Pod is scheduled. It’s created when the Pod is ready. Specifically, the Kubernetes DNS service (CoreDNS) watches the API server for Pods that have all their readiness conditions met (status.conditions.ready is True).
This is brilliant and annoying. Brilliant because it prevents traffic from being sent to a Pod that’s still booting up or failing its health checks. Annoying because if your readiness probe is misconfigured (too strict, too slow), your Pod will be alive but won’t be discoverable via DNS, leading to head-scratching “why can’t I connect?” moments. Always check kubectl get pod -o wide to see the Pod’s IP and kubectl describe pod to see its readiness state if your DNS lookups are failing.
The record created is a straight A record, not a CNAME. It points directly at the Pod’s IP. Simple, effective.
Common Pitfalls and Best Practices
- The Headless Service Must Exist: This is the most common mistake. You’ll set
subdomain: my-cool-domain, but if a Headless Service namedmy-cool-domaindoesn’t exist in the same namespace, nothing happens. No error, no record. It just silently ignores your request. It’s a classic Kubernetes “assume good intentions” move that can waste an afternoon. - It’s for Stable Pods: This pattern is fantastic for stateful workloads managed by a StatefulSet (which handles this hostname/subdomain setup for you automatically). It’s overkill and pointless for a Deployment where Pods are meant to be identical and ephemeral. For those, you want a regular ClusterIP Service for load-balanced discovery.
- Readiness is Key: As mentioned, tie your DNS readiness to your application’s true readiness. If your app takes 30 seconds to start listening on its port, your readiness probe must reflect that. A quick TCP socket check might not be sufficient.
- It’s Cluster-Local: These Pod DNS records are not exposed externally. They are for internal cluster communication only. If you need external access, you’re looking at Ingress or LoadBalancer Services, which is a whole other chapter of fun.
In essence, Pod DNS records are a powerful tool for direct, stable Pod-to-Pod communication when you need to break out of the Service abstraction. It’s a system that expects you to understand its rules but rewards you with precision and control once you do.