In addition to the standard format of expressing IP addresses with numbers and dots, symbolic names can also refer to a host. Symbolic names are preferred as they are easier to remember. A service like Domain Name System (DNS) converts domain names into IP addresses using a database that maintains the mapping between host names (or domain names) and host numbers (or IP addresses).
In the Linux environment, name resolution is described in the /etc/nsswitch.conf
file, where the default entry for hosts includes files and DNS. It means that the system will check /etc/hosts
file before requesting the DNS server for name resolution.
Docker provides a DNS system that can route service requests using their service names. However, a Fully Qualified Domain Name (FQDN) like nginx.service.com
cannot be used within Docker. Instead, we'll need to use the container's name to connect between different services.
Let's get into it!
Pre-Requisites
To follow along with this blog, you'll need the following:
- Linux System with Docker Pre-Installed
- Docker-Compose
First, let's set up our DNS Server. We will use BIND 9 for the DNS Server, provided free by the Internet Consortium. It's available as a Docker image at DockerHub.
Setup Docker Network
Let's create a docker network.
docker network create --name lab-net --subnet 172.24.0.0/16
For our network, we're using a /16
subnet.
DNS Server Configuration
- First, setup a directory to store our BIND 9 configuration and create a new file called
named.conf.options
to start configuring the BIND 9 server.
mkdir -p /opt/bind9/configuration
vim /opt/bind9/configuration/named.conf.options
2. Copy the contents below into the file.
This ensures that BIND listens on all interfaces for DNS requests, and any domain name requests that cannot be fulfilled by the BIND server (ones outside of the zone file) will be sent to the forwarders for resolution. For example, google.com
, docker.com
, etc.
3. Next, we'll define a Zone called lab-net.lan
, which points to /etc/bind/zones/db.
Zone File.
Let's create the file that will define our zone.
vim /opt/bind9/configuration/named.conf.local
Copy the contents below into the file.
The Zone File called db.lab-net.lan
contains the domain names that will be managed by our BIND DNS server. We will assign each domain name an IP address. Mention the container's IP address during container runtime so the domain names can point to the corresponding IP addresses.
We'll now add a few domain names to our zone file. Create the lab-net
zone file.
vim /opt/bind9/configuration/db.lab-net.lan
4. Copy the contents below into the zone file.
In the above example, one name server ns1.lab-net.lan
, and two hosts nginx.lab-net.lan
and httpd.lab-net.lan
have been added.
Also Read: A Deep Dive Into Kubernetes Pods
Build the Docker Image
We'll use the official Docker Image of BIND 9 as a base image and install some additional dependencies into it. Finally, we'll copy our configuration files directly into the Docker image.
- Create the
Dockerfile
.
vim /opt/bind9/Dockerfile.bind9
2. Copy the contents below into the Dockerfile
.
3. Build and tag the BIND image using the command below.
docker build -t dns-master . -f Dockerfile.bind9
Run the Docker Container
Once our container has been successfully built, we will now run it in our lab-net
network with an explicitly mentioned IP address.
docker run -d -p 53:53/tcp -p 53:53/udp -p 127.0.0.1:953:953/tcp --rm --name=dns-master --net=lab-net --ip=172.24.0.2 internetsystemsconsortium/bind9:9.18
Verifying server configuration:
Also Read: 9 Essential Tips for Writing Engaging Tech Blogs
Demo Setup
We can now use the previously started DNS container as our DNS server. Any services we need domain mapping for can simply be rebuilt with the required domain names.
docker run -d --rm --name=nginx --net=lab-net --ip=172.24.0.3 --dns=172.24.0.2 nginx:latest
sudo docker run -d --rm --name=httpd --net=lab-net --ip=172.24.0.4 --dns=172.24.0.2 httpd:latest
As you can see, all containers now run on the same network.
docker network inspect lab-net
We can test whether our domain name mapping functions properly by executing an nslookup
command. Using the nslookup
command, we will be able to determine the IP address of a domain name and the DNS server supplying the details to us.
To check if the forwarder is working, we can perform nslookup
on a domain like google.com
, which exists outside of our internal DNS zone. If our request is successful, we should be able to see the IP address of Google's domain.
As you can see, the reply that came back has the string Non-authorative
. This means the DNS server that supplied us with domain details google.com
is not under our control. We got this outcome because we added forwarders in our BIND configuration. The domain name we requested detail for was not available in the local zone file, so its details could not be supplied by the BIND server.
Conclusion
This way, we can set up a local DNS server in Docker.
The benefits of setting up a local DNS server are endless. It helps a lot when you don't have access to domain names and want to test different technologies that rely somehow on a domain name.
Please comment below if you have any queries. I try to update my articles regularly to maintain legibility!