CoreDNS is not just a drop-in replacement for kube-dns; it’s a more flexible and extensible DNS server that can handle complex routing scenarios and custom DNS policies.
Let’s see CoreDNS in action. Imagine we have a Kubernetes cluster with a few deployments:
apiVersion: apps/v1
kind: Deployment
metadata:
name: frontend
spec:
replicas: 2
selector:
matchLabels:
app: frontend
template:
metadata:
labels:
app: frontend
spec:
containers:
- name: nginx
image: nginx:latest
ports:
- containerPort: 80
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: backend
spec:
replicas: 2
selector:
matchLabels:
app: backend
template:
metadata:
labels:
app: backend
spec:
containers:
- name: app
image: your-custom-backend-image # Replace with your actual backend image
ports:
- containerPort: 8080
And a Service for the backend:
apiVersion: v1
kind: Service
metadata:
name: backend-service
spec:
selector:
app: backend
ports:
- protocol: TCP
port: 80
targetPort: 8080
type: ClusterIP
Now, if we deploy a pod that needs to resolve backend-service.default.svc.cluster.local (assuming it’s in the default namespace), CoreDNS will handle this.
Inside a pod (e.g., a simple busybox pod for testing):
kubectl run -it --rm busybox --image=busybox -- /bin/sh
Once inside the pod, you’ll see /etc/resolv.conf populated with your CoreDNS cluster IP:
cat /etc/resolv.conf
nameserver 10.43.0.10 # This will be your CoreDNS ClusterIP
search default.svc.cluster.local svc.cluster.local cluster.local
options ndots:5
Now, let’s try to resolve the backend service:
nslookup backend-service.default.svc.cluster.local
You should get a response like:
Server: 10.43.0.10
Address 1: 10.43.0.10
Name: backend-service.default.svc.cluster.local
Address 1: 10.43.0.20 # This is the ClusterIP of backend-service
This demonstrates the basic functionality: resolving internal Kubernetes services.
CoreDNS’s power comes from its plugin architecture. Instead of a monolithic binary like kube-dns, CoreDNS is a Go program that loads plugins dynamically. Each plugin handles a specific DNS functionality. The Corefile is the central configuration for CoreDNS, dictating which plugins are loaded and how they behave.
Here’s a typical Corefile you might find in a Kubernetes cluster:
.:53 {
errors
health {
lameduck 5s
}
ready
kubernetes cluster.local in-addr.arpa ip6.arpa {
pods insecure
upstream
fallthrough in-addr.arpa ip6.arpa
}
prometheus :9153
forward . /etc/resolv.conf
cache 30
loop
reload
loadbalance
}
Let’s break down the key parts of this Corefile:
.:53: This is the "server block" and specifies that CoreDNS will listen on all interfaces (.) on port53(the standard DNS port).errors: This plugin handles logging of errors.health: Exposes a health check endpoint, typically on port8080. Thelameduck 5sdirective means it will wait 5 seconds before reporting unhealthy, allowing for graceful restarts.ready: Exposes a readiness probe endpoint, typically on port8080.kubernetes cluster.local in-addr.arpa ip6.arpa: This is the core plugin for Kubernetes. It handles DNS queries for services and pods within thecluster.localdomain.pods insecure: Allows CoreDNS to resolve pod IPs directly without needing to verify ownership (useful for certain scenarios).upstream: If a query doesn’t match Kubernetes services or pods, it forwards the query to an upstream DNS server (defined by/etc/resolv.confin this example).fallthrough in-addr.arpa ip6.arpa: For reverse DNS lookups (e.g.,X.Y.Z.W.in-addr.arpa), if thekubernetesplugin can’t resolve it, it passes it to the next plugin.
prometheus :9153: Exposes Prometheus metrics on port9153, which is invaluable for monitoring.forward . /etc/resolv.conf: This is crucial for external DNS resolution. It forwards all queries (.) that are not handled by the preceding plugins to the DNS servers listed in the node’s/etc/resolv.conf.cache 30: Caches DNS responses for 30 seconds, reducing load on upstream servers and improving lookup speed.loop: Detects and prevents DNS forwarding loops.reload: Allows theCorefileto be reloaded without restarting the CoreDNS pods.loadbalance: Distributes load across multiple CoreDNS instances if you have more than one replica running.
The kubernetes plugin is what makes CoreDNS understand your cluster’s services, endpoints, and pods. When a DNS query arrives, CoreDNS processes it through the plugins listed in the Corefile in order. If the kubernetes plugin can resolve the query (e.g., it’s a service name), it returns the answer. If not, it might fallthrough to the next plugin. In this configuration, forward . /etc/resolv.conf acts as the ultimate fallback for any external domains.
One of the most powerful, yet often overlooked, aspects of CoreDNS is its ability to define custom DNS policies using the kubernetes plugin’s acl directive or by creating entirely new server blocks. For instance, you could have a separate server block that handles DNS for a specific subdomain, routing it to a different upstream resolver or applying unique caching rules.
Consider this Corefile snippet:
example.com:53 {
errors
cache 60
forward . 8.8.8.8
}
.:53 {
# ... your default k8s config ...
}
Here, any query for example.com will be handled by the first block, using Google’s DNS (8.8.8.8) and caching for 60 seconds, completely bypassing the default Kubernetes DNS configuration for that domain.
The next concept you’ll likely encounter is implementing custom DNS policies for specific namespaces or domains, potentially involving external DNS servers or even federating DNS across multiple Kubernetes clusters.