After putting off the work for a year or so, I finally went through the effort to migrate my docker swarm to Traefik v2. There isn't anything by default that I needed, but I intended to migrate eventually, and it has some new features like TCP and UDP forwarding and SNI.
Migrating to Traefik v2 in Docker Swarm
As a reminder, you can find the original post here that describes my docker swarm setup. This replaces the original traefik stack in that article.
Notes on Migration
Albeit somewhat annoying, the traefik migration is mostly an exercise in understanding the new concepts (Router, service, middleware), and then
mapping your old services to use the new syntax. This was mostly a matter of things like replacing traefik.frontend.rule=Host:whoami.${HOST}
with things like traefik.http.routers.whoami.rule=Host(`whoami.${HOST}`)
Traefik does a decent job at documenting all these changes here
The last thing to note is that I assume I want all services that I expose through traefik to automatically get TLS (https). I set this in the defaults for the traefik service so that I don't have to set it in each service separately.
Docker Swarm Services
As before, please note there are a few environment variables that need setting here, namely,
HOST
(as your default host), ACME_EMAIL
, and GLOBAL_HTPASSWD
to secure traefik.
GLOBAL_HTPASSWD
takes the htpasswd format of username:$apr1$1OVn3aYo$DprEtRhEy37LNhnEXg5st/
(the password in this case is "test"). Use openssl passwd -apr1
to generate.
version: "3.5"
services:
traefik:
image: traefik:v2.4
command:
- --api.dashboard=true
- --metrics.prometheus
# Docker
- --providers.docker=true
- --providers.docker.swarmMode=true
- --providers.docker.defaultRule=Host(`.${HOST}`) # Similar to the old --domain flag, this sets the default name for services
- --providers.docker.exposedByDefault=false
# Entrypoints
- --entrypoints.web.address=:80
- --entrypoints.websecure.address=:443
# Certs
- --certificatesresolvers.le.acme.httpchallenge=true
- --certificatesresolvers.le.acme.httpchallenge.entrypoint=web
- --certificatesresolvers.le.acme.email=${ACME_EMAIL}
- --certificatesresolvers.le.acme.storage=/letsencrypt/acme.json # Store in volume
# Auto TLS
- --entrypoints.websecure.http.tls=true
- --entrypoints.websecure.http.tls.certresolver=le
# Auto redirection
- --entrypoints.web.http.redirections.entrypoint.to=websecure
- --entrypoints.web.http.redirections.entrypoint.scheme=https
networks:
- traefik-net
ports:
- "80:80"
- "443:443"
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- letsencrypt:/letsencrypt
deploy:
mode: global
placement:
constraints: [node.role==manager] # Pin to your manager node (also makes the volume only in 1 spot, assuming you have 1 manager)
update_config:
parallelism: 1
delay: 10s
restart_policy:
condition: on-failure
labels: # Self-expose traefik dashboard behind password. Use `traefik.${HOST}`
- "traefik.enable=true"
- "traefik.docker.network=traefik-net"
- "traefik.http.routers.traefik-dash.rule=Host(`traefik.${HOST}`)"
- "traefik.http.routers.traefik-dash.entrypoints=websecure"
- "traefik.http.routers.traefik-dash.service=api@internal"
- "traefik.http.routers.traefik-dash.middlewares=traefik-auth"
- "traefik.http.middlewares.traefik-auth.basicauth.users=${GLOBAL_HTPASSWD}"
- "traefik.http.services.traefik-dash.loadbalancer.server.port=8080"
resources:
limits: { cpus: '0.5', memory: '64M' }
reservations: { cpus: '0.1', memory: '32M' }
# Bonus cleanup job to clean old containers, images, and networks once a day on all nodes
swarm-cleanup:
image: docker
command: |
sh -c "
docker container prune -f --filter 'until=36h' &&
docker image prune -f -a --filter 'until=24h' &&
docker network prune -f --filter 'until=24h'
"
volumes:
- /var/run/docker.sock:/var/run/docker.sock
deploy:
mode: global
restart_policy:
delay: 24h
resources:
limits: { cpus: '0.1', memory: '32M' }
reservations: { cpus: '0.025', memory: '16M' }
networks:
# This is the network your other services will use
traefik-net:
driver: overlay
name: 'traefik-net'
attachable: true
volumes:
letsencrypt: {}
Test Service
This is a simple service that will expose the traefik/whoami
web service (Just spits out some information about the container)
on whoami.${HOST}
.
version: "3.3"
services:
whoami:
image: traefik/whoami
networks:
- traefik-net
deploy:
replicas: 2
labels: # Make sure to put labels on the deployment, not the container
- "traefik.enable=true"
# Not stricly required, since there's only 1 network here. In more complex setups you might need this line
#- "traefik.docker.network=traefik-net"
- "traefik.http.routers.whoami.rule=Host(`whoami.${HOST}`)"
- "traefik.http.services.whoami.loadbalancer.server.port=80"
- "traefik.http.routers.whoami.entrypoints=websecure"
networks:
traefik-net:
external:
name: 'traefik-net'