In my previous post Add HTTPS support for your app backend API in 5 mins with traefik as a reverse proxy, I demonstrated how to deploy a service with docker-compose and Traefik. But docker-compose is really meant to be use in development, not production, although it still works decently for small apps.
So in this post, I will show you how to make some modifications to the docker-compose file so you can deploy your services with docker swarm mode, which supports multiple instances of docker containers running on different machines and scales better for larger apps.
I recommend read my previous post and get you app running with docker-compose before reading this post.
Prerequisites
- everything used in the previous post
- make sure you have a folder name
letsencrypt
- if you haven't enabled swarm mode, run
docker swarm init
Update docker-compose
Add a network at the bottom of the file. This is necessary. For some reason, the default network doesn't seem to work in swarm mode.
+ networks:
+ overlay:
Under command
of the service traefik
, add
traefik:
image: "traefik:v2.2"
container_name: "traefik"
command:
#- "--log.level=DEBUG"
- "--api.insecure=true"
- "--providers.docker=true"
+ - "--providers.docker.swarmMode=true"
- "--providers.docker.exposedbydefault=false"
- "--entrypoints.web.address=:80"
- "--entrypoints.websecure.address=:443"
- "--certificatesresolvers.myresolver.acme.httpchallenge=true"
- "--certificatesresolvers.myresolver.acme.httpchallenge.entrypoint=web"
# - "--certificatesresolvers.myresolver.acme.caserver=https://acme-staging-v02.api.letsencrypt.org/directory"
- "--certificatesresolvers.myresolver.acme.email=johndoe@gmail.com"
- "--certificatesresolvers.myresolver.acme.storage=/letsencrypt/acme.json"
Under the networks
, add
+ networks:
+ - overlay:
This will put traefik
on the overlay
network, allowing traefik
to communicate with whatever services you're running, which we will put on overlay
as well.
Finally, add deploy
under traefik
. Note that traefik
needs to be put on a manager node on the swarm, thus the constraint node.role == manager
. And we only want 1 traefik
instance on the swarm, so we're using global
mode. Learn more here
+ deploy:
+ mode: global
+ placement:
+ constraints:
+ - node.role == manager
+ update_config:
+ parallelism: 1
+ delay: 10s
+ restart_policy:
+ condition: on-failure
That's it for the traefik
service! Now let's handle your api service. Remember to replace example.com with your domain name. In this part we
- put your api service on
overlay
- add the
deploy
section to specify configs for deployment
api:
+ networks:
+ - overlay
+ deploy:
+ replicas: 1
+ update_config:
+ parallelism: 2
+ delay: 10s
+ restart_policy:
+ condition: on-failure
+ labels:
+ - "traefik.enable=true"
+ - "traefik.http.routers.api.rule=Host(`example.com`)"
+ - "traefik.http.routers.api.entrypoints=websecure"
+ - "traefik.http.routers.api.tls.certresolver=myresolver"
+ - "traefik.http.services.api.loadbalancer.server.port=80"
+ - "traefik.docker.network=api_overlay"
You've probably noticed the labels are very similar to the old laybels we add to the container in the previous post. The reason is that when using swarm mode, traefik
loads from service labels instead of container labels, so the labels we set for the container will NOT be read by traefik
.
Also notice that we added - "traefik.http.services.api.loadbalancer.server.port=80"
This tells traefik
which port it should use to communicate with api
.(the port your docker container exposes). We didn't need to specify the port when using docker-compose because traefik
automatically detects the port. However, in swarm, you have to explicitly tell traefik
which port to use.
- "traefik.docker.network=biased-api_overlay"
on the other hand, tells traefik
which network to use to communicate with the service. Make sure to prefix the network name, in this case, overlay
, with the name of your stack.(You can specify your stack name when you run the docker stack deploy
command later)
Final Result
version: "3.3"
services:
traefik:
image: "traefik:v2.2"
container_name: "traefik"
command:
#- "--log.level=DEBUG"
- "--api.insecure=true"
- "--providers.docker=true"
+ - "--providers.docker.swarmMode=true"
- "--providers.docker.exposedbydefault=false"
- "--entrypoints.web.address=:80"
- "--entrypoints.websecure.address=:443"
- "--certificatesresolvers.myresolver.acme.httpchallenge=true"
- "--certificatesresolvers.myresolver.acme.httpchallenge.entrypoint=web"
# - "--certificatesresolvers.myresolver.acme.caserver=https://acme-staging-v02.api.letsencrypt.org/directory"
- "--certificatesresolvers.myresolver.acme.email=johndoe@gmail.com"
- "--certificatesresolvers.myresolver.acme.storage=/letsencrypt/acme.json"
ports:
- "80:80"
- "443:443"
- "8080:8080"
volumes:
- "./letsencrypt:/letsencrypt"
- "/var/run/docker.sock:/var/run/docker.sock:ro"
+ networks:
+ - overlay
+ deploy:
+ mode: global
+ placement:
+ constraints:
+ - node.role == manager
+ update_config:
+ parallelism: 1
+ delay: 10s
+ restart_policy:
+ condition: on-failure
api:
image: "jamesku/api"
container_name: "api"
ports:
- "5001:80"
labels:
- "traefik.enable=true"
- "traefik.http.routers.api.rule=Host(`example.com`)"
- "traefik.http.routers.api.entrypoints=websecure"
- "traefik.http.routers.api.tls.certresolver=myresolver"
+ networks:
+ - overlay
+ deploy:
+ replicas: 1
+ update_config:
+ parallelism: 2
+ delay: 10s
+ restart_policy:
+ condition: on-failure
+ labels:
+ - "traefik.enable=true"
+ - "traefik.http.routers.api.rule=Host(`example.com`)"
+ - "traefik.http.routers.api.entrypoints=websecure"
+ - "traefik.http.routers.api.tls.certresolver=myresolver"
+ - "traefik.http.services.api.loadbalancer.server.port=80"
+ - "traefik.docker.network=api_overlay"
+networks:
+ overlay:
Deployment
Now that everything is ready, we can now run the deploy command.
- deploy
docker stack deploy -c docker-compose.yml <stack name>
- check the status of the stack
docker stack ps <stack name>
- list services
docker service ls
If you want to stop your service, run docker stack rm <stack name>
That's it. You've successfully deployed your app with docker swarm. You can easily scale it by adding more nodes to your swarm and changing the replicas
parameter.