Configuring links for name and service resolution

Container linking provides a means for one container to easily communicate with another container on the same host. As we’ve seen in previous examples, most container-to-container communication has occurred through IP addresses. Container linking improves on this by allowing linked containers to communicate with each other by name. In addition to providing basic name resolution, it also provides a means to see what services a linked container is providing. In this recipe, we’ll review how to create container links as well as discuss some of their limitations.

Getting ready

In this recipe, we’ll be demonstrating the configuration on a single Docker host. It is assumed that this host has Docker installed and that Docker is in its default configuration. We’ll be altering name resolution settings on the host, so you’ll need root-level access.

How to do it…

The phrase container linking might imply to some that it involves some kind of network configuration or modification. In reality, container linking has very little to do with container networking. In the default mode, container linking provides a means for one container to resolve the name of another. For instance, let’s start two containers on our lab host docker1:

root@docker1:~# docker run -d -P --name=web1 jonlangemak/web_server_1
88f9c862966874247c8e2ba90c18ac673828b5faac93ff08090adc070f6d2922 root@docker1:~# docker run -d -P --name=web2 --link=web1 
jonlangemak/web_server_2
00066ea46367c07fc73f73bdcdff043bd4c2ac1d898f4354020cbcfefd408449
root@docker1:~#

Notice how, when I started the second container, I used a new flag named --link and referenced the container web1. We would now say that web2 was linked to web1. However, they’re not really linked in any sort of way. A better description might be to say that web2 is now aware of web1. Let’s connect to the container web2 to show you what I mean:

root@docker1:~# docker exec -it web2 /bin/bash
root@00066ea46367:/# ping web1 -c 2
PING web1 (172.17.0.2): 48 data bytes
56 bytes from 172.17.0.2: icmp_seq=0 ttl=64 time=0.163 ms
56 bytes from 172.17.0.2: icmp_seq=1 ttl=64 time=0.092 ms
--- web1 ping statistics ---
2 packets transmitted, 2 packets received, 0% packet loss
round-trip min/avg/max/stddev = 0.092/0.128/0.163/0.036 ms
root@00066ea46367:/#

It appears that the web2 container is now able to resolve the container web1 by name. This is because the linking process inserted records into the web2 container's hosts file:

root@00066ea46367:/# more /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
172.17.0.2      web1 88f9c8629668
172.17.0.3      00066ea46367
root@00066ea46367:/#

With this configuration, the web2 container can reach the web1 container either by the name we gave the container at runtime (web1) or the unique hostname Docker generated for the container (88f9c8629668).

In addition to the hosts file being updated, web2 also generates some new environmental variables:

root@00066ea46367:/# printenv
WEB1_ENV_APACHE_LOG_DIR=/var/log/apache2
HOSTNAME=00066ea46367
APACHE_RUN_USER=www-data
WEB1_PORT_80_TCP=tcp://172.17.0.2:80
WEB1_PORT_80_TCP_PORT=80
LS_COLORS=
WEB1_PORT=tcp://172.17.0.2:80
WEB1_ENV_APACHE_RUN_GROUP=www-data
APACHE_LOG_DIR=/var/log/apache2
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
PWD=/
WEB1_PORT_80_TCP_PROTO=tcp
APACHE_RUN_GROUP=www-data
SHLVL=1
HOME=/root
WEB1_PORT_80_TCP_ADDR=172.17.0.2
WEB1_ENV_APACHE_RUN_USER=www-data
WEB1_NAME=/web2/web1
_=/usr/bin/printenv
root@00066ea46367:/# 

You’ll notice many new environmental variables. Docker will copy any environmental variables from the linked container that were defined as part of the container. This includes:

  • Environmental variables described in the Docker image. More specifically, any ENV variables from the images Dockerfile
  • Environmental variables passed to the container at runtime through the --env or -e flag

In this case, these three variables were defined as ENV variables in the image's Dockerfile:

APACHE_RUN_USER=www-data
APACHE_RUN_GROUP=www-data
APACHE_LOG_DIR=/var/log/apache2

Because both container images have the same ENV variables defined, we’ll see the local variables as well as the same environmental variables from the container web1 prefixed with WEB1_ENV_:

WEB1_ENV_APACHE_RUN_USER=www-data
WEB1_ENV_APACHE_RUN_GROUP=www-data
WEB1_ENV_APACHE_LOG_DIR=/var/log/apache2

In addition, Docker also created six other environmental variables that describe the web1 container as well as any of its exposed ports:

WEB1_PORT=tcp://172.17.0.2:80
WEB1_PORT_80_TCP=tcp://172.17.0.2:80
WEB1_PORT_80_TCP_ADDR=172.17.0.2
WEB1_PORT_80_TCP_PORT=80
WEB1_PORT_80_TCP_PROTO=tcp
WEB1_NAME=/web2/web1

Linking also allows you to specify aliases. For instance, let’s stop, remove, and respawn container web2 using a slightly different syntax for linking:

user@docker1:~$ docker stop web2
web2
user@docker1:~$ docker rm web2
web2
user@docker1:~$ docker run -d -P --name=web2 --link=web1:webserver 
jonlangemak/web_server_2
e102fe52f8a08a02b01329605dcada3005208d9d63acea257b8d99b3ef78e71b
user@docker1:~$

Notice that, after the link definition, we inserted a :webserver. The name after the colon represents the alias for the link. In this case, I’ve specified an alias for the container web1 as webserver.

If we examine the web2 container, we’ll see that the alias is now also listed in the hosts file:

root@c258c7a0884d:/# more /etc/hosts
…<Additional output removed for brevity>… 
172.17.0.2      webserver 88f9c8629668 web1
172.17.0.3      c258c7a0884d
root@c258c7a0884d:/# 

Aliases also impact the environmental variables created during linking. Rather than using the container name, they’ll instead use the alias:

user@docker1:~$ docker exec web2 printenv
…<Additional output removed for brevity>… 
WEBSERVER_PORT_80_TCP_ADDR=172.17.0.2
WEBSERVER_PORT_80_TCP_PORT=80
WEBSERVER_PORT_80_TCP_PROTO=tcp
…<Additional output removed for brevity>… 
user@docker1:~$

At this point, you might be wondering how dynamic this is. After all, Docker is providing this functionality by updating static files in each container. What happens if a container’s IP address changes? For instance, let’s stop the container web1 and start a new container named web3 using the same image:

user@docker1:~$ docker stop web1
web1
user@docker1:~$ docker run -d -P --name=web3 jonlangemak/web_server_1
69fa80be8b113a079e19ca05c8be9e18eec97b7bbb871b700da4482770482715
user@docker1:~$

If you’ll recall from earlier, the container web1 had an IP address of 172.17.0.2 allocated to it. Since I stopped the container, Docker will release that IP address reservation making it available to be reassigned to the next container we start. Let’s check the IP address assigned to the container web3:

user@docker1:~$ docker exec web3 ip addr show dev eth0
79: eth0@if80: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP
    link/ether 02:42:ac:11:00:02 brd ff:ff:ff:ff:ff:ff
    inet 172.17.0.2/16 scope global eth0
       valid_lft forever preferred_lft forever
    inet6 fe80::42:acff:fe11:2/64 scope link
       valid_lft forever preferred_lft forever
user@docker1:~$

As expected, web3 took the now open IP address of 172.17.0.2 that previously belonged to the web1 container. We can also verify that the container web2 still believes that this IP address belongs to the web1 container:

user@docker1:~$ docker exec –t web2 more /etc/hosts | grep 172.17.0.2
172.17.0.2      webserver 88f9c8629668 web1
user@docker1:~$

If we start the container web1 once again, we should see that it will get a new IP address allocated to it:

user@docker1:~$ docker start web1
web1
user@docker1:~$ docker exec web1 ip addr show dev eth0
81: eth0@if82: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP
    link/ether 02:42:ac:11:00:04 brd ff:ff:ff:ff:ff:ff
    inet 172.17.0.4/16 scope global eth0
       valid_lft forever preferred_lft forever
    inet6 fe80::42:acff:fe11:4/64 scope link
       valid_lft forever preferred_lft forever
user@docker1:~$

If we check the container web2 again, we should see that Docker has updated it to reference the web1 container’s new IP address:

user@docker1:~$ docker exec web2 more /etc/hosts | grep web1
172.17.0.4      webserver 88f9c8629668 web1
user@docker1:~$

However, while Docker takes care of updating the hosts file with the new IP address, it will not take care of updating any of the environmental variables to reflect the new IP address:

user@docker1:~$ docker exec web2 printenv
…<Additional output removed for brevity>…
WEBSERVER_PORT=tcp://172.17.0.2:80
WEBSERVER_PORT_80_TCP=tcp://172.17.0.2:80
WEBSERVER_PORT_80_TCP_ADDR=172.17.0.2
…<Additional output removed for brevity>…
user@docker1:~$

In addition, it should be pointed out that the link is only one way. That is, this link does not cause the container web1 to become aware of the web2 container. Web1 will not receive the host records or the environmental variables referencing the web2 container:

user@docker1:~$ docker exec -it web1 ping web2
ping: unknown host
user@docker1:~$

Another reason to provision links is when you use Docker Inter-Container Connectivity (ICC) mode set to false. As we’ve discussed previously, ICC prevents any containers on the same bridge from talking directly to each other. This forces them to talk to each other only though published ports. Linking provides a mechanism to override the default ICC rules. To demonstrate, let’s stop and remove all the containers on our host docker1 and then add the following Docker option to the systemd drop-in file:

ExecStart=/usr/bin/dockerd --icc=false

Now reload the systemd configuration, restart the service, and start the following containers:

docker run -d -P --name=web1 jonlangemak/web_server_1
docker run -d -P --name=web2 jonlangemak/web_server_2

With ICC mode on, you’ll notice that containers can’t talk directly to each other:

user@docker1:~$ docker exec web1 ip addr show dev eth0
87: eth0@if88: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP
    link/ether 02:42:ac:11:00:02 brd ff:ff:ff:ff:ff:ff
    inet 172.17.0.2/16 scope global eth0
       valid_lft forever preferred_lft forever
    inet6 fe80::42:acff:fe11:2/64 scope link
       valid_lft forever preferred_lft forever
user@docker1:~$ docker exec -it web2 curl http://172.17.0.2
user@docker1:~$

In the preceding example, web2 is not able to access the web servers on web1. Now, let’s delete and recreate the web2 container, this time linking it to web1:

user@docker1:~$ docker stop web2
web2
user@docker1:~$ docker rm web2
web2
user@docker1:~$ docker run -d -P --name=web2 --link=web1 
jonlangemak/web_server_2
4c77916bb08dfc586105cee7ae328c30828e25fcec1df55f8adba8545cbb2d30
user@docker1:~$ docker exec -it web2 curl http://172.17.0.2
<body>
  <html>
    <h1><span style=”color:#FF0000;font-size:72px;”>Web Server #1 - Running on port 80</span>
    </h1>
</body>
  </html>
user@docker1:~$

We can see that, with the link in place, the communication is allowed as expected. Once again, just like the link, this access is allowed in one direction.

It should be noted that linking works differently when using user-defined networks. In this recipe, we covered what are now being named legacy links. Linking with user-defined networks will be covered in the next two recipes.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset