I want to talk a little bit about Docker Compose, networking, and services calling each other. This is a super regular scenario that ‘ve already talked/posted/written, but I keep these post mostly for myself? if that makes sense.
So, from the beginning;
A docker-compose
file, there is service-a
(the main service) and service-b
. I need service-a
to ping to service-b
.
And service-a
has an environment variable to define the URL for service-b
.
The happy case works right out of the box; service-a
can reach http://service-b:1080
why? cause there is a default Docker network configuration (I pretend that this is already written in my compose file)
networks:
default:
driver: bridge
Which means:
- Each service you define in the compose joins the network tagged
default
(its named like{folder-name}_default
). - Each service is automatically assigned automatically an IP address (like
172.x.x.x
). - key thing:
service-a
can reachservice-b
by name cause this network already has a DNS resolution built-in
(I made up these IPs):
# DNS resolution example inside the Docker network
service-a -> 172.18.0.1:8080
service-b -> 172.18.0.2:1080
Quick note!
Inside the Docker network they can call each other using these names… but from your machine you can’t use these names.
It’s like in Zootopia.
In Zootopia, a bunny can call another bunny "cute," but when other animals do it, it's a little… well, you know what I mean.
This is the same, you computer can’t use the name alias referring to service-a, it needs to use localhost and the forwarded port, that’s why we do this forwarding port thing like 4080:1080
Anyway, this isn’t what I want to focus 👯,
because my particular use case has an additional key requirement:
service-a
can not use http://service-b:1080
(there was an explicit code guard) and NEEDED an actual ip-like address.
So, yay, easy right? just create an alias:
service-b:
image: ...
networks:
default:
ports:
- 4080:1080
+ aliases:
+ - service-b.local
So instead of
- http://service-b:1080
+ http://service-b.local:1080
Except this needs an extra step.
Remember that DNS mapping we have by default? we neet to create a subnet and assign the IPs to each service manually….
…maybe there’s a more ‘DevOps-ish’ way to do this? but I find statically assigning the IPs pretty straightforward, im opened to alternatives:
services:
service-a:
image: ...
+ networks:
+ default:
+ ipv4_address: 172.21.0.10
environment:
SERVICE_B_URL: http://service-b:1080
service-b:
image: ...
networks:
default:
+ ipv4_address: 172.21.0.20
aliases:
- service-b.local
ports:
- 4080:1080
+ networks:
+ default:
+ ipam:
+ driver: default
+ config:
+ - subnet: 172.21.0.0/24
IPAM means “Ey Docker, Im to assign manually the IPs”; The subnet part: 172.21.0.0/24
is creating a range of IPs between:
172.21.0.1
,172.21.0.2
,172.21.0.3
…172.21.0.254
And I assign each service… honestly, somewhat random (.0.10
, 0.20
).
Just one thing left, the DNS; we need to tell service-a
to map the name and the IP:
service-a:
image: ...
networks:
default:
ipv4_address: 172.21.0.10
+ extra_hosts:
+ - "service-b.local:172.21.0.20"
environment:
SERVICE_B_URL: http://service-b:1080
And that’s it. Happy ending. service-a
can now reach service-b
using the name service-b.local
, and everything works inside the Docker network.
Top comments (0)