In this chapter, we will be taking a look at Docker Compose. We will break the chapter down into the following sections:
One of the prominent features of the Docker technology is linking containers. That is, cooperating containers can be linked together to offer complex and business-aware services. The linked containers have a kind of source-recipient relationship, wherein the source container gets linked to the recipient container, and the recipient securely receives a variety of information from the source container. However, the source container would know nothing about the recipients to which it is linked to. Another noteworthy feature of linking containers, in a secured setup, is that the linked containers can communicate using secured tunnels without exposing the ports used for the setup, to the external world.
The Docker engine provides the --link
option in the docker run
subcommand to link a source container to a recipient container.
The format of the --link
option is as follows:
--link <container>:<alias>
Here, <container>
is the name of the source container and <alias
> is the name seen by the recipient container. The name of the container must be unique in a Docker host, whereas alias is very specific and local to the recipient container, and hence, the alias need not be unique to the Docker host. This gives a lot of flexibility towards implementing and incorporating functionalities with a fixed source alias name inside the recipient container.
When two containers are linked together, the Docker engine automatically exports a few environment variables to the recipient container. These environment variables have a well-defined naming convention, where the variables are always prefixed with capitalized form of the alias name. For instance, if src
is the alias name given to the source container, then the exported environment variables would begin with SRC_
. Docker exports three categories of environment variables, as enumerated here:
NAME
: This is the first category of environment variables. This variable takes the form of <ALIAS>_NAME
, and it carries the recipient container’s hierarchical name as its value. For instance, if the source container’s alias is src
and the recipient container’s name is rec
, then the environment variable and its value would be SRC_NAME=/rec/src
.ENV
: This is the second category of environment variables. These variables export the environment variables configured in the source container by the -e
option of the docker run
subcommand or the ENV
instruction of Dockerfile. This type of an environment variable takes the form of <ALIAS>_ENV_<VAR_NAME>
. For instance, if the source container’s alias is src
and the variable name is SAMPLE
, then the environment variable would be SRC_ENV_SAMPLE
.PORT
: This is the final and third category of environment variables that is used to export the connectivity details of the source container to the recipient. Docker creates a bunch of variables for each port exposed by the source container through the -p
option of the docker run
subcommand or the EXPOSE
instruction of the Dockerfile.These variables take the form:
*<ALIAS>_PORT_<port>_<protocol>
This form is used to share the source’s IP address, port, and protocol as an URL. For example, if the source container’s alias is src
, the exposed port is 8080
, the protocol is tcp
, and the IP address is 172.17.0.2
, then the environment variable and its value would be SRC_PORT_8080_TCP=tcp://172.17.0.2:8080
. This URL further splits into the following three environment variables:
<ALIAS>_PORT_<port>_<protocol>_ADDR
: This form carries the IP address part of the URL (for example: SRC_PORT_8080_TCP_ADDR=172.17.0.2
)<ALIAS>_PORT_<port>_<protocol>_PORT
: This form carries the port part of the URL (for example: SRC_PORT_8080_TCP_PORT=8080
)<ALIAS>_PORT_<port>_<protocol>_PROTO
: This form carries the protocol part of the URL (for example: SRC_PORT_8080_TCP_PROTO=tcp
)In addition to the preceding environment variables, the Docker engine exports one more variable in this category, that is, of the form <ALIAS>_PORT
, and its value would be the URL of the lowest number of all the exposed ports of the source container. For instance, if the source container’s alias is src
, the exposed port numbers are 7070
, 8080
, and 80
, the protocol is tcp
, and the IP address is 172.17.0.2
, then the environment variable and its value would be SRC_PORT=tcp://172.17.0.2:80
.
Docker exports these auto-generated environment variables in a well-structured format so that they can be easily discovered programmatically. Thus, it becomes very easy for the recipient container to discover the information about the source container. In addition, Docker automatically updates the source IP address and its alias as an entry in the recipient’s /etc/hosts
file.
In this chapter, we will take you deep into the mentioned features provided by the Docker engine for container linkage through a bevy of pragmatic examples.
To start with, let’s choose a simple container linking example. Here, we will show you how to establish a linkage between two containers, and transfer some basic information from the source container to the recipient container, as illustrated in the following steps:
$ sudo docker run --rm --name example -it busybox:latest
The container is named example
using the --name
option. In addition, the --rm
option is used to clean up the container as soon as you exit from the container.
/etc/hosts
entry of the source container using the cat
command:/ # cat /etc/hosts 172.17.0.3 a02895551686 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
Here, the first entry in the /etc/hosts
file is the source container’s IP address (172.17.0.3
) and its hostname (a02895551686
).
env
command:/ # env HOSTNAME=a02895551686 SHLVL=1 HOME=/root TERM=xterm PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin PWD=/
--link
option of the docker run
subcommand, as shown here:$ sudo docker run --rm --link example:ex -it busybox:latest
Here, the source container named example
is linked to the recipient container with ex
as its alias.
/etc/hosts
file of the recipient container using the cat
command:/ # cat /etc/hosts 172.17.0.4 a17e5578b98e 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.3 ex
Of course, as always, the first entry of the /etc/hosts
file is the container’s IP address and its hostname. However, the noteworthy entry in the /etc/hosts
file is the last entry, where the source container’s IP address (172.17.0.3
) and its alias (ex
) are added automatically.
env
command:/ # env HOSTNAME=a17e5578b98e SHLVL=1 HOME=/root EX_NAME=/berserk_mcclintock/ex TERM=xterm PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin PWD=/
Apparently, a new EX_NAME
environment variable is added automatically to /berserk_mcclintock/ex
, as its value. Here EX
is the capitalized form of the alias ex
and berserk_mcclintock
is the auto-generated name of the recipient container.
ping
command for two counts, and use the alias name as the ping address:/ # ping -c 2 ex PING ex (172.17.0.3): 56 data bytes 64 bytes from 172.17.0.3: seq=0 ttl=64 time=0.108 ms 64 bytes from 172.17.0.3: seq=1 ttl=64 time=0.079 ms --- ex ping statistics --- 2 packets transmitted, 2 packets received, 0% packet loss round-trip min/avg/max = 0.079/0.093/0.108 ms
Evidently, the source container’s alias ex
is resolved to the IP address 172.17.0.3
, and the recipient container is able to successfully reach the source. In the case of secured container communication, pinging between containers is not allowed. We have given more details on the aspect of securing containers in Chapter 11, Securing Docker Containers.
In the preceding example, we could link two containers together, and also, observe how elegantly, networking is enabled between the containers by updating the source container’s IP address in the /etc/hosts
file of the recipient container.
The next example is to demonstrate how container-linking exports the source container’s environment variables, which are configured using the -e
option of the docker run
subcommand or the ENV
instruction of Dockerfile
, to the recipient container. For this purpose, we are going to craft a Dockerfile
with the ENV
instruction, build an image, launch a source container using this image, and then launch a recipient container by linking it to the source container:
Dockerfile
with the ENV
instruction, as shown here:FROM busybox:latest ENV BOOK=”Learning Docker” CHAPTER=”Orchestrating Containers”
Here, we are setting up two environment variables BOOK
and CHAPTER
.
envex
using the docker build
subcommand from the preceding Dockerfile
:$ sudo docker build -t envex .
example
using the envex
image, we just built:$ sudo docker run -it --rm --name example envex
env
command:/ # env HOSTNAME=b53bc036725c SHLVL=1 HOME=/root TERM=xterm PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin BOOK=Learning Docker CHAPTER=Orchestrating Containers PWD=/
In all the preceding environment variables, both the BOOK
and the CHAPTER
variables are configured with the ENV
instruction of the Dockerfile
.
ENV
category of environment variables, launch the recipient container with the env
command, as shown here:$ sudo docker run --rm --link example:ex busybox:latest env PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin HOSTNAME=a5e0c07fd643 TERM=xterm EX_NAME=/stoic_hawking/ex EX_ENV_BOOK=Learning Docker EX_ENV_CHAPTER=Orchestrating Containers HOME=/root
This example is also available on GitHub at https://github.com/thedocker/learning-docker/blob/master/chap08/Dockerfile-Env.
Strikingly, in the preceding output, the variables that are prefixed with EX_
are the outcomes of container-linking. The environment variables of interest are EX_ENV_BOOK
and EX_ENV_CHAPTER
, which were originally set through the Dockerfile
as BOOK
and CHAPTER
but modified to EX_ENV_BOOK
and EX_ENV_CHAPTER
, as an effect of container-linking. Though the environment variable names get translated, the values stored in these environment variables are preserved as is. We already discussed the EX_NAME
variable name in the previous example.
In the preceding example, we could experience how elegantly and effortlessly Docker exports the ENV
category variables from the source container to the recipient container. These environment variables are completely decoupled from the source and the recipient, thus the change in the value of these environment variables in one container does not impact the other. To be even more precise, the values the recipient container receives are the values set during the launch time of the source container. Any changes made to the value of these environment variables in the source container after its launch has no effect on the recipient container. It does not matter when the recipient container is launched because the values are being read from the JSON file.
In our final illustration of linking containers, we are going to show you how to take advantage of the Docker feature to share the connectivity details between two containers. In order to share the connectivity details between containers, Docker uses the PORT
category of environment variables. The following are the steps used to craft two containers and share the connectivity details between them:
Dockerfile
to expose port 80
and 8080
using the EXPOSE
instruction, as shown here:FROM busybox:latest EXPOSE 8080 80
portex
using the docker build
subcommand from the Dockerfile
, we created just now, by running the following command:$ sudo docker build -t portex .
example
using the earlier built image portex
:$ sudo docker run -it --rm --name example portex
env
command to display all the environment variables, as shown here:$ sudo docker run --rm --link example:ex busybox:latest env PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin HOSTNAME=c378bb55e69c TERM=xterm EX_PORT=tcp://172.17.0.4:80 EX_PORT_80_TCP=tcp://172.17.0.4:80 EX_PORT_80_TCP_ADDR=172.17.0.4 EX_PORT_80_TCP_PORT=80 EX_PORT_80_TCP_PROTO=tcp EX_PORT_8080_TCP=tcp://172.17.0.4:8080 EX_PORT_8080_TCP_ADDR=172.17.0.4 EX_PORT_8080_TCP_PORT=8080 EX_PORT_8080_TCP_PROTO=tcp EX_NAME=/prickly_rosalind/ex HOME=/root
This example is also available on GitHub at https://github.com/thedocker/learning-docker/blob/master/chap08/Dockerfile-Expose.
From the preceding output of the env
command, it is quite evident that, the Docker engine exported a bunch of four PORT
category environment variables for each port that was exposed using the EXPOSE
instruction in the Dockerfile
. In addition, Docker also exported another PORT
category variable EX_PORT
.