Article Image
read

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.

Traefik

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'
Blog Logo

Christopher LaPointe


Published

Image

Chris LaPointe

Another site of Code

Back to Overview