CoreDNS is now the default DNS server in Kubernetes, and integrating it with a service mesh like Istio or Linkerd is a common task. Here’s how to get it done.

Let’s see CoreDNS in action, resolving a service within a mesh. Imagine you have a frontend service and a backend service, both managed by your service mesh.

# From a pod within the mesh
kubectl exec -it <your-pod-name> -- nslookup frontend.default.svc.cluster.local

You’d expect to see an IP address that’s not necessarily the backend service’s ClusterIP, but rather an IP managed by the service mesh’s ingress gateway or a specific sidecar proxy.

The Problem CoreDNS Solves in a Service Mesh

In a service mesh, pods have their own IP addresses, and direct communication between them is often preferred. However, applications are built assuming DNS resolution. The service mesh needs to intercept DNS requests for services within the mesh and return IPs that route traffic correctly through the mesh’s proxies (sidecars). CoreDNS, being the cluster’s DNS, is the natural place to configure this interception.

How CoreDNS Integrates

CoreDNS works by processing a Corefile. This file contains a list of "zones" and "plugins" that dictate how DNS queries are handled. For service mesh integration, we’re primarily concerned with two plugins:

  1. kubernetes plugin: This is the default plugin that CoreDNS uses to resolve Kubernetes service and pod names. It queries the Kubernetes API for service endpoints.
  2. hosts plugin: This plugin allows you to define static DNS mappings. This is where we’ll often override or add entries for mesh-specific resolutions.

The Corefile is typically managed by a ConfigMap in Kubernetes, which CoreDNS then loads.

Configuring the Corefile for Service Mesh

The exact configuration depends on your service mesh, but the general idea is to modify the Corefile to handle internal service names.

Scenario: Istio

Istio typically injects its own DNS proxy (istio-coredns) or configures the existing CoreDNS to forward requests for services within the mesh to the Istio ingress gateway or a dedicated DNS service.

Here’s a simplified example of what your Corefile might look like if you’re using Istio and want CoreDNS to directly handle service resolution:

.:53 {
    errors
    health {
       lameduck 5s
    }
    ready
    kubernetes cluster.local in-addr.arpa ip6.arpa {
       pods insecure
       fallthrough in-addr.arpa ip6.arpa
    }
    prometheus :9153
    # This is where you'd add custom forwarding or host entries if needed
    # For Istio, the kubernetes plugin might be sufficient, or you might
    # forward to istio-ingressgateway:port
    forward . /etc/resolv.conf
    cache 30
    loop
    reload
    loadbalance
}

In many Istio deployments, the Istio operator modifies the CoreDNS ConfigMap automatically. If you’re managing it manually, you’d ensure the kubernetes plugin is correctly configured for your cluster.local domain. Istio often relies on the kubernetes plugin to discover service IPs and then the Istio sidecars handle the routing.

Scenario: Linkerd

Linkerd often uses its own linkerd-dns component, which might run as a sidecar or a separate deployment. However, if you want CoreDNS to be the central point, you can configure it to forward requests for your cluster’s domain to Linkerd’s DNS service.

A common approach is to use the forward plugin in CoreDNS.

.:53 {
    errors
    health {
       lameduck 5s
    }
    ready
    kubernetes cluster.local in-addr.arpa ip6.arpa {
       pods insecure
       fallthrough in-addr.arpa ip6.arpa
    }
    prometheus :9153
    # Forward requests for services in the mesh to Linkerd's DNS
    # Assuming linkerd-dns runs on port 8600 in the linkerd namespace
    forward cluster.local {
        to linkerd-dns.linkerd.svc.cluster.local:8600
    }
    cache 30
    loop
    reload
    loadbalance
}

Here, linkerd-dns.linkerd.svc.cluster.local:8600 is the address where Linkerd’s DNS service is listening. CoreDNS will send any query for *.cluster.local to this address.

Key Levers You Control

  • Corefile Content: This is the primary configuration. You dictate which plugins are active, how they’re ordered, and their specific parameters.
  • Plugin Order: The order of plugins matters. A kubernetes plugin before a forward plugin means Kubernetes services are resolved first.
  • forward Destination: When using forward, you specify the IP address and port of the DNS server that should handle the forwarded queries. This is crucial for pointing to your service mesh’s DNS component.
  • kubernetes Plugin Configuration: Options like pods insecure or fallthrough affect how CoreDNS interacts with the Kubernetes API and handles internal IP resolutions.

The Mechanics of Interception

When a pod makes a DNS query (e.g., curl backend), the request first hits the CoreDNS service IP. CoreDNS processes its Corefile. If the kubernetes plugin is configured and the service backend.default.svc.cluster.local exists, it will query the Kubernetes API for its ClusterIP. If you’ve configured a forward rule for cluster.local to point to your service mesh’s DNS, CoreDNS will delegate that resolution there. The service mesh’s DNS then returns an IP that the mesh’s sidecar proxies understand, ensuring traffic is routed through the mesh.

This setup ensures that even though your application code uses standard DNS, the service mesh can intercept and control the traffic flow invisibly.

The next challenge is understanding how to configure custom DNS entries or override existing ones within the service mesh’s DNS resolver itself, beyond what CoreDNS directly manages.

Want structured learning?

Take the full Coredns course →