DNS
DNS plays a central role in Kubernetes service discovery. As it is mentioned in the Services chapter, DNS is an essential part of how Services are consumed by end clients and, while implementation is not baked into core Kubernetes controllers, [DNS specification] is very explicit about the behaviour expected from such an implementation. The DNS spec defines the rules for the format of the queries and the expected responses. All Kubernetes Services have at least one corresponding A/AAAA DNS record in the format of {service-name}.{namespace}.svc.{cluster-domain} and the response format depends on the type of a Service:
| Service Type | Response |
|---|---|
| ClusterIP, NodePort, LoadBalancer | ClusterIP value |
| Headless | List of Endpoint IPs |
| ExternalName | CNAME pointing to the value of spec.externalName |
Note
Some Services have additional SRV and PTR records and Pods also have a corresponding A/AAAA record; see the official docs for more details.
Historically, there had been two implementations of this DNS spec – one based on dnsmasq and another one based on CoreDNS, the latter had become the default option for kubeadm since Kubernetes 1.11.
Service Discovery – Server-side
CoreDNS implements the Kubernetes DNS spec in a dedicated plugin that gets compiled into a static binary and deployed in a Kubernetes cluster as a Deployment and exposed as a ClusterIP service. This means that all communications with the DNS service inside a cluster are subject to the same network forwarding rules used and limitations experienced by normal Pods and set up by the CNI and Services plugins.
Since DNS speed and stability are considered crucial in any network-based communication, CoreDNS implementation is highly optimised to minimise memory consumption and maximise query processing rate. In order to achieve that, CoreDNS stores only the relevant parts of Services, Pods and Endpoints objects in its local cache that is optimised to return a response in a single lookup.
By default, CoreDNS also acts as a DNS proxy for all external domains (e.g. example.com) using the forward plugin and is often deployed with the cache plugin enabled. The entire CoreDNS configuration can be found in the coredns ConfigMap:
Service Discovery – Client-side
DNS configuration inside a Pod is controlled by the spec.dnsPolicy and spec.dnsConfig settings. By default, kubelet will configure the cluster DNS IP, stored in the configuration file and hard-coded to the tenth IP of the ClusterIP range by the kubeadm.
With the above default settings, this is how a Pod deployed in the default namespace would see its own resolv.conf file:
The search domains and ndots value are configured so that any non-FQDN DNS query made by a Pod is first tried in all of the specified domains, which allows for internal cluster DNS schema to take precedence over the external DNS (explanation). For example, any Pod in the default Namespace, can lookup the ClusterIP of the kubernetes Service in a single lookup (the shell is running a stern -n kube-system -l k8s-app=kube-dns in the background):
The downside of this behaviour is that any external domain lookup will require at least 4 separate queries:
Optimisations
DNS is widely regarded as the main source of all IT problems, and Kubernetes is no exception (see 1, 2, 3, 4). Its way of deployment and reliance on HPA mean that some Nodes could become connection bottlenecks while the CPU and Memory of the DNS Pods may remain relatively low. There are a number of optimisations that can be enabled to improve DNS performance at the expense of additional resource utilisation and complexity:
- The authopath plugin can be enabled in CoreDNS to make it follow the chain of search paths on behalf of a client, thereby reducing the number of queries for an external domain required by the client from 4 (see above) to just one.
- Each Kubernetes Node can run a NodeLocal DNSCache – a daemonset of recursive DNS resolvers designed to reduce the load on a centralised CoreDNS deployment by serving as a caching layer between Pods and the DNS service.
- Adjust lookups that are used frequently to add a trailing dot (example,
tkng.io.). This is most effective for external hostnames that experience overhead as an absolute query is performed, avoiding the search path expansion through thecluster.local, and potentially other search domains in/etc/resolv.conf.
External DNS
The DNS Specification is only focused on the intra-cluster DNS resolution and service discovery. Anything to do with external DNS is left out of scope, despite the fact that most of the end-users are located outside of a cluster. For them, Kubernetes has to provide a way to discover external Kubernetes resources, LoadBalancer Services, Ingresses and Gateways, and there are two ways this can be accomplished:
- An out-of-cluster DNS zone can be orchestrated by the ExternalDNS cluster add-on – a Kubernetes controller that synchronises external Kubernetes resources with any supported third-party DNS provider via an API (see the GH page for the list of supported providers).
- An existing DNZ zone can be configured to delegate a subdomain to a self-hosted external DNS plugin, e.g. k8s_gateway. This approach assumes that this DNS plugin is deployed inside a cluster and exposed via a LoadBalancer IP, which is then used in an NS record for the delegated zone. All queries hitting this subdomain will get forwarded to this plugin which will respond as an authoritative nameserver for the delegated subdomain.