Ingress DNS

DNS service for ingress controllers running on your minikube server

Overview

Problem

When running minikube locally, you may want to run your services on an ingress controller so that you don’t have to use minikube tunnel or NodePorts to access your services. While NodePort might be okay in a lot of circumstances, an ingress is necessary to test some features. Ingress controllers are great because you can define your entire architecture in something like a helm chart, and all your services will be available.

However, for minikube, there is an additional challenge. Your ingress controller relies on DNS, so local DNS names like myservice.test will have to resolve to your minikube ip. The only real way to do this is to add an entry for every service in your /etc/hosts file. This gets messy for obvious reasons. For each service you are running that each has its own DNS entry, you will need to configure it manually. Even if you automate it, you then need to rely on the host operating system for storing configurations instead of storing them in your cluster. To make it worse, these configurations have to be constantly maintained and updated as services are added, removed, and renamed. I call it the /etc/hosts pollution problem.

Solution

What if you could just access your local services magically without having to edit your /etc/hosts file? Well, now you can. The ingress-dns addon acts as a DNS service that runs inside your Kubernetes cluster. All you have to do is install the service and add the minikube ip as a DNS server on your host machine. Each time the DNS service is queried, an API call is made to the Kubernetes master service for a list of all the ingresses. If a match is found for the name, a response is given with an IP address matching minikube ip. For example, with a minikube ip of 192.168.99.106 and an ingress rule for myservice.test configured in the cluster, a DNS query from the host would produce:

  1. #bash:~$ nslookup myservice.test $(minikube ip)
  2. Server: 192.168.99.169
  3. Address: 192.168.99.169#53
  4. Non-authoritative answer:
  5. Name: myservice.test $(minikube ip)
  6. Address: 192.168.99.169

Installation

1Start minikube

  1. minikube start

2Enable the addons

  1. minikube addons enable ingress
  2. minikube addons enable ingress-dns

3Add the `minikube ip` as a DNS server

Linux OS with resolvconf

Update the file /etc/resolvconf/resolv.conf.d/base to have the following contents.

  1. search test
  2. nameserver 192.168.99.169
  3. timeout 5

Replace 192.168.99.169 with your minikube ip.

If your Linux OS uses systemctl, run the following commands.

  1. sudo resolvconf -u
  2. systemctl disable --now resolvconf.service

If your Linux OS does not use systemctl, run the following commands.

  1. # TODO add supporting docs for Linux OS that do not use `systemctl`

See https://linux.die.net/man/5/resolver

Linux OS with Network Manager

Network Manager can run integrated caching DNS server - dnsmasq plugin and can be configured to use separate nameservers per domain.

Edit /etc/NetworkManager/NetworkManager.conf and set dns=dnsmasq

  1. [main]
  2. dns=dnsmasq

Also see dns= in NetworkManager.conf.

Configure dnsmasq to handle .test domain

  1. sudo mkdir /etc/NetworkManager/dnsmasq.d/
  2. echo "server=/test/$(minikube ip)" >/etc/NetworkManager/dnsmasq.d/minikube.conf

Restart Network Manager

  1. systemctl restart NetworkManager.service

Ensure your /etc/resolv.conf contains only single nameserver

  1. cat /etc/resolv.conf | grep nameserver
  2. nameserver 127.0.0.1

Create a file in /etc/resolver/minikube-test with the following contents.

  1. domain test
  2. nameserver 192.168.99.169
  3. search_order 1
  4. timeout 5

Replace 192.168.99.169 with your minikube ip.

If you have multiple minikube IPs, you must configure a file for each.

See https://www.unix.com/man-page/opendarwin/5/resolver/

Note that the port feature does not work as documented.

Open Powershell as Administrator and execute the following.

  1. Add-DnsClientNrptRule -Namespace ".test" -NameServers "$(minikube ip)"

The following will remove any matching rules before creating a new one. This is useful for updating the minikube ip.

  1. Get-DnsClientNrptRule | Where-Object {$_.Namespace -eq '.test'} | Remove-DnsClientNrptRule -Force; Add-DnsClientNrptRule -Namespace ".test" -NameServers "$(minikube ip)"

4(optional) Configure in-cluster DNS server to resolve local DNS names inside cluster

Sometimes it’s useful to access other applications inside cluster via ingress and by their local DNS name - microservices/APIs/tests. In such case ingress-dns addon should be used by in-cluster DNS server - CoreDNS to resolve local DNS names.

Edit your CoreDNS config

  1. kubectl edit configmap coredns -n kube-system

and add block for your local domain

  1. test:53 {
  2. errors
  3. cache 30
  4. forward . 192.168.99.169
  5. }

Replace 192.168.99.169 with your minikube ip.

The final ConfigMap should look like:

  1. apiVersion: v1
  2. data:
  3. Corefile: |
  4. .:53 {
  5. errors
  6. health {
  7. lameduck 5s
  8. }
  9. ...
  10. }
  11. test:53 {
  12. errors
  13. cache 30
  14. forward . 192.168.99.169
  15. }
  16. kind: ConfigMap
  17. metadata:
  18. ...

See https://kubernetes.io/docs/tasks/administer-cluster/dns-custom-nameservers/

Testing

1Add the test ingress

  1. kubectl apply -f https://raw.githubusercontent.com/kubernetes/minikube/master/deploy/addons/ingress-dns/example/example.yaml

Note: Minimum Kubernetes version for the example ingress is 1.19

2Confirm that DNS queries are returning A records

  1. nslookup hello-john.test $(minikube ip)
  2. nslookup hello-jane.test $(minikube ip)

3Confirm that domain names are resolving on the host OS

  1. ping hello-john.test
  2. ping hello-jane.test

Expected results:

  1. PING hello-john.test (192.168.99.169): 56 data bytes
  2. 64 bytes from 192.168.99.169: icmp_seq=0 ttl=64 time=0.361 ms
  1. PING hello-jane.test (192.168.99.169): 56 data bytes
  2. 64 bytes from 192.168.99.169: icmp_seq=0 ttl=64 time=0.262 ms

4Curl the example server

  1. curl http://hello-john.test
  2. curl http://hello-jane.test

Expected results:

  1. Hello, world!
  2. Version: 1.0.0
  3. Hostname: hello-world-app-557ff7dbd8-64mtv
  1. Hello, world!
  2. Version: 1.0.0
  3. Hostname: hello-world-app-557ff7dbd8-64mtv

Known issues

.localhost domains will not resolve on chromium

.localhost domains will not correctly resolve on chromium since it is used as a loopback address. Instead use .test, .example, or .invalid

.local is a reserved TLD

Do not use .local as this is a reserved TLD for mDNS and bind9 DNS servers

Mac OS

mDNS reloading

Each time a file is created or a change is made to a file in /etc/resolver you may need to run the following to reload Mac OS mDNS resolver.

  1. sudo launchctl unload -w /System/Library/LaunchDaemons/com.apple.mDNSResponder.plist
  2. sudo launchctl load -w /System/Library/LaunchDaemons/com.apple.mDNSResponder.plist

TODO

  • Add a service that runs on the host OS which will update the files in /etc/resolver automatically
  • Start this service when running minikube addons enable ingress-dns and stop the service when running minikube addons disable ingress-dns

Contributors

Images used in this plugin

ImageSourceOwner
ingress-nginxingress-nginxKubernetes ingress-nginx
minikube-ingress-dnsminikube-ingress-dnsCryptex Labs

Last modified December 12, 2021: use ingress-dns addon inside cluster (8b2f0fb8b)