This is part 2 of a 3-part blog series looking at Docker Swarm. We will be picking up from where we left off in the previous article. In the previous article, we were able to run our application using Docker Swarm, but we weren’t able to use our application. We need to add a load balancer to make it work. In production, we will be running multiple replicas of a single service to handle the load. Hence it is important to integrate a load balancer into the cluster so that the incoming requests are distributed between the replicas. In this article, we will add the Traefik load balancer for redirecting requests to our service.
Since we are picking from where we left off in the previous article, we are assuming that you can run Docker swarm. Please check this Github repo in case you haven’t set up the project.
We need to add a load balancer or web server to redirect the incoming requests to the service. Some of the popular options are Nginx and Traefik. In this article, we will be using Traefik.
Traefik is an open-source load balancer and reverse proxy. The following are some of the features of Traefik:
In this article, we will be using both static and dynamic configurations.
Let’s add a new service to our docker-compose.yml file called Traefik to integrate Traefik into the stack. Paste the following under the services section.
traefik:
image: traefik:v2.10.7
deploy:
mode: global
networks:
- sample-net
ports:
- target: 80
published: 3000
protocol: tcp
mode: host
volumes:
- ./traefik.yml:/etc/traefik/traefik.yml
- "/var/run/docker.sock:/var/run/docker.sock:ro"
Let’s go through the service:
image: traefik:v2.10.7
We are using the image of Traefik.
deploy:
mode: global
This will instruct docker to deploy it globally so that even if we have multiple nodes, the Traefik service can redirect requests across multiple nodes in the cluster.
networks:
— sample-net
We are instructing docker to deploy the service in a network called sample-net.
ports:
— target: 80
published: 3000
protocol: tcp
mode: host
We instruct Docker to listen to port 3000 in the host machine (Meaning port 3000 is exposed to access this service). All incoming requests from port 3000 from outside the container will be redirected to port 80 inside the docker container. Then we have mentioned the protocol and the mode.
volumes:
— ./traefik.yml:/etc/traefik/traefik.yml
— “/var/run/docker.sock:/var/run/docker.sock:ro”
Under volumes, we are loading the static configurations (We will add the static configuration next) into the container and listening to docker events for dynamic configurations.
The static configuration will be used when the Traefik service starts. Create a new file called traefik.yml and paste the following code into it:
providers:
docker:
swarmMode: true
network: sample-net
exposedbydefault: false
log:
level: DEBUG
entryPoints:
web:
address: :80
providers:
docker:
swarmMode: true
network: sample-net
exposedbydefault: false
Since we are using docker we need to specify the configuration under the docker section. We need to enable swarm mode and specify the docker network. exposedbydefault: false tells Traefik to listen for dynamic configurations
log:
level: DEBUG
This will enable logs in Traefik.
entryPoints:
web:
address: :80
EntryPoints are the network entry points into Traefik. They define the port that will receive the packets.
The dynamic configuration for Traefik can be added under the labels, or commands section of the service defined in the docker-compose.yml file. We need to redirect requests from Traefik to our nodeapp service, hence we will add it under it. Paste the following code under the deploy section of the nodeapp service.
labels:
- "traefik.enable=true"
- "traefik.http.routers.api-redirect.service=nodeapp-service"
- "traefik.http.routers.api-redirect.entryPoints=web"
- "traefik.http.routers.api-redirect.rule=PathPrefix(`/`)"
-"traefik.http.services.nodeapp-service.loadBalancer.server.port=3000"
Let’s go through the above configuration:
traefik.enable=true
We have enabled Traefik for this service.
t-traefik.http.routers.api-redirect.service=nodeapp-service
Then we are adding our custom route called api-redirect under http.routes section. We are instructing Traefik to redirect incoming requests to a Traefik service called nodeapp-service. The important point to note here is Traefik service is different from the services (nodeapp is a service) that we mention in the docker-compose.yml file.
traefik.http.routers.api-redirect.entryPoints=web
We have mentioned the EntryPoint as web which is listening to port 80.
traefik.http.routers.api-redirect.rule=PathPrefix(`/`)
This is the rule through which Traefik will redirect incoming requests. Since nodeapp is the only service we have in our stack we are telling Traefik to send all requests to this service. PathPrefix, as the name says is a rule for filtering requests that starts with a certain pathname.
traefik.http.services.nodeapp-service.loadBalancer.server.port=3000
Under the services section, we have a service called nodeapp-service . Under this service, we have specified the port number to redirect the incoming request to. This will connect with the nodeapp which running in port 3000.
There are other ways to add the dynamic configurations you can check the Traefik documentation for more details. The final docker-compose.yml file will look like the following:
version: "3.9"
services:
traefik:
image: traefik:v2.10.7
deploy:
mode: global
networks:
- sample-net
ports:
- target: 80
published: 3000
protocol: tcp
mode: host
volumes:
- ./traefik.yml:/etc/traefik/traefik.yml
- "/var/run/docker.sock:/var/run/docker.sock:ro"
nodeapp:
image: 127.0.0.1:5000/nodeapp
networks: # <-------- add 'sample-net' network
- sample-net
deploy:
replicas: 1
restart_policy:
condition: any
update_config:
parallelism: 1
delay: 15s
labels:
- "traefik.enable=true"
- "traefik.http.routers.api-redirect.service=nodeapp-service"
- "traefik.http.routers.api-redirect.entryPoints=web"
- "traefik.http.routers.api-redirect.rule=PathPrefix(`/`)"
- "traefik.http.services.nodeapp-service.loadBalancer.server.port=3000"
build:
context: .
args:
NODE_ENV: production
command: ["npm", "run", "start"]
networks: # <-------- initialize the network
sample-net:
Notice that we have removed the ports section from the nodeapp service.
networks:
— sample-net
We have added nodeapp to the same network where Traefik is running so that they can communicate.
networks:
sample-net:
At the bottom of the file, we have initialized sample-net under the network section.
After enabling Docker swarm mode (We did this in the previous article), we can deploy the stack once again with the Traefik service using the following command:
$ docker stack deploy -c docker-compose.yml node_stack
Ignoring unsupported options: build
Creating network node_stack_sample-net
Creating service node_stack_traefik
Creating service node_stack_nodeapp
You will notice the Traefik service is also running now. If you access. nodeapp in your browser, it should work the same as before. The text “Hello world!” will be visible.
Cheers! you have successfully deployed your NodeJS app using the Traefik load balancer. To see the deployment logs in the Traefik service you can use the following command:
docker logs
In the next article, we will learn how to Scale our application, perform rolling updates, and more.
You can refer to the entire source code for this article in this GitHub repo.
Let's collaborate to turn your business challenges into AI-powered success stories.
Get Started