CoreDNS and service discovery

How Services and Pods get DNS names, how a Pod looks them up, and how to change the Corefile safely.

40 min 3 hands-on tasksNetworking · 20% of the exam

Key ideas in this lesson · tap one for a simple explanation

What you will be able to do

  • Predict the DNS name of any Service, headless Service or Pod, and look it up from a client Pod.
  • Set dnsPolicy and dnsConfig on a Pod, and check the result in its /etc/resolv.conf.
  • Read the CoreDNS Corefile and change it safely.

Inside a cluster, apps find each other by name. Your frontend calls http://api, and DNS turns api into the 's IP. is the DNS server that answers. It runs as a in kube-system, behind a Service called kube-dns, and one holds its settings. The exam topic is Understand and use CoreDNS.

How a Pod looks up a name

Your app

looks up the name inventory

/etc/resolv.conf

in every Pod; says where to ask

nameserver 10.96.0.10

CoreDNS

behind the kube-dns Service, at 10.96.0.10

The answer

the ClusterIP of Service inventory

CoreDNS reads Services and Pods from the API, so a new Service has a name within seconds.

The names CoreDNS answers

Every Service gets a name made of its own name, its and svc.cluster.local. For Service inventory in namespace nw-dns, that is inventory.nw-dns.svc.cluster.local. In the table, an A record is a DNS answer that maps a name to an IP.

ObjectNameDNS answers with
Service ()<svc>.<ns>.svc.cluster.localAn A record: the ClusterIP
Headless Service<svc>.<ns>.svc.cluster.localOne A record per ready IP
Pod behind a headless Service<pod>.<svc>.<ns>.svc.cluster.localAn A record: that Pod's IP
ExternalName Service<svc>.<ns>.svc.cluster.localA CNAME (an alias) to spec.externalName
Any Pod<a-b-c-d>.<ns>.pod.cluster.local (its IP, with dashes)An A record: that IP

What a Pod's resolver does

The writes a file called /etc/resolv.conf into every Pod. It tells the Pod where to send lookups, and how to finish short names.

Terminal
kubectl -n nw-dns exec client -- cat /etc/resolv.conf
# search nw-dns.svc.cluster.local svc.cluster.local cluster.local
# nameserver 10.96.0.10
# options ndots:5

kubectl -n kube-system get svc kube-dns      # 10.96.0.10: the ClusterIP of CoreDNS
  • nameserver 10.96.0.10 is the ClusterIP of the kube-dns Service. Every lookup goes there.
  • search lists the endings tried for a short name. That is why inventory works inside nw-dns, and inventory.nw-dns works from any namespace.
  • ndots:5 means a name with fewer than five dots gets each search ending tried first. So api.example.com costs three failed cluster lookups before the real one. A dot at the end (api.example.com.) skips the search list.

dnsPolicy and dnsConfig

Two Pod fields change that file. dnsPolicy picks where lookups go. dnsConfig adds your own settings on top.

dnsPolicyWhere the Pod's lookups go
ClusterFirst (the default)To CoreDNS, with the cluster search list. CoreDNS passes names outside the cluster on to the upstream DNS servers.
DefaultTo the 's own DNS servers, from the node's resolv.conf. Despite its name, this is not the default.
ClusterFirstWithHostNetTo CoreDNS, for a Pod with hostNetwork: true. Without it, such a Pod falls back to Default.
NoneNowhere automatic. Everything comes from dnsConfig, which must then list at least one nameserver.
dnsConfig merges into what the policy generates
apiVersion: v1
kind: Pod
metadata:
  name: resolver
  namespace: nw-dnscfg
spec:
  dnsPolicy: ClusterFirst      # dnsPolicy and dnsConfig are immutable: change the manifest and
  dnsConfig:                   # recreate the Pod (kubectl replace --force -f), or edit a Deployment template
    searches:
      - corp.internal          # appended after the cluster search domains
    options:
      - name: ndots
        value: "2"             # value is a string
      - name: timeout
        value: "2"
  containers:
    - name: busybox
      image: busybox:1.36
      command: ["sleep", "3600"]

The Corefile

CoreDNS reads its settings from a file called the Corefile, stored in the coredns ConfigMap in kube-system. Each line inside the block turns on one plugin. Here is the one in this lab:

kubectl -n kube-system get cm coredns -o jsonpath='{.data.Corefile}'
.:53 {
    errors
    health {
       lameduck 5s
    }
    ready
    kubernetes cluster.local in-addr.arpa ip6.arpa {
       pods insecure
       fallthrough in-addr.arpa ip6.arpa
       ttl 30
    }
    prometheus :9153
    forward . /etc/resolv.conf {
       max_concurrent 1000
    }
    cache 30 {
       disable success cluster.local
       disable denial cluster.local
    }
    loop
    reload
    loadbalance
}

You do not need every line. These are the ones exam tasks touch:

  • kubernetes answers names in cluster.local, from the Services and Pods in the API.
  • forward . /etc/resolv.conf sends every other name to the DNS servers of the node CoreDNS runs on.
  • cache 30 keeps answers for up to 30 seconds. Here it skips cluster.local names, so only outside names can lag behind a change.
  • hosts is not there by default. It serves fixed name-to-IP entries. Always add fallthrough, so names it does not list still reach the next plugin.
  • reload rereads the Corefile when the ConfigMap changes. That can take a minute or two, so restart CoreDNS instead of waiting.

Every Pod in the cluster depends on this file, and one typo breaks DNS everywhere. So change it in four steps: back it up, edit it, restart CoreDNS, and check the logs.

Changing it safely
# Keep a copy first. Every Pod in the cluster depends on this file.
kubectl -n kube-system get cm coredns -o jsonpath='{.data.Corefile}' > /tmp/cka/Corefile.bak

kubectl -n kube-system edit cm coredns      # e.g. add, inside .:53 { … }:
#    hosts {
#       10.10.10.10 legacy-db.corp.internal
#       fallthrough
#    }

# Apply now instead of waiting for reload, then make sure both replicas come back
kubectl -n kube-system rollout restart deploy coredns
kubectl -n kube-system rollout status deploy coredns --timeout=120s
kubectl -n kube-system logs -l k8s-app=kube-dns --tail=20     # a syntax error shows up here

# Roll back if it went wrong
kubectl -n kube-system create cm coredns --from-file=Corefile=/tmp/cka/Corefile.bak --dry-run=client -o yaml | kubectl replace -f -
kubectl -n kube-system rollout restart deploy coredns

Running a lookup

To test DNS, run nslookup from a client Pod in the namespace you care about. The busybox:1.36 image has it.

Terminal
kubectl -n nw-dns run client --image=busybox:1.36 --restart=Never -- sleep 3600
kubectl -n nw-dns wait --for=condition=Ready pod/client --timeout=60s

kubectl -n nw-dns exec client -- nslookup inventory.nw-dns.svc.cluster.local   # one Service
kubectl -n nw-dns exec client -- nslookup inventory-pods.nw-dns.svc.cluster.local   # headless: one line per Pod
kubectl -n nw-dns exec client -- nslookup kubernetes.default.svc.cluster.local      # the API: always exists

When a lookup fails, the error message tells you which part is broken. Service and DNS failures walks through each case.

In the official docs· open on exam day too

Hands-on task 1 of 3nw-dns-1
~5 min4% weightNetworking

Resolve a Service and a headless Service

In namespace nw-dns, start a temporary Pod with image busybox:1.36. From it, resolve the fully qualified DNS name of the Service inventory and write the address the lookup returns to /tmp/cka/dns-inventory-ip.txt. Then resolve the fully qualified name of the headless Service inventory-pods and write every address it returns, one per line, to /tmp/cka/dns-inventory-pods.txt. Delete the temporary Pod when you are done.

Cluster:kubectl config use-context kind-cka

  1. 1

    Set up the scenario

    curl -fsSL https://codewithmuh.com/labs/cka/t/nw-dns-1/setup | bash
  2. 2

    Solve it in your terminal

    Use kubectl and the Kubernetes docs, like on exam day. Save any answer files under /tmp/cka/.

  3. 3

    Check your work

    curl -fsSL https://codewithmuh.com/labs/cka/t/nw-dns-1/verify | bash
    Got FAIL? Read which check failed, fix it, run it again.
  4. 4

    Clean up (optional)

    curl -fsSL https://codewithmuh.com/labs/cka/t/nw-dns-1/cleanup | bash
Hands-on task 2 of 3nw-dns-2
~4 min3% weightNetworking

Give a Pod an extra search domain

Create a Pod named resolver in namespace nw-dnscfg using image busybox:1.36 that runs sleep 3600. It must keep using cluster DNS. Add corp.internal as an additional search domain and set the resolver option ndots to 2. The Pod must be Running.

Cluster:kubectl config use-context kind-cka

  1. 1

    Set up the scenario

    curl -fsSL https://codewithmuh.com/labs/cka/t/nw-dns-2/setup | bash
  2. 2

    Solve it in your terminal

    Use kubectl and the Kubernetes docs, like on exam day. Save any answer files under /tmp/cka/.

  3. 3

    Check your work

    curl -fsSL https://codewithmuh.com/labs/cka/t/nw-dns-2/verify | bash
    Got FAIL? Read which check failed, fix it, run it again.
  4. 4

    Clean up (optional)

    curl -fsSL https://codewithmuh.com/labs/cka/t/nw-dns-2/cleanup | bash
Hands-on task 3 of 3nw-dns-3
~7 min6% weightNetworking

Serve a static record from CoreDNS

A legacy database has no DNS record. Configure the cluster DNS so that every Pod resolves legacy-db.corp.internal to 10.10.10.10. All existing names, including Services and names outside the cluster, must keep resolving. Confirm from Pod client in namespace nw-dnshosts.

Cluster:kubectl config use-context kind-cka

Setup breaks part of your lab on purpose, like the exam does. Run the clean-up step when you finish.
  1. 1

    Set up the scenario

    curl -fsSL https://codewithmuh.com/labs/cka/t/nw-dns-3/setup | bash
  2. 2

    Solve it in your terminal

    Use kubectl and the Kubernetes docs, like on exam day. Save any answer files under /tmp/cka/.

  3. 3

    Check your work

    curl -fsSL https://codewithmuh.com/labs/cka/t/nw-dns-3/verify | bash
    Got FAIL? Read which check failed, fix it, run it again.
  4. 4

    Clean up (optional)

    curl -fsSL https://codewithmuh.com/labs/cka/t/nw-dns-3/cleanup | bash

Finished this lesson?

0 of 3 tasks passed. Your readiness counts tasks, so run them before you move on.