In a previous article about the
docker run command, one of my final thoughts was the following:
In this article, we covered quite a few options for the
docker runcommand. However, while these options are key, they only scratch the surface of the available options to
docker run. Some of the options are complex enough to deserve an article unto themselves.
In today's article, we are going to do just that: explore a single
docker run command option. Specifically, we will explore an option that allows us to change DNS within a container.
Today, we will explore the
What Does Add-Host Do?
--add-host flag is used to add a single host to IP mapping within a Docker container. This flag can be useful when you need to connect a service within a container to an external host.
To get an understanding of how this works, let's use the
--add-host flag to map
We will be testing with a container named curl. This container has the
curl command preinstalled and set as the entrypoint. With this container, we can use the
curl command to connect to our test host.
Before we start adding the
--add-host flag, let's see what happens if we use this curl container to connect to the
$ docker run --rm=True curl -I testing.example.com curl: (6) Could not resolve host: testing.example.com
We can see from the above, the
curl command returned an error
Could not resolve host. This is the typical error we would see when trying to reach a domain that does not have a DNS entry.
This example shows us what happens without any
--add-host flags added. Let's see what happens when we use this flag to map
$ docker run --rm=True --add-host=testing.example.com:10.0.0.1 curl -I testing.example.com HTTP/1.1 200 OK Server: nginx Date: Fri, 17 Nov 2017 14:01:20 GMT Content-Type: text/html Content-Length: 97652 Last-Modified: Sun, 16 Jul 2017 11:29:52 GMT Connection: keep-alive Vary: Accept-Encoding ETag: "596b4e30-17d74" Accept-Ranges: bytes
Great! Now when our container reaches out to
testing.example.com, it is able to connect to our example host. This means our internal DNS resolution works; but how exactly does it work?
Understanding How Add-Host Works
To understand how the
--add-host flag works, let's spin up a simple Ubuntu container and take a look around. We can do this by using the
-i (Interactive) and
-t (Pseudo-TTY) flags.
These flags will start the container in a mode that allows us to interact with the running process. The process in this case will be
/bin/bash; this gives us a shell within the container. With this shell, we can explore within the container itself.
$ docker run --rm=True --add-host=testing.example.com:10.0.0.1 -it ubuntu /bin/bash root@a87b1beb2eee:/#
With our shell startup successful, let's start looking at this container's configuration files. Specifically, let's look at the configurations that drive DNS resolution. The first file we will look at is the
nsswitch.conf file in Unix and Linux is used to define where and how the system will perform name service-related lookups. The specific setting we are interested in is the
hosts setting. The
hosts setting is used to define the sources and order used for hostname resolution.
root@a87b1beb2eee:/# cat /etc/nsswitch.conf # /etc/nsswitch.conf # # Example configuration of GNU Name Service Switch functionality. # If you have the `glibc-doc-reference' and `info' packages installed, try: # `info libc "Name Service Switch"' for information about this file. passwd: compat group: compat shadow: compat gshadow: files hosts: files dns networks: files protocols: db files services: db files ethers: db files rpc: db files netgroup: nis
In the above, we can see that the
hosts setting is set to
files dns. This setting means that for hostname resolution, the system will first consult files, then dns.
When our system is performing a DNS lookup, it will first reference local configuration files, such as
/etc/hosts. If the host is not found within
/etc/hosts, the system will then query the name servers defined within the
/etc/resolv.conf configuration file.
Something important to remember is that this methodology is the standard Linux setup. These steps are common for most Linux systems, not just containers.
What does this mean for the
--add-host flag? Well, it means that there are only two places Docker could change DNS resolution: either the
/etc/hosts file or the name servers referenced in
The most likely candidate is the
root@a87b1beb2eee:/# cat /etc/hosts 127.0.0.1 localhost ::1 localhost ip6-localhost ip6-loopback fe00::0 ip6-localnet ff00::0 ip6-mcastprefix ff02::1 ip6-allnodes ff02::2 ip6-allrouters 10.0.0.1 testing.example.com 172.17.0.3 a87b1beb2eee
If we look at the
/etc/hosts file, we can see an interesting entry.
It appears that the
--add-host flag places an entry into the
/etc/hosts file. What is interesting about this is that editing the
/etc/hosts file is a time-honored sysadmin trick. It has been used for many years to perform just this task: taking an unresolvable hostname and mapping it to a static IP.
!Sign up for a free Codeship Account
Using Add-Host to Override DNS Resolution
Another common use of the
/etc/hosts file is overriding normal DNS. Since
files is listed first within the
/etc/nsswitch.conf configuration file, the
/etc/hosts file is consulted before any traditional DNS name servers.
This means that it is possible to place an entry into the
/etc/hosts file that overrides an existing name. To experiment a little, let's do this with Google's domain.
To start our experiment, lLet's take a look at what normally happens when we use our curl container to connect to
$ docker run --rm=True curl -I google.com HTTP/1.1 301 Moved Permanently Location: http://www.google.com/ Content-Type: text/html; charset=UTF-8 Date: Fri, 17 Nov 2017 14:04:43 GMT Expires: Sun, 17 Dec 2017 14:04:43 GMT Cache-Control: public, max-age=2592000 Server: gws Content-Length: 219 X-XSS-Protection: 1; mode=block X-Frame-Options: SAMEORIGIN
From the above, we can see a pretty typical response from
google.com. Now, let's see what happens if we use the
--add-host flag to map
$ docker run --rm=True --add-host=google.com:10.0.0.1 curl -I google.com HTTP/1.1 200 OK Server: nginx Date: Fri, 17 Nov 2017 14:05:52 GMT Content-Type: text/html Content-Length: 97652 Last-Modified: Sun, 16 Jul 2017 11:29:52 GMT Connection: keep-alive Vary: Accept-Encoding ETag: "596b4e30-17d74" Accept-Ranges: bytes
From the above, we can see that our
--add-host addition resulted in our
curl command to connect to our example host. What this shows is that the
--add-host flag can be used for more than mapping an unresolved hostname. It can be used to override traditional DNS as well.
This can be a useful method to force a container to connect to a specific host address.
Adding Multiple Entries
Another nice feature of this flag is that it can be specified multiple times.
$ docker run --rm=True --add-host=1.example.com:10.0.0.1 --add-host=2.example2.com:10.0.0.2 --add-host=3.example.com:10.0.0.3 ubuntu cat /etc/hosts 127.0.0.1 localhost ::1 localhost ip6-localhost ip6-loopback fe00::0 ip6-localnet ff00::0 ip6-mcastprefix ff02::1 ip6-allnodes ff02::2 ip6-allrouters 10.0.0.1 1.example.com 10.0.0.2 2.example.com 10.0.0.3 3.example.com 172.17.0.3 17796a2cb640
In the above example, we can see that specifying the
--add-host multiple times resulted in multiple entries within the
/etc/hosts file. This is true whether multiple hosts are specified (as shown) or if the same host is specified.
This is useful in cases where you may want to specify multiple manual host to IP mappings.
In this article, we explored the
--add-host flag available with the
docker run command. We also learned that Docker uses a time-honored sysadmin trick for the heavy lifting behind the
At this point though, you might be thinking, "This flag is interesting but how can it be used in the real world?"
When creating an application that uses another service, like Redis for example, I tend to have my application configured to reach out to the host of
redis. I then link a Redis container with that name to my application container. This means my container's configuration is simple -- it just references
redis. This works great when I can simply link the Redis container to my application container.
But what happens when the Redis instance can't be "linked" or is even outside of your control? We would then have to configure the application to reach out to a specific host, somewhat complicating the application's configuration.
--add-host flag can be a simple way to keep a simplistic application configuration while still enabling the application to connect to the remote instance.