Understanding Docker Networks: A Beginner's Tutorial

I am student currently pursuing my Bachelor's in computer science. I have a keen interest in DevOps and LINUX related technologies
CONTENTS:
Introduction
Building our custom image
Creating a custom network
Connecting two custom networks
Introduction
Firstly we need to be a little familiar with the docker networks. Docker connects freshly created containers to a default network called bridge. This bridge network acts as a bridge between host and containers, this allows communication between containers and with the host as well. To verify this let's spin up a simple container.
docker run -d --name s1 httpd
docker inspect s1
you will see something like this

You can see that the network type is a bridge, there is an IP address 172.17.0.2 which is your container's internal IP address and a gateway 172.17.0.1. This is the common gateway for all the containers attached to the default bridge network. This gateway allows for communications outside the network. To understand it more effectively we need to equip the httpd image with some Linux networking tools.
Building our custom image
FROM httpd
RUN apt-get update &&\
apt-get install -y iputils-ping &&\
apt-get install -y inetutils-traceroute &&\
apt-get install -y iproute2 &&\
apt-get install -y curl telnet dnsutils vim
This file will install tools like ping,traceroute,ip route,curl ,telnet,DNS utilities and vim.
Copy this code into a docker file and build an image, we will be using nhttpd as the name of our new image.
Run two containers by name s1 and s2 and exec into it
docker build . -t nhttpd
docker run --name s1 -d nhttpd
docker run --name s2 -d nhttpd
docker exec -it s1 /bin/bash
Notice that we did not publish any ports to the host, try pinging google.com now that you are inside the container.
ping google.com
You will see something like this

How are we getting a response back? Well, the gateway makes this possible. When you ping google the packets are sent to the gateway which is connected to an interface that opens to the host connected to the internet and that is how we can reach Google from inside our container.
Try pinging the container s2 using its hostname
ping s2
We did not get a response back. Why is that? well let's try nslookup on s2
nslookup s2

nslookup
Notice the server address this is not the container's subnet. The networking intricacies within Docker often lead to packets being directed to a gateway connected to a host-accessible interface, rather than the intended container subnet. This scenario can result in attempts to access resources outside the network through the host's DNS server. In the upcoming section on "Creating a Custom Network," we will explore a solution to address this issue and gain a deeper understanding of how to configure Docker networks to ensure efficient communication within containers while resolving external resource requests accurately.
Here is something try pinging container s2 from container s1 using its ip address.
ping 172.17.0.3

So the hostname doesn't work but the IP address does.
This is also concerning. A container can ping another container on the same network. This explains the need for creating our network as any container on the same network can communicate with our container.
Creating a custom network
Create a network named backend and connect our containers s1 and s2 to it using the below commands.
docker network create backend --subnet 10.0.0.0/24
docker network connect backend s1
docker network connect backend s2
Also, we need to disconnect these two from the default network to isolate them.
docker network disconnect bridge s1
docker network disconnect bridge s2
# exec inside s1
#you can inspect network to get more information using docker network inspect backend
docker exec -it s1 /bin/bash
Now let us try again to reach s2 without using its ip
nslookup s2

Notice the difference, what happens is that Now within this isolated network, we have our DNS set up — a great step forward! However, should you wish to restrict internet access, you can achieve this by creating the network using the --internal option.
But what if you want this isolated network to establish communication with another network, perhaps named "frontend"? In the next section, we'll explore how to facilitate this inter-network communication while maintaining the desired level of isolation.
Connecting two custom networks
This is where things get fun. First, create another container named gateway and connect to another network named frontend.
docker network create frontend --subnet 10.0.1.0/24
docker run --name gw -d nhttpd
# connecting container to networks
docker network connect frontend gw
docker network connect backend gw
What we need to do is to add an IP route inside the containers s1 and s2 defining the rule for transferring packets. To do that we need to grant NET_ADMIN capabilities to these containers. You can remove these containers and create new ones with --cap-add=NET_ADMIN.
--cap-add=NET_ADMIN
Remove the containers s1 and s2 and create new containers s1 and s2.
docker rm s1 s2 -f
#creating new containers
docker run --name s1 -d --cap-add=NET_ADMIN nhttpd
docker run --name s2 -d --cap-add=NET_ADMIN nhttpd
#attach them to backend and frontend respectively
docker network connect backend s1
docker network connect frontend s2
Now we need to add the rule to both s1 and s2. Exec into s1 and add
docker exec -it s1 /bin/bash
ip route add 10.0.1.0/24 via 10.0.0.4 #10.0.0.4 is ip of gw in backend
Similarly, add this to s2
docker exec -it s2 /bin/bash
ip route add 10.0.0.0/24 via 10.0.1.2 #10.0.0.2 is ip of gw in frontend
ip route
You can check the IP addresses of containers in a network using inspect. Make sure that you enter the correct IP address while adding the route.
docker network inspect backend
docker network inspect frontend
Try tracing the route of packets from inside s1 and s2.
docker exec -it s2 /bin/bash
traceroute 10.0.0.2

traceroute
As you can see the packet first went to 10.0.1.2 which is our container gw and then to its destination 10.0.0.2 which is s1.
similarly,
docker exec -it s1 /bin/bash
traceroute 10.0.1.3

Here the gw container is at address 10.0.0.4
This concludes this article on playing with docker networks. Docker networks can be a little tricky but are fun to experiment with, if you have questions feel free to drop them in the comment box and I will see you in the next article.


