A beginner’s guide to networking in Docker

💡 I am going to use thatisuday/express-example image we have developed in the “ Creating your first Docker application ” lesson to demonstrate some networking examples. This image exposes port 8000 by default and runs node server.js command (using CMD instruction) to start an express HTTP server when a container is created from it, however, we can override this command by providing parameters with the $ docker run <image> <params> command.

This command displays the built-in networks provided by Docker and they are not removable so you can’t use $ docker network rm <network> command (where network is either NETWORK ID or NAME ) to remove them.

When you install Docker for the first time, we get three types of networks out of the box. You can use the $ docker network ls command to see them.

A Docker network is a medium through which a Docker container can talk to its host, other containers on the host, or any other machines on or outside the host’s network. To configure networks, we use the $ docker network command that provides us subcommands such as ls , create , attach to configure networks and containers’ relationship to them.

In this lesson, we are going to talk about the networking aspect of the containerization process. We will explore what different options Docker gives us to control the network of a Docker container and how we can use them depending on our needs.

In the previous lessons , we learned a great deal about Docker, Dockerfile, Docker images, and Docker containers. We understood the isolated nature of a Docker container and how to connect with a running container using -p (or –publish ) flag to map a host port to a container port for network access or -v (or –volume ) flag to access files of a container from the host.

In this lesson, we are going to understand how host-to-container and container-to-container communication takes place. Since this won’t be an advanced guide, we are only going to explore the surface of networking in Docker.

Bridge network

The network with the name bridge is the default network a container is attached to by default. It is created from the bridge driver. A driver is like a template for the network with specific behavior and capability. A bridge driver creates an isolated pool of private IP addresses (a private subnet) on the host which usually starts with an IP address 172.X.X.X.

We can inspect a network using $ docker inspect or more preferably $ docker network inspect <network> command. Let’s see the configuration of the network bridge network.

$ docker network inspect

The $ docker network inspect bridge command displays the configuration of the network with the name bridge. It shows that the name of the network is bridge(Name field), it was created from the bridge driver (Driver field) and its ID is 9a923c76f347 (Id field).

Let’s focus our attention on the IPAM (IP Address Management) section of the JSON above. It displays the Subnet and Gateway IP addresses. The Subnet field specifies the pool of IP addresses which in this case will start from 172.17.X.X except for the 172.17.0.1 which is the default gateway. Any containers that will be attached to this network will receive an IP address from this pool and will use the 172.17.0.1 address as a default gateway.

💡 The reason containers attached to this network will gain an IP address that starts from 172.17.X.X is that the subnet mask in CIDR notation is 16 represented by 172.17.0.0/16 value. This means, the first 16 bits of 172.17.0.0 are fixed while other 16 bits will be used for the IP pool of the subnet. To know more about subnetting, watch this video.

If we take a look at the Containers property, it seems empty. That’s because we do not have a running container attached to this network. As we learned, when we create a container, the container will use this network by default.

So let’s create a container from the thatisuday/express-example image. We will run this container in a non-detached mode and override the default node server.js command (defined by CMD instruction of the Dockerfile) with sh command to start a shell.

$ docker run -it --init thatisuday/express-example sh

$ docker run

When we run the container, it will drop us into a shell of the container where we can access various Linux commands. The $ hostname -i displays the IP address of the machine (container) which is 172.17.0.2. We can look at other network configurations of the container using $ ifconfig command.

From the eth0 network interface of the container, we can also read the IP address as well as the subnet mask. The 255.255.0.0 IP address as a subnet mask translates to /16 CIDR notation. Therefore, we can confirm that the container is using an IP address from the 172.17.0.2/16 pool which belongs to the network with the name bridge.

Let’s inspect the running container using the $ docker inspect or more preferably the $ docker container inspect <container> command to see the network configuration details of the container.

We can list the running containers using $ docker ps command and our running containers ID is c81cc3b5f8aa. Then using $ docker container inspect command, we can see the .NetworkSettings value. Take a good look at Networks property. Not only it displays the IPAddress but also the network it is attached to which is bridge in this case with ID 9a923c76f347.

Now that we have a running container attached to the bridge network, we should be able to see the container in the Containers field of the JSON returned by the $ docker network inspect command.

$ docker network inspect

Once we exit from the container, it will no longer be attached to the network and Container field will be empty again. Let’s run another container but this time, we will manually attach it to a network. So let’s create a bridge network (using the bridge driver).

To create a network we use the $ docker network create command. The --driver or -d flag sets the driver to be used which defaults to bridge if not provided. We can also control the subnet (IP pool) using the --subnet else Docker will configure it automatically.

$ docker network create -d bridge mybridge

$ docker network create

In the example above, we have a network with the name mybridge from the bridge driver. Upon inspection, we can see that the subnet of this network is 172.19.0.0/16 which means any container that is attached to this network will get an IP address that starts with 172.19.X.X.

Now let’s create another container from the thatisuday/express-example image but this time, let’s specify a network manually using the --network <network> or --net <network> flag with the $ docker run.

$ docker run

In the example above, we have asked Docker daemon to attach mybridge network to the container we are creating hence Docker has given the 172.19.0.2 IP address to the container from the mybridge IP pool.

You can manually connect a container to a network using the $ docker network connect <network> <container> command. A container can join multiple networks and thus have multiple public IP addresses each per network. This way, a container can talk to multiple containers on different networks, thus consuming multiple services.

Similarly, you can use the disconnect subcommand to do the opposite. You can remove user-defined networks using the $ docker network rm <network> command as long as no running containers are attached to the network.

Benefits

So what are the benefits of having a bridge network? A bridge network creates a private local network that a container can join and it is isolated from the containers connected to another bridge network.

A container in the bridge network is like an isolated machine with its own public IP address that can be used by other containers in the same network and the host for communication. All ports of the container remain hidden from the host and the host would need to use http://<container-ip>:<port> URL for HTTP communication. However, the host can use the --publish or -p flag to bind a local port with a port of the container to make things simple.

💡 I am using Docker on macOS which runs the Docker engine inside a virtual machine. This does make things a little complicated but I have explained in this article how to gain access to the shell of this virtual machine. So whenever I mean host, I mean this virtual machine.

(Docker Container)

Let’s run the node server.js command inside the container we have started previously to start an HTTP server on port 8000. Since the IP address this container is 172.19.0.2, we should be able to access this server from the host using http://172.19.0.2:8080 URL.

(Docker Host)

As you can see, we got the response back from the HTTP server of the container with IP address 172.19.0.2 running on port 8000. However, we can’t use the http://127.0.0.1:8000 or http://localhost:8000 URL since we haven’t mapped the local port of the host to the port 8000 of the container. However, if we use the -p 8000:8000 flag in the $ docker run command, we should be able to do that.

(Docker Host)

As we discussed, a container in a bridge network can’t communicate with a container from another bridge network. To be able to communicate, they should be on the same network. We can use $ docker network connect command to connect a running container with a network.

In the example above, the first container was able to download the image from http://172.19.0.2:8080 URL because it is on the same network (mybridge) as the container serving the HTTP endpoint. However, the second container can’t use this endpoint since it’s on the default network (bridge).

A bridge network constructs its own DNS (domain name system). The hostname of each container is mapped to its IP address by default hence you can use http://<hostname>:<port> to communicate with containers in the same bridge network. You can also use the --net-alias=<alias> flag with $ docker run command to add an entry in DNS to resolve the IP address of a container such that http://<alias>:<port> would work.

(Docker Container One)

In the example above, we have created a container that uses mybridge network and we have set host.one network alias to it. Let’s create another container (with or without an alias) and access the service of the first container using various available options.

(Docker Container Two)

As you can see from the results above, you can use either the IP address, hostname, or a network alias to access the containers attached to the same bridge network. This way, you don’t have to rely on the IP address or hostname of the container and you can freely use the network alias.

💡 To know more about the bridge network, follow this documentation.