K8s by Example: DNS & Service Discovery

Kubernetes runs CoreDNS for internal name resolution. Every Service gets a DNS A record automatically. Pods resolve Service names to ClusterIPs without hardcoding addresses.

service.yaml

Services automatically get DNS records. This Service gets DNS: my-app.production.svc.cluster.local with an A record pointing to the ClusterIP.

apiVersion: v1
kind: Service
metadata:
  name: my-app
  namespace: production
spec:
  ports:
    - port: 80
  selector:
    app: my-app
terminal

DNS resolution from shortest to FQDN. Same namespace: just service name. Cross-namespace: service.namespace. With svc suffix: service.namespace.svc. Full FQDN (rarely needed): service.namespace.svc.cluster.local.

$ curl http://my-app
Hello from my-app!

$ curl http://my-app.other-namespace
Hello from my-app!

$ curl http://my-app.other-namespace.svc

$ curl http://my-app.other-namespace.svc.cluster.local
terminal

Services also get SRV records for named ports. SRV records contain port information, useful for dynamic port discovery.

$ nslookup my-app.default.svc.cluster.local
Server:    10.96.0.10
Address:   10.96.0.10#53

Name:      my-app.default.svc.cluster.local
Address:   10.96.0.100

$ nslookup -type=SRV _http._tcp.my-app.default.svc.cluster.local
_http._tcp.my-app.default.svc.cluster.local   SRV 0 100 80 my-app.default.svc.cluster.local
headless-service.yaml

Headless Services (clusterIP: None) return Pod IPs directly instead of a ClusterIP. DNS returns all matching Pod IPs. Essential for StatefulSets and client-side load balancing.

apiVersion: v1
kind: Service
metadata:
  name: my-db
spec:
  clusterIP: None
  ports:
    - port: 5432
  selector:
    app: postgres
terminal

StatefulSet Pods get stable, predictable DNS names via headless Services. Each Pod gets its own A record: pod-name.service.namespace.svc.cluster.local.

$ nslookup my-db-0.my-db.default.svc.cluster.local
Address: 10.244.1.5

$ nslookup my-db-1.my-db.default.svc.cluster.local
Address: 10.244.2.8

$ nslookup my-db-2.my-db.default.svc.cluster.local
Address: 10.244.3.12

$ psql -h my-db-0.my-db.default.svc.cluster.local
pod-dns-config.yaml

Pod DNS policy controls how Pods resolve names. ClusterFirst is default. Use None with custom dnsConfig for special cases. Options: Default, ClusterFirst, ClusterFirstWithHostNet, None.

spec:
  dnsPolicy: ClusterFirst
  dnsConfig:
    nameservers:
      - 8.8.8.8
    searches:
      - my-namespace.svc.cluster.local
    options:
      - name: ndots
        value: "5"
terminal

CoreDNS configuration is in a ConfigMap in kube-system. Customize for external DNS forwarding, custom domains, or caching. Pods get /etc/resolv.conf configured by kubelet.

$ kubectl get configmap coredns -n kube-system -o yaml
apiVersion: v1
kind: ConfigMap
data:
  Corefile: |
    .:53 {
        kubernetes cluster.local in-addr.arpa ip6.arpa {
           pods insecure
           fallthrough in-addr.arpa ip6.arpa
        }
        forward . /etc/resolv.conf
        cache 30
    }

$ kubectl exec my-pod -- cat /etc/resolv.conf
nameserver 10.96.0.10
search default.svc.cluster.local svc.cluster.local cluster.local
terminal

Debug DNS issues by checking CoreDNS pods, testing resolution from within the cluster, and verifying Service endpoints exist. Common issue: ndots setting causes external lookups to fail.

$ kubectl run dns-test --rm -it --image=busybox:1.28 -- sh
/ # nslookup my-app
Server:    10.96.0.10
Address 1: 10.96.0.10 kube-dns.kube-system.svc.cluster.local
Name:      my-app
Address 1: 10.96.0.100

/ # nslookup kubernetes.default
Name:      kubernetes.default
Address 1: 10.96.0.1

$ kubectl logs -n kube-system -l k8s-app=kube-dns
[INFO] 10.244.0.5:53842 - 12345 "A IN my-app.default.svc.cluster.local."

$ kubectl get pods -n kube-system -l k8s-app=kube-dns
NAME                       READY   STATUS    AGE
coredns-5644d7b6d9-abc12   1/1     Running   10d
coredns-5644d7b6d9-def34   1/1     Running   10d

Index | GitHub | Use arrow keys to navigate |