Docker Networking 101: A Blueprint for Seamless Container Connectivity
Tags: devops, docker, kubernetes, linux
Published: 2023-12-13 14:05:42

Docker is a platform for developing, shipping, and running applications in containers. Containers are lightweight, standalone, and executable software packages that include everything needed to run a piece of software, including the code, runtime, libraries, and system tools. Docker provides a consistent and reproducible environment across different environments, making it easier to build, deploy, and scale applications.

To enable communication between containers, your host and the outside world, docker implements an interesting networking system. It is this system that bridges the gap between isolated containers, allowing them to function as well-coordinated services.

In this article, you will learn about the basics of the different network types and how to use them in development or deployment.

We will cover:
- [Docker Network Types](#docker-network-types)
- [Using Docker Networks](#using-docker-networks)


## Prerequisites
- [Have Docker Engine Installed](https://www.digitalocean.com/community/tutorials/how-to-install-and-use-docker-on-ubuntu-22-04)
- Have a basic understanding of Docker



## Docker Network Types
Docker comes with 7 (rumours say there are more) network types. However, we will discuss the most important four. They are:
- None
- Bridge (default)
- Bridge (user-defined)
- Host


If you already have Docker setup on your host machine, and you run the command **"docker network list"**, you should get an output similar to this:

```sh
$ docker network list
NETWORK ID     NAME      DRIVER    SCOPE
6f23ed77734d   bridge    bridge    local
3bbca0d09738   host      host      local
a67ca10d6dfd   none      null      local

```

"Driver" in this list, tells us the _network type_, and by default, docker creates one **bridge**, **host** and **null**(none) network each.

Let's discuss each network type in more detail...

### 1. None
![None network type](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7xszaoi4zir8x7ygv7ow.png)
_None_ is a docker network-type where the container is not attached to any network. As a result, the container is unable to communicate with any external network or other containers. It is isolated from every other network. 

You can run an nginx container in a "none" network type using the following command:
```
docker run -d --network none --name my_nginx nginx
```

### 2. Bridge (default)
![Default bridge network](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4prii12z3v8r1x8et54r.png)
The bridge network mode sets up an internal private network within the host. This allows communication between containers within such network, but isolates them from the host's network. 

When docker containers are created without specifying a network, they are automatically placed in the _default bridge_ network.

For example:
```bash
# running a container in the default bridge network
$ docker run -dit --name Rock nginx
```

### 3. Bridge (user-defined)

![User-defined bridge network](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/skgslqwgc5kt84mxxrwx.png)

This network type is similar to the _user-defined bridge_ network. It also creates an internal private network within the host, but it does not come already created by Docker. You have to create it yourself. 

Here's how you create a user-defined bridge network called "demo_net":

```bash
# create a user-defined bridge network
$ docker network create -d bridge demo_net

# list all your docker networks
$ docker network list
NETWORK ID     NAME       DRIVER    SCOPE
6f23ed77734d   bridge     bridge    local
7a824036d47a   demo_net   bridge    local
3bbca0d09738   host       host      local
a67ca10d6dfd   none       null      local

```

Here's how to attach a container to the user-defined bridge network you just created...
```bash
# Attaching containers to bridged network
$ docker run -dit --network demo_net --name service_A nginx

# create another container and publish a port
$ docker run -dit -p 8000:80 --network demo_net --name service_B nginx
```

_service_A_ and _service_B_ containers are now attached to the 'demo_net' network. By default, all containers within the same network can communicate with one another freely but are isolated from the host network and other user-defined networks.  

In order to access applications running on these containers from the host network, we have to publish (or expose) ports from the containers. In the snippet above, port 80 on _service_B_ container is mapped to port 8000 on the host, allowing access to an application running on service_B's container port 80 to be accessed from port 8000 on the host.

### 4. Host
![Host network](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wdsc9kpo0kpg73dn838y.png)

Containers in host network mode directly utilize your host's network stack, lacking isolation. They do not receive separate IP addresses, and any port bindings are directly exposed on your host's network interface. In practical terms, if a container process is configured to listen on port 80, it will bind to your host machine's IP address on port 80.

Here's an example of a container that binds nginx  to a host network:
```bash
# bind container to host network
docker run -d --network host --name my_nginx nginx
```
If you're using the host's network for your web container (like Nginx), it means you can't run multiple web containers on the same host and port. This is because they would all share the same network settings, causing conflicts.

## Using Docker Networks

### Testing Communications Within and Between Networks
In an earlier step, we attached two containers (service_A and service_B) to the _demo_net_ network. Let's confirm that they can communicate with one another within the same network and whether they can communicate with a container in a different network(e.g the "Rock" container in the default bridge network).

```bash
# open a shell into service_A container
$ docker exec -it service_A /bin/bash

# when inside service_A container update its apt repos and install ping
$ apt update && apt install iputils-ping -y

# Then ping service_B using the container name because name resolution is automatically enabled within the network 
$ ping service_B

root@06dbaea8c2a2:/# ping service_B
PING service_B (172.18.0.3) 56(84) bytes of data.
64 bytes from service_B.demo_net (172.18.0.3): icmp_seq=1 ttl=64 time=0.086 ms
64 bytes from service_B.demo_net (172.18.0.3): icmp_seq=2 ttl=64 time=0.154 ms
64 bytes from service_B.demo_net (172.18.0.3): icmp_seq=3 ttl=64 time=0.119 ms
64 bytes from service_B.demo_net (172.18.0.3): icmp_seq=4 ttl=64 time=0.121 ms
...
```
The results are similar when you exec into service_B and ping service_A container. However, none of the containers can reach the "Rock" container which is in the default bridge network because they exist in different virtual networks.

```bash
# Inside service_A
root@06dbaea8c2a2:/# ping Rock
ping: Rock: Temporary failure in name resolution
```

### Reaching Apps Running in Containers via Published Ports
When creating _service_B_ in an earlier step, we ran the following command:

```bash
$ docker run -dit -p 8000:80 --network demo_net --name service_B nginx
```
This command mapped port 80 within the container to port 8000 on the host machine. Hence, when we visit localhost:8000 in our browser, we see the web server running in _service_B_:

![localhost:8000](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mtk8emmt81xsrmfqok9c.png)

### Manipulating Network Connections

```bash
# Disconnect service_A
$ docker network disconnect demo_net service_A

# Reconnect service_A to default "bridge" network
docker network connect bridge service_A

#Next, use `docker inspect` to get the ip of the Rock container and try pinging it.

root@06dbaea8c2a2:/# ping 172.17.0.2
PING 172.17.0.2 (172.17.0.2) 56(84) bytes of data.
64 bytes from 172.17.0.2: icmp_seq=1 ttl=64 time=0.093 ms
64 bytes from 172.17.0.2: icmp_seq=2 ttl=64 time=0.186 ms
64 bytes from 172.17.0.2: icmp_seq=3 ttl=64 time=0.149 ms

# It works!
```

### List Docker Networks
```bash
$ docker network ls
NETWORK ID     NAME       DRIVER    SCOPE
a87871978d59   bridge     bridge    local
747d0aff98fc   demo_net   bridge    local
3bbca0d09738   host       host      local
a67ca10d6dfd   none       null      local

```

### Deleting Docker Networks
To delete a custom network, you must first stop or disconnect all running containers attached to the network. When that's done, proceed to delete the network by running the **docker network rm** command like this:

```bash
$ docker network rm demo_net

# Confirm that the demo_net network has been removed
$ docker network ls
NETWORK ID     NAME      DRIVER    SCOPE
a87871978d59   bridge    bridge    local
3bbca0d09738   host      host      local
a67ca10d6dfd   none      null      local
```
<br>
<br>
<br>

## Conclusion
Docker's networking system gives you different choices for handling communication between containers, their nearby containers, and your Docker host. In a network, containers can connect with each other using their names or IP addresses.

User-defined bridge networks work well when you want various containers to talk to each other on the same Docker host. On the flip side, host networks are more suitable when you don't want the network stack to be separate from the Docker host, but you still want other parts of the container to be isolated.

Docker's networking system could initially feel overwhelming, but I hope this gives you some clarity. 

...

If you have any questions or comments, you can leave them below, or reach out to me on [LinkedIn](https://www.linkedin.com/in/davidomokhodion/). Till we meet again...

Stay awesome!
~ David Omokhodion

<br>
<br>
<br>
## Attributions

- [Docker Networking – Basics, Network Types & Examples](https://spacelift.io/blog/docker-networking#how-docker-networking-works)
- [Docker — Container Network Basics](https://tonylixu.medium.com/docker-container-network-basics-2d7d0e45ccf8)
- [A Quick Guide to Docker Network Types](https://kodekloud.com/blog/networking-docker-containers/)