Traefik, Docker and dnsmasq to simplify container networking

Good tech adventures begin with some frustration, a necessity, or a requirement. That is the story of how I simplified the administration and entry of my native net purposes with the assistance of Traefik and dnsmasq. The reasoning applies simply as properly for a manufacturing server utilizing Docker.
My dev atmosphere consists of a rising variety of net purposes self-hosted on my laptop computer. Such purposes embody a number of web sites, instruments, editors, registries, … They use databases, REST APIs, or extra complicated backends. Take the instance of Supabase, the Docker Compose file consists of the Studio, the Kong API gateway, the authentication service, the REST service, the real-time service, the storage service, the meta service, and the PostgreSQL database.
The result’s a rising variety of containers began on my laptop computer, accessible at localhost
on varied ports. A few of them use the default ports and can’t run in parallel to keep away from conflicts. For instance, the 3000
and 8000
ports are frequent to numerous containers current on my machine. To avoid the difficulty, some containers use customized ports which I usually occur to neglect.
The answer is to create native domains that are straightforward to recollect and use an online proxy to route the requests to the right container. Traefik helps within the routing and the invention of these companies and dnsmasq offers a customized top-level area (pseudo-TLD) to entry them.
One other utilization of Traefik is a manufacturing server utilizing a number of Docker Compose information for varied web sites and net purposes. The containers talk inside an inside community and are uncovered by means of a proxy service, in our case applied with Caddy.
Downside description
Out of many, let’s take 3 net purposes operating domestically. All of them are managed with Docker Compose:
- Adaltas web site, 1 container, Gatsby-based static web site
- Alliage web site, 10 containers, Subsequent.js frontend, Node.js backend, and Supabase
- Penpot, 6 containers, Penpot frontend, backend companies plus Inbucket for e mail testing (private addition)
By default, these containers expose the next ports on localhost:
- Adaltas
8000
Gatsby server in dev mode9000
Gatsby service to serve a construct web site
- Alliage
3000
Subsequent.js web site each dev and construct mode3001
Node.js customized API3000
Supabase Studio5555
Supabase Meta8000
Kong HTTP8443
Kong HTTPS5432
PostgreSQL2500
Inbucket SMTP server9000
Inbucket Net interface1100
Inbucket POP3 server
- Penpot
2500
Inbucket SMTP server9000
Inbucket Net interface1100
Inbucket POP3 server9001
Penpot frontend
Word, relying in your atmosphere and wishes, some ports is likely to be restricted whereas different ports is likely to be accessible.
As you possibly can see, many ports collide with one another. It isn’t simply the two cases of Inbucket operating in parallel. For instance, port 8000
is used each by Gatsby and Kong. It’s a frequent default port for a number of purposes. The identical goes for ports 3000
, 8080
, 8443
, …
One resolution is to assign distinctive ports for every service. Nevertheless, this strategy will not be scalable. Quickly sufficient, I neglect to which port every service is assigned.
Anticipated conduct
A greater resolution is the utilization of a reverse proxy with hostnames straightforward to recollect. Here’s what we count on:
- Adaltas
www.adaltas.native
Gatsby server in dev modeconstruct.adaltas.native
Gatsby service to serve a construct web site
- Alliage
www.alliage.native
Subsequent.js web site each dev and construct modeapi.alliage.native
Node.js customized APIstudio.alliage.native
Supabase Studiometa.alliage.native
Supabase Metakong.alliage.native
Kong HTTPkong.alliage.native
Kong HTTPSsql.alliage.native
PostgreSQLsmtp.alliage.native
Inbucket SMTP servermail.alliage.native
Inbucket Net interfacepop3.alliage.native
Inbucket POP3 server
- Penpot
www.penpot.native
Penpot frontendsmtp.penpot.native
Inbucket SMTP servermail.penpot.native
Inbucket Net interfacepop3.penpot.native
Inbucket POP3 server
In a standard setting, the reverse proxy is configured with one or a number of configuration information with all of the routing data. Nevertheless, a central configuration will not be so handy. It’s preferable to have every service declares which hostname they resolve.
Automated routing registration
All my net companies are managed with Docker Compose. Ideally, I count on data to be current contained in the Docker Compose file. Traefik is cloud-native within the sense that it configures itself utilizing cloud-native workflows. The applying offers some directions current in its docker-compose.yml
file and the containers are robotically uncovered.
The way in which Traefik works with Docker, it plugs into the Docker socket, detects new companies, and creates the routes for you.
Beginning Traefik
To start out Traefik inside Docker is easy (by no means say straightforward). The docker-compose.yml
file is:
model: '3'
companies:
reverse-proxy:
picture: traefik:v2.9
command: --api.insecure=true --suppliers.docker
ports:
- "80:80"
- "8080:8080"
volumes:
- /var/run/docker.sock:/var/run/docker.sock
Registering new companies
Let’s take into account a further service. The Adaltas web site is a single container based mostly on Gatsby. In improvement mode, it begins an online server on port 8000
. I count on it to be accessible with the hostname www.adaltas.native
on port 80
.
Following the Traefik’s getting began with Docker, the mixing is made with the property traefik.http.routers.router_name.rule
current within the labels
area of the docker service. It defines the hostname underneath which our web site is accessible on port 80
. It’s set to www.adaltas.localhost
as a result of the .localhost
TLD resolves domestically by default. Since I favor to make use of the .native
area, we set the area to www.adaltas.native
later utilizing dnsmasq. The site visitors is then routed to the container IP on port 8000. The container port is obtained by Traefik from the Docker Compose’s ports
area.
model: '3'
companies:
www:
container_name: adaltas-www
...
labels:
- "traefik.http.routers.adaltas-www.rule=Host(`www.adaltas.localhost`)"
ports:
- "8000:8000"
This works when each the Traefik and the Adaltas companies are outlined in the identical Docker compose file. Firing docker-compose up
and you’ll:
http://localhost:8080
: Entry the Traefik net UIhttp://localhost:8080/api/rawdata
: Entry the Traefik’s API rawdatahttp://www.adaltas.localhost
: Entry the Adaltas web site in improvement modehttp://localhost:8080
: Similar ashttp://www.adaltas.localhost
There are 3 limitations we have to take care of:
- Inside networking
It solely works as a result of all of the companies are declared inside the identical Docker Compose file. With separated Docker Compose information, an inside community should be used to speak between the Traefic container and the targetted containers. - Area identify
I want to use a pseudo top-level area (TLD), for instance,www.adaltas.native
as a substitute ofwww.adaltas.localhost
. The.native
TLD doesn’t but resolve domestically, an area DNS server should be configured. - Port label
The port of Adaltas is outlined contained in the Docker Compose file. Thus, it’s uncovered on the host machine and it collides with different companies. Port forwarding should be disabled and Traefik should be instructed concerning the port with one other mechanism than theports
area.
Inside networking
When outlined throughout separated information, the container can not talk. Every Docker Compose file generates a devoted community. The focused service is seen contained in the Traefik UI. Nevertheless, the request fails to be routed.
The containers should share a standard community to speak. When the Traefik container is began, a traefik_default
community is created, see docker community checklist
. As an alternative of making a brand new community, let’s reuse it. Enrich the Docker Compose file of the targetted container, the Adaltas web site in our case, with the community
area:
model: '3'
companies:
www:
container_name: adaltas-www
networks: default: identify: traefik_default
After beginning the two Docker Compose setups with docker-compose up
, the Traefik and the Web site containers begin speaking.
Area identify
It’s time to sort out the FQDN of our companies. The present TLD in use, .localhost
, is completely high quality. It really works by default and it’s formally reserved for this utilization. Nevertheless, I want to use my very own top-level domains (pseudo-TLD identify), we’ll use .native
on this instance.
Disclaimer, utilizing a pseudo-TLD identify will not be really useful. The
.native
TLD is utilized by multicast DNS / zero-configuration networking. In follow, I haven’t encountered any points. To mitigate the chance of conflicts, RFC 2606 reserves the next TLD names:.take a look at
,.instance
,.invalid
,.localhost
.
A neighborhood DNS server is used to resolve the *.native
addresses. I had some expertise with Bind previously. A less complicated and extra light-weight possibility is the utilization of dnsmasq. The directions under cowl the set up on MacOS and Ubuntu Desktop. In each instances, dnsmaq is put in and configured to not intervene with the present DNS settings.
MacOS directions:
brew set up dnsmasq
mkdir -pv $(brew --prefix)/and many others/
echo 'handle=/.native/127.0.0.1' >> $(brew --prefix)/and many others/dnsmasq.conf
sudo brew companies begin dnsmasq
sudo mkdir -v /and many others/resolver
sudo bash -c 'echo "nameserver 127.0.0.1" > /and many others/resolver/take a look at'
scutil --dns
Linux directions with NetworkManager (eg Ubuntu Desktop):
systemctl disable systemd-resolved
systemctl cease systemd-resolved
unlink /and many others/resolv.conf
cat <<CONF | sudo tee /and many others/NetworkManager/conf.d/00-use-dnsmasq.conf
[main]
dns=dnsmasq
CONF
cat <<CONF | sudo tee /and many others/NetworkManager/dnsmasq.d/00-dns-public.conf
server=8.8.8.8
CONF
cat <<CONF | sudo tee /and many others/NetworkManager/dnsmasq.d/00-address-local.conf
handle=/.native/127.0.0.1
CONF
systemctl restart NetworkManager
Use dig
to validate that any FQDN utilizing our pseudo-TLD resolves to the native
machine:
Port label
With the introduction of a reverse proxy like Traefik, exposing the container port on the host machine is now not essential, thus, eliminating the chance of collision between the uncovered port and those of different companies.
One label is already current to outline the hostname of the web site service. Traefik comes with numerous complementary labels. The traefik.http.companies.service_name.loadbalancer.server.port
property tells Traefik to make use of a particular port to connect with a container.
The ultimate Docker Compose file seems to be like this:
model: '3'
companies:
www:
container_name: adaltas-www
picture: node:18
volumes:
- .:/app
consumer: node
working_dir: /app
command: bash -c "yarn set up && yarn run develop"
labels:
- "traefik.http.routers.adaltas-www.rule=Host(`www.adaltas.native`)"
- "traefik.http.companies.adaltas-www.loadbalancer.server.port=8000"
networks:
default:
identify: traefik_default
Conclusion
With Traefik, I like the concept of my container companies registering robotically in a cloud-native philosophy. It offered me with confort and ease. Additionally, dnsmasq has proved to be well-documented and fast to regulate to my varied necessities.