Docker containers are self-contained, isolated environments. However, they’re often only useful if they can talk to each other.
There are many ways to connect containers. And we won’t attempt to cover them all. But in this miniseries, we will look at some common ways.
This topic seems elementary, but grasping these techniques and the underlying design concepts is important for working with Docker.
Understanding this topic will:
- Help developers and ops people explore the broad spectrum of container deployment choices
- Let developers and ops people to embark more confidently with a microservice design architecture
- Empower developers and ops people to better orchestrate more complex distributed applications
Fortunately, the large number of connection options for containers enables a broad range of approaches, giving us the flexibility to choose an architecture that suits the needs of any application.
In this post, we’ll look at three of the older, more basic ways of connecting Docker containers. Using this knowledge and experience as a foundation, we’ll then move on to two newer, easier, more powerful ways in the next post.
Setup
Before we can demonstrate how containers can be connected, we need to create a pair of them for use in our examples.
The first image will be derived from a simple Ubuntu installation. It will act as a client container.
First, we create the container and attach to it:
$ sudo docker attach client_setup
Then, once we have a shell inside the container, we run:
If you do not see the shell, click the up arrow.
Now, detach from the client container using CTRL+p then CTRL+q.
Then, stop it, and commit:
$ sudo docker commit client_setup client_img
We now have an image called client_img to use.
The second container we want to use is, again, derived from an Ubuntu installation. But this time, we’ll modify it to run a Apache HTTP server.
First, we create it and attach, like before:
$ sudo docker attach server_setup
Then, once we have a shell inside the container, we install the Apache HTTP server:
When the installation is complete, detach from the container using CTRL+p and CTRL+q.
Now, stop the container, and commit it:
$ sudo docker commit server_setup server_img
We now have two images: client_img and server_img.
Now we have this set up, we can explore the various connection possibilities.
The Docker Bridge
By default, a Docker container is isolated from other containers and the external network. Docker provides a bridge, the docker0 interface, which is created with the installation of the Docker Engine.
It is through the Docker bridge that communication is possible amongst containers and between containers and the host machine.
You can see the Docker bridge by running this command on a Docker host:
You should see something like this in the output:
inet addr:172.17.0.1 Bcast:0.0.0.0 Mask:255.255.0.0
inet6 addr: fe80::42:a2ff:fedc:fa8/64 Scope:Link
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:1477 errors:0 dropped:0 overruns:0 frame:0
TX packets:2436 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:0
RX bytes:83901 (83.9 KB) TX bytes:3606039 (3.6 MB)
The bridge interface works locally, on a single Docker host, and is the connection mechanism behind all three approaches we cover in this post. In the next post, we’ll move on to the overlay interface, which allows us to network containers across multiple Docker hosts.
Exposing Ports
First, let’s see how we can run a server container so that it exposes port 80 (HTTP) to other containers.
To do so, I run the container with the expose command, which tells Docker to expose port number specified when it runs the container. An exposed port is one that other containers can reach.
Let’s run server_img as a container called server1, exposing port 80:
We’ll name our containers sequentially (server1, server2, and so on) as we go.
Then, attach to the container:
Again, If you do not see the shell, click the up arrow.
Start the Apache HTTP server:
And get the IP address:
eth0 Link encap:Ethernet HWaddr 02:42:ac:11:00:03
inet addr:172.17.0.3 Bcast:0.0.0.0 Mask:255.255.0.0
Okay, so we have an IP address of 172.17.0.3. Let’s test we can see that from a client container.
Open a second terminal.
Start the client1 container:
Attach to it:
If you do not see the shell, click the up arrow.
Make a test connection to the server1 container’s IP address:
If everything works, you should see the HTML of the Apache HTTP server’s default page. This indicates the the client1 container is able to make connections to the server1 container’s HTTP port.
Port Binding
What if we want to expose our HTTP server to the host network, including application on the host machine, and other machines on the host network? In this scenario, we need to bind a host port to a container port.
To expose the Apache HTTP server to the host network, we need to bind port 80 on the server container to port 8080 on the host machine.
We can do this like so:
The -p 8080:80 option is the thing to pay attention to here.
Now, attach to the container:
If you do not see the shell, click the up arrow. And start the Apache HTTP server:
Now, from the host system, visit http://localhost:8080/ and you should see the Apache HTTP server default page.
Any machine on your host network that can access port 8080 of your host machine will also be able to access this.
Linking Containers
Another approach to connecting containers involves something Docker calls linking.
When you link one container to another, Docker will make some information about the linked container available via environment variables.
Let’s take a look.
First, start the server container:
Now, start the client container and link it to the server container, like so:
Notice the –link server3 option being used here.
Now attach to the client container:
And check out the available environment variables:
$
SERVER3_PORT_80_TCP_PROTO=tcp
SERVER3_PORT=tcp://172.17.0.2:80
SERVER3_PORT_80_TCP_PORT=80
SERVER3_NAME=/client3/server3
SERVER3_PORT_80_TCP=tcp://172.17.0.2:80
SERVER3_PORT_80_TCP_ADDR=172.17.0.
Docker also updates the /etc/hosts file in the client container to add server3 as a local hostname pointing to the server container.
To demonstrate this, run:
You should see the same default HTML again.
Wrap-Up
In part one of this miniseries, we introduced the Docker bridge interface, which lets us connect containers on the same host.
We looked at three connection methods:
Connection through port exposure
Binding of the host port to the container port
Linking two containers through the link option
In part two, we’ll look at isolating containers inside user-defined networks. We’all also introduce the overlay interface, and take a look at how to use it for networking Docker containers across multiple Docker hosts. Even across datacenters and cloud providers!