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
Docker compose allows us to boot up multiple services from one configuration file (usually docker-compose.yml
).
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 | > hello-node@1.0.0 prod /usr/src/app
# tweedle-a_1 | > node index.js
# tweedle-a_1 |
# tweedle-b_1 |
# tweedle-b_1 | > hello-node@1.0.0 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:
docker-compose ps
Which would output
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
https://docs.docker.com/network/bridge/
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. curl tweedle-a:3001
.
apt-get update
apt-get install curl
curl tweedle-a:3000
# => Hello from tweedle a
curl tweedle-b:3000
# => Hello from tweedle b
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.
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
Now that 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 [172.18.0.XXX](<http://172.18.0.XXX>)
where 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!