Last time we discussed to talk with docker containers by publishing and exposing ports. Now let’s use Docker compose to see how multiple services might talk to one another.
From last time, we were using
ryanlabouve/hello-world we can run a tiny web server and provide it a default message.
# => runs the docker image docker run -p 3000:3000 \\ -e MESSAGE=test \\ ryanlabouve/hello-node:v2 curl localhost:3000 # => test
Talking between containers
Docker compose allows us to boot up multiple services from one configuration file (usually
Let’s setup a simple service with the
hello-node image we used last time.
# docker-compose.yml version: "3.7" services: tweedle-a: image: 'ryanlabouve/hello-node:v2' ports: - "3001:3000" environment: - MESSAGE="Hello form tweedle a" tweedle-b: image: 'ryanlabouve/hello-node:v2' ports: - "3002:3000" environment: - MESSAGE="Hello form tweedle b"
And now let’s boot them up and say hi.
docker-compose up # tweedle-a_1 | # tweedle-a_1 | > firstname.lastname@example.org prod /usr/src/app # tweedle-a_1 | > node index.js # tweedle-a_1 | # tweedle-b_1 | # tweedle-b_1 | > email@example.com prod /usr/src/app # tweedle-b_1 | > node index.js # tweedle-b_1 | # tweedle-a_1 | Example app listening at <http://localhost:3000> # tweedle-b_1 | Example app listening at <http://localhost:3000> curl <http://localhost:3001> # => tweedle a curl <http://localhost:3002> # => tweedle b
And very similar to
docker ps, we have commands we can use to inspect docker compose:
Which would output
Communication with private / internal services
Let’s assume these two services need to talk to each other, ideally within the isolated confines of the Docker network.
Let’s start by shelling in to one of our running containers:
docker exec -it 918618a84cd3 /bin/bash # use `docker ps` to grab the id
So let’s recall, from outside this is exposed as
localhost:3001. Internally though it’s
localhost:3000. If you weren’t sure you could check out our previous post about how to discover this.
We can verify this via curl:
apt-get update apt-get install curl curl localhost:3001 # => unreachable curl localhost:3000 # => Test message
Next we need to reach the other box. To understand how, we’ll exit the bash session inside the container and inspect the Docker network that the containers are attaching to.
If we use
docker network ls to investigate the docker network these services are using we’ll see that it’s a named bridge network.
a bridge network uses a software bridge which allows containers connected to the same bridge network to communicate
And looking deeper, we can use
docker network inspect talking-with-docker-compose-containers_default
So based on the Docker docs we see that “each container for a service joins the default network and is both reachable by other containers on that network, and discoverable by them at a hostname identical to the container name”
This means is we shell into the boxes, we can access either other by their respective container name. e.g.
apt-get update apt-get install curl curl tweedle-a:3000 # => Hello from tweedle a curl tweedle-b:3000 # => Hello from tweedle b
Bonus: Inspecting a docker network from the inside
This is not strictly necessary to know to solve our original problem of communicating between multiple Docker Compose containers, but it’s a fun exercise to use more vanilla linux tools to poke around.
Scanning the network with NMAP
Let’s start by using
nmap to scan around the network.
apt-get install nmap # grab the current boxes IP ip addr show # => 172.17.0.3
nmap is installed and know the containers ip, we can knock around the network using. We’ll end our known ip with a
/24. This is called “CIDR Notation” and is a way to specify which IP’s we want to scan. In this case it’s everything on
XXX is 0-255 (i.e. 2^24).
nmap -sP 172.18.0.3/24
We are scanning from tweedle-a and can see tweedle-b on our local network. If we hit it with a port scan we should see 3000 open.
nmap -v -A 172.18.0.2
Here’s some fun information I’ve extracted from that output:
# Discovered open port 3000/tcp on 127.0.0.1 # ... # 3000/tcp open http Node.js (Express middleware) # | http-methods: # |_ Supported Methods: GET HEAD POST OPTIONS #|_http-title: Site doesn't have a title (text/html; charset=utf-8).
More info about nmap here: https://nmap.org/book/man.html
There’s so many other entertaining networking topics we could dive into now, but we’ll wrap the post here. Stay tuned for our next post on learning Docker!