docker-redes-de-usuario

User-Defined Networks (bridge) in Docker

  • 4 min

In the previous article, we learned how to connect a container to the “outside world” using ports (-p). But, what happens when we want to connect two containers to each other?

Imagine the most classic web development scenario:

  1. A container with your Application (Node.js, PHP, Python…).
  2. A container with your Database (MySQL, Mongo…).

Your application needs the database address to connect. When you run docker inspect, you see the database has IP 172.17.0.2. You put it in your code, deploy it, and it works.

The next day, you restart your computer, start Docker, and… everything is broken 💥. The database now has IP 172.17.0.3 because it started in a different order.

IPs in Docker are ephemeral and dynamic. Never, under any circumstances, should you rely on them.

The solution is to use names instead of numbers. And for that, we need to create our own network.

The “Default” Network vs “User-Defined”

Docker comes with a default network called bridge (the one it uses if you don’t specify anything). However, the default network does NOT have automatic DNS name resolution.

For containers to be able to call each other by name (e.g.: ping database-server), we need to create a User-Defined Bridge Network.

When we do this, Docker activates an internal DNS server. If a container tries to connect to http://my-database, Docker intercepts the call, looks up the IP of that container at that moment, and resolves it automatically.

Step 1: Create your own network

Creating a network is as simple as giving it a name. Let’s create a network for our project:

docker network create my-app-network

If we list the networks, we’ll see our new one:

docker network ls

Step 2: Connect containers to that network

Now let’s launch two containers, but we’ll explicitly tell them to connect to my-app-network using the --network flag.

Important: We must use the --name flag, because that name will be the domain we will use to find them.

We will use Nginx, but imagine it’s a database we’ll call db-server.

docker run -d --name db-server --network my-app-network nginx
Copied!

Note that there is no need to publish ports (-p). Since the communication is going to be internal (between containers on the same network), we don’t need to expose anything to the outside. Port 80 is accessible by its network “neighbors”.

Now let’s launch a lightweight container (Alpine) to run connection tests. We’ll call it my-app.

docker run -it --name my-app --network my-app-network alpine /bin/sh
Copied!

Step 3: The internal DNS

Now we are inside the terminal of my-app (the Alpine container). Let’s try to connect to the other container.

If we ping the IP, it would work, but we said that is forbidden. Let’s ping the container’s name:

/ # ping db-server

You will see a response:

PING db-server (172.18.0.2): 56 data bytes 64 bytes from 172.18.0.2: seq=0 ttl=64 time=0.098 ms 64 bytes from 172.18.0.2: seq=1 ttl=64 time=0.102 ms

It works! Docker has resolved that db-server corresponds to 172.18.0.2.

If you restart Docker tomorrow and db-server gets IP 172.18.0.55, your ping will still work just the same, because Docker will update the DNS record automatically.

This is the reason why, in your application configuration files, in the “Database Host” field, you will put the container name (e.g.: mysql-container) and not localhost or an IP.