Have you ever wondered how networks in Docker work? Maybe you are interested in the less known things that you can do with the networking layer with Docker? Here are some interesting facts and use cases, that might help in every-day use.
Let’s start with the basics. Ports exposing is the most commonly used thing around Docker networking, but do you know all the things about it?
Let’s take a look at a simple command like this:
docker run -p 127.0.0.1:80:8080/tcp ubuntu bash
What does this mean? Well, might be tricky, but it means: Listen for TCP connections on 127.0.0.1 on port 80, and forward the traffic to port 8080 inside the container.
If we simplify that a bit:
docker run -p 80:8080/tcp ubuntu bash
We have omitted the IP part, and now Docker will listen on all interfaces, so it will be possible to access the service from the outside.
We can change the notation even more, and run:
docker run -p 80:8080/udp ubuntu bash
This will forward udp connections. Another option is sctp but it is not widely used for web related stuff. TCP is obviously the most common one, so if we skip the /protocol part - it will be set to TCP by default.
And what happens if we run just this:
docker run -p 8080 ubuntu bash
This will forward TCP traffic, from a randomly chosen port to port 8080 in the container. Wait? Randomly?! How do I know which port was used?
Just take a look at docker ps - there is a column for that:
$ docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 2f82dac833ae mariadb:10.3 "docker-entrypoint.s…" 10 days ago Up 9 hours 3306/tcp project_db_1 86f00e7f41a2 phpmyadmin "/docker-entrypoint.…" 10 days ago Up 9 hours 0.0.0.0:8080->80/tcp project_phpmyadmin_1 31ea70729fbf redis:6 "docker-entrypoint.s…" 6 weeks ago Up 9 hours 6379/tcp project_redis_1
In the PORTS column is all you need plus more. It also shows exposed ports that are not forwarded. Such port is not accessible from the outside (except docker networks, but this will come alter), but you get this info in case you would like to forward it.
You can also run
docker port to check mapping for a given container:
$ docker port project_phpmyadmin_1 80/tcp -> 0.0.0.0:8080
But it is missing the part for not mapped ports, so I guess you won’t use that command too often ;)
There is one last thing that we need to mention, and that is the -P argument for docker run.
$ docker run -P ubuntu bash
-P exposes all ports mentioned in the Dockerfile on random ports on the host machine.
Let's start a simple web-server:
docker run -d --name test_web nginx:alpine
If we then lunch a second container, lets say ubuntu, install curl on it and try to access the web page:
docker run -t -i --rm ubuntu bash apt update apt install curl curl test_web
This wont work, the name is not resolved! We could make use of
docker inspect and check that the IP of the
test_web container is
172.17.0.2, then run
And it would work. So the connectivity is limited, but it is possible.
If you are familiar with docker-compose, this might be confusing. Services inside docker-compose can easily communicate with each other using their names! If you have a simple file like:
version: '3.6' services: db: image: mariadb:10.3 environment: ... phpmyadmin: image: phpmyadmin restart: always ports: - 8080:80 environment: - PMA_HOSTS=db
phpmyadmin can obviously connect to the db service using its name -
The reason for that is quite simple, it works for containers within the same network, except for the default one.
Lets give it a try!
We can create a new network by running:
docker network create test
and connect existing containers to it running:
docker network connect test test_web docker network connect test NameOfYourBashContainer
I assume the test bash container is still alive, you just need to use its name in the second line above. Now switching back to it and running:
Will work! Notice that a port does not need to be exposed in order to access it from a second container as long as both containers share the same network.
Disconnect one of the containers from the newly created network by running:
docker network disconnect test NameOfYourBashContainer
And the name wont be resolved anymore!
It is also possible to connect a container to a network while creating it, just pass the network as one of the input options:
docker run -t -i --rm --network test ubuntu bash
One sample use cases would be to connect different projects or microservices, without having all of the running containers in one network. This allows to quite freely adjust the matrix of connections between containers. You can use this to test security rules that are configured on production - ex. on AWS.
Another use case would be to test connection issues (chaos monkey). We will cover a better approach to this in follow up articles, but networks will do the work for basic scenarios.
I published, and plan to publish more follow-up articles with networks in docker-compose, microservices, etc. Subscribe to our newsletter to make sure you won't miss them.
Follow-ups written so far:
- Docker Networks - part 2 - includes network separation and chaos monkey (simulating network issues).
- Docker reverse proxy using Traefik - describes the basics of load balancing and reverse proxy set-up using Traefik.
Also, if you are interested in more advanced Docker techniques, check my recent eBook - Docker Deep Dive.