Connecting containers to existing containers

Docker network connectivity up until this point has relied on exposing individual services hosted in a container to the physical network. However, what if you want to expose a service from one container to another without exposing it to the Docker host? In this recipe we'll walk through how to map services between two containers running on the same Docker host.

Getting ready

You'll need access to a Docker host and an understanding of how your Docker host is connected to the network. In this recipe, we'll be using the docker1 host that we used in previous recipes. You'll want to make sure that you have access to view iptables rules to verify netfilter policies. If you wish to download and run example containers, your Docker host will also need access to the Internet. In some cases, the changes we make may require you to have root-level access to the system.

How to do it…

Mapping services from one container to another is sometimes referred to as mapped container mode. Mapped container mode allows you to start a container that utilizes an existing, or primary, container's network configuration. That is, a mapped container will use the same IP and port configuration as the primary container. For the sake of example, let's consider running the following container:

user@docker1:~$ docker run --name web4 -d -P 
jonlangemak/web_server_4_redirect

Running this container starts the container in bridge mode and attaches it to the docker0 bridge as we would expect.

The topology will look pretty standard at this point, something like what is shown in the following topology:

How to do it…

Now run a second container on the same host, but this time specify that the network should be that of the primary container web4:

user@docker1:~$ docker run --name web3 -d --net=container:web4 
jonlangemak/web_server_3_8080

Our topology now looks as follows:

How to do it…

Note how the container web3 is now depicted as being attached directly to web4 rather than to the docker0 bridge. By looking at the networking configuration of each container, we can validate that this is actually the case:

user@docker1:~$ docker exec web4 ip addr show
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN qlen 1
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host
       valid_lft forever preferred_lft forever
16: eth0@if17: <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:~$
user@docker1:~$ docker exec web3 ip addr show
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN qlen 1
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host
       valid_lft forever preferred_lft forever
16: eth0@if17: <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 we can see, the interfaces are identical both in IP configuration as well as MAC addresses. Using the syntax of --net:container<container name/ID> in the docker run command joins the new container to the same network construct that the referenced container is in. This means that the mapped container has the same network configuration as the primary container.

There is a limitation to this configuration that is worth noting. A container that joins another container's network cannot publish any of its own ports. So while this means that we can't publish ports of mapped containers to the host, we can consume them locally. Going back to our example, this means that we can't publish port 8080 of container web3 to the host. However, container web4 can consume nonpublished services of the container web3 locally. For instance, each of the containers in this example hosts a web service:

  • web3 hosts a web server running on port 8080
  • web4 hosts a web server running on port 80

From an external host perspective, there is no way to access the web service of the container web3. We can however, access these services through the container web4. The container web4 is hosting a PHP script named test.php that pulls the index pages of its own web server as well as that of a web server running on port 8080. The script is as follows:

<?
$page = file_get_contents('http://localhost:80/');
echo $page;
$page1 = file_get_contents('http://localhost:8080/');
echo $page1;
?>

The script lives in the web server's root hosting directory (/var/www/), so we can access the port by browsing to the web4 container's published port followed by test.php:

user@docker1:~$ docker port web4
80/tcp -> 0.0.0.0:32768
user@docker1:~$
user@docker1:~$ curl http://localhost:32768/test.php
<body>
  <html>
    <h1><span style="color:#FF0000;font-size:72px;">Web Server #4 - Running on port 80</span>
    </h1>
</body>
  </html>
<body>
  <html>
    <h1><span style="color:#FF0000;font-size:72px;">Web Server #3 - Running on port 8080</span>
    </h1>
</body>
  </html>
user@docker1:~$

As you can see, the script is able to pull the index page from both containers. Let's stop the container web3 and run this test again to prove that it's really the one providing this index page response:

user@docker1:~$ docker stop web3
web3
user@docker1:~$ curl http://localhost:32768/test.php
<body>
  <html>
    <h1><span style="color:#FF0000;font-size:72px;">Web Server #4 - Running on port 80</span>
    </h1>
</body>
  </html>
user@docker1:~$

As you can see, we no longer get the response from the mapped container. Mapped container mode is useful for scenarios where you need to provide a service to an existing container, but don't need to publish any of the mapped container's ports directly to the Docker host or outside network. Although there is a limitation that mapped containers cannot publish any of their own ports, this does not mean we can't publish them ahead of time.

For instance, we could expose port 8080 when we run the primary container:

user@docker1:~$ docker run --name web4 -d --expose 8080 
-P jonlangemak/web_server_4_redirect
user@docker1:~$ docker run --name web3 -d --net=container:web4 
jonlangemak/web_server_3_8080

Because we published the port for the mapped container when we ran the primary container (web4), we don't need to publish it when we run our mapped container (web3). We should now be able to access each service directly through its published port:

user@docker1:~$ docker port web4
80/tcp -> 0.0.0.0:32771
8080/tcp -> 0.0.0.0:32770
user@docker1:~$
user@docker1:~$ curl localhost:32771
<body>
  <html>
    <h1><span style="color:#FF0000;font-size:72px;">Web Server #4 - Running on port 80</span>
    </h1>
</body>
  </html>
user@docker1:~$ curl localhost:32770
<body>
  <html>
    <h1><span style="color:#FF0000;font-size:72px;">Web Server #3 - Running on port 8080</span>
    </h1>
</body>
  </html>
user@docker1:~$

Care should be taken in mapped container mode to not attempt to expose or publish the same port on different containers. Since the mapped containers share the same network construct as the primary container, this would cause a port conflict.

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

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