Feel free to raise any issue or PR
- Motivation
- Requirements
- Configurations required
- Start the service
- Configure backend services
- Socket activation
It’s common for developers to work in more than one project at the same time and using different tools, which usually means different ports, if those ports implement the HTTP protocol, then there is no need to remember the ports number, and it could be accessed using DNS.
This repository is a showcase to simplify this access.
- Docker engine or podman
- docker-compose
- Use a local DNS
In order to resolve any subdomain, is possible to use dnsmasq.
address=/docker/172.17.0.1
and restart the service:
systemctl restart NetworkManager
Only the first time it is required to create the network
make network
Then running make start it’s enough. As it’s the service will be always working, if you don’t want it to happen, please modify the restart configuration in docker-compose.yml
make start
Check that you can access to http://proxy.docker
With that working adding the following configurations to a service in a docker-compose:
labels: traefik.enable: "true" traefik.http.routers.service-name.rule: "Host(`example.docker`)" traefik.http.services.service-name.loadbalancer.server.port: "<example-application-port>" traefik.webservice.service-name.entryPoints: http,ws traefik.http.routers.service-name.tls: "false" traefik.docker.network: lb networks: lb: aliases: - ${SERVICE_NAME}.${DOMAIN}
Please be sure to modify service-name to avoid duplications, the domain ‘example.docker and also set the right port number on <example-application-port>
Then at the bottom of the docker-compose, you have to declare the access to the network:
networks: lb: name: lb external: true
With that in place and after you start the backend service, you will be able to access it through example.docker
The idea of socket activation is to not waste resources, so you start them only when you need it. While for the first attempt it might take a little bit to respond, I think it has a good compensation, it’s like a sustainability concept. To not use more than needed.
Nowadays it’s common that GNU/Linux operative systems use Systemd. I will leave it for you to learn it, if you are interested.
So we will have 3 parts, It could be done in 2 if the service that you are trying to run is able to receive a socket as a parameter, and altough it’s becoming more common, explainig a general solution for those services that don’t make the idea more complete.
On this example I will be using TCP/IP sockets, but you can choose file sockets as well.
- The Socket service.
It’s not a service per se, but a definition on where do we want systemd to be
monitoring or waiting for incomming communication.
[Unit] Description=Socket for EXAMPLE [Socket] ListenStream=GLOBAL_IP:GLOBAL_PORT Transparent=True [Install] WantedBy=sockets.target
- The Service for the socket, or proxy service.
Usually, if not defined on the socket definition, once a message is received,
the socket will try to start a service with the same name as the socket
definition, and pass the listening socket to the service.
Since not all the services are prepared, as I mention before, here we will use
a proxy service, that it’s able to direct the communication in the right path.
So this will start the objective service, and deliver the messages to it.
[Unit] Description=Intermediate proxy socket for EXAMPLE Requires=EXAMPLE-container.service After=EXAMPLE-container.service Requires=EXAMPLE.socket After=EXAMPLE.socket [Service] EnvironmentFile=SERVICE_PATH/.env ExecStart=/usr/lib/systemd/systemd-socket-proxyd --exit-idle-time="10m" ${SERVICE_IP}:${PORT_EXAMPLE} NonBlocking=true
- The containerized service
This is the actual service that we want to run.
[Unit] Description=EXAMPLE container [Service] EnvironmentFile=SERVICE_PATH/.env # Use docker or podman, as you prefer it WorkingDirectory=SERVICE_PATH ExecStart=make start-service c=EXAMPLE ExecStop=make stop-service c=EXAMPLE [Install] WantedBy=multi-user.target
To summarize the idea this sequence diagram might help:
Note: To make this work with TCP/IP, it’s required to use 2 different IP addresses, if you want to use the same port, or either use differnt port. I prefere different IPs because then I can have a proxy for docker and another for podman.
From theory to making it works requires an extra effort, and because I don’t like to waste time neither, you can just define your preferences on the .env file and run:
make install-sockets