Communication between Docker containers

There is a default bridge network that exists on all docker hosts, and any containers you launch will be added to that bridge by default.  You can see it with:

$ docker network inspect bridge

With no containers running, you’ll see nothing listed here under “Containers”, but once you start a container, it will be on that bridge:

$ docker run -itd postgres

$ docker network inspect bridge

// result snipped for brevity
"Containers": {
 "f94674a1a0df545bb64058f3b763426466e4f0daa80397504581d537a4907e11": {
 "Name": "prickly_lovelace",
 "EndpointID": "d3d721da42c5b2d7b50266e49112c0590af80c01d96b272c83e2fcd05e9c2e73",
 "MacAddress": "02:42:ac:11:00:02",
 "IPv4Address": "172.17.0.2/16",
 "IPv6Address": ""
 }
},

Now start a container with bash, and you can ping 172.17.0.2:

$ docker run -it debian /bin/bash
root@176b02a2a6e2:/# ping -c 1 172.17.0.2

PING 172.17.0.2 (172.17.0.2): 56 data bytes
64 bytes from 172.17.0.2: icmp_seq=0 ttl=64 time=0.136 ms

That’s cool…they can see each other, and they can actually connect over any ports that are exposed in the Dockerfile, 5432 in this case.

root@176b02a2a6e2:/# </dev/tcp/172.17.0.2/5432 && echo connected

connected

On the default bridge network, you link two containers with the --link option, which does little more than add a /etc/hosts entry:

$ docker run -it --rm  --link prickly_lovelace:pg debian /bin/bash
root@84e87ecb1060:/# ping -c 1 pg

PING pg (172.17.0.2): 56 data bytes
64 bytes from 172.17.0.2: icmp_seq=0 ttl=64 time=0.101 ms

root@84e87ecb1060:/# cat /etc/hosts | grep pg
172.17.0.2 pg da01e1088f69 prickly_lovelace

 

Docker also exposed port 5432 on the bridge network only.  It’s not available on the host, but another container on that network can connect to it.

$ docker run -it --rm  --link prickly_lovelace:pg postgres psql -h pg -U postgres

This dropped us in a psql shell connected on one container to the postgres container on another.

You may have heard that --link is deprecated.  It is, as you only really use that on the default bridge network to connect containers by name.  To really isolate your applications, you should create a bridge, and you get DNS entries for each docker container.

$ docker network create --driver bridge mynet

Now you have a network and you can inspect it, e.g. docker network inspect myna.  When launching containers on that bridge, if you specify the name, then other containers on the same bridge can resolve by name using Docker’s embedded DNS server:

$ docker run -itd --name pgserver --network mynet postgres

Here you named the container “pgserver”, put it on the network “mynet”, and launched it from the “postgres” image.  You can verify that a container launched in “mynet” can connect, while a container launched elsewhere will not.

$ docker run -it --rm postgres psql -h pgserver -U postgres

psql: could not translate host name "pgserver" to address: Name or service not known

$ docker run -it --rm --network mynet postgres psql -h pgserver -U postgres

psql (9.6.2)
Type "help" for help.
postgres=# \q

Looks great, we have network isolation and hostname resolution between containers in our bridge network!

We were able to connect to 5432 since that is exposed in the Dockerfile for the postgres image, but what if you deployed an application to a standard image that doesn’t expose any ports?  For example, you might mount a directory containing your application in a container launched from the microsoft/dotnet:runtime image, and wish to expose the port for your application.  To do this, pass --expose=<port> as an option to docker run.  As a very simplistic example, the following will install python3 on a container based off debian and start the simple http server:

$ docker run -itd --network mynet --expose 8000 --name littlehttp debian /bin/bash -c "apt-get update && apt-get install -y python3 && python3 -m http.server"

Once the container is done installing python3, it will listen on port 8000, which you can connect to with another container.

$ docker run -it --rm --network mynet debian /bin/bash -c "apt-get update && apt-get install -y curl && curl http://littlehttp:1500"

You can expose a port for a container attached to the bridge network on the host network instead:

$ docker run -itd --network mynet --expose 8000 -p 0.0.0.0:1234:8000 --name littlehttp debian /bin/bash -c "apt-get update && apt-get install -y python3 && python3 -m http.server"

In the above, we tell the container to expose 8000 and then bind host port 1234 to that container’s port 8000.  Now we can browse to our docker host’s port 1234 to connect to the simple http service from the container.

When it’s time to tear down the application, after removing the containers, you can remove the “mynet” bridge network for them with docker network remove mynet.

 

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s