Configuration differences between traefik v2 and traefik v1 (with the docker provider)
24 Feb 2020Introduction
Traefik v2 is out! It has already been released for a few months but it’s never too late to jump in.
This new version is quite a big one. It broke all my existing configurations and got myself all over the new documentation. As it was a bit of a pain for me, here is a tutorial with what I learned when migrating from traefik v1 to v2.
What’s new
I will try to be as concise as possible and sum up what’s new (this might come with some shortcuts). What’s written below should be considered only when using the docker provider.
Static vs dynamic configuration
The first things that lost me at the beginning (but which is very important nevertheless) is the difference between static and dynamic configuration.
- Static configuration refers to configuration which is loaded on startup. When using docker, this configuration can be loaded from either a .toml file or from flags passed on the traefik container (in the command directive). For instance, entrypoints must be defined at the very beginning and are part of the static configuration.
- Dynamic configuration refers to any configuration that traefik watches. Traefik does not have to restart to apply it. With the docker provider, this configuration could come from .toml files or from docker labels. In my opinion it is very easy to get confused with .toml files (detecting whether it’s dynamic or static is not obvious). As such I prefer to write every configuration in labels and flags. Plus this way, everything can be found in docker-compose files. Services are an example of what can be configured dynamically. They can scale which is a good indicator that they are part of what can be dynamically configured. Middleware can be added or removed on the fly ; they are also part of the dynamic configuration.
Providers, router, services and middleware
New concepts in a few words:
- providers are infrastructure components (in this case docker and/or docker swarm) leveraged by traefik to handle service discovery
- routers treat requests according to some rules
- services serve the request
- middleware modify the request
With this in mind, you will need to define routers attached to entrypoints, then attach middleware to these routers (middleware are anything between the entrypoint and the service ; basic authentication, redirections, etc.)
With the docker provider, you (almost) do not have to take care of services as treafik automatically creates and assigns these services to the router. The only thing you have to do within a swarm is to specify which container port to target.
Setup traefik
The dashboard
Let’s create a docker-compose.yml file with the following content. You will find configuration for both traefik version 1.7 and version 2.1 so it’s easy to compare and find differences.
-
version: '3.7' services: traefik: image: traefik:v2.1 ports: - 80:80 command: - --entrypoints.http.address=:80 - --providers.docker.exposedByDefault=false - --log.level=DEBUG - --api=true - --api.dashboard=true labels: traefik.enable: 'true' traefik.http.routers.traefik-dashboard-http-router.entrypoints: http traefik.http.routers.traefik-dashboard-http-router.rule: Host(`traefik-ui.local`) traefik.http.routers.traefik-dashboard-http-router.service: api@internal volumes: - /var/run/docker.sock:/var/run/docker.sock
-
version: '3.7' services: traefik: image: traefik:v1.7 ports: - 80:80 command: - --entrypoints=Name:http Address::80 - --docker - --docker.exposedbydefault=false - --logLevel=DEBUG - --api labels: traefik.port: 8080 traefik.frontend.rule: Host:traefik-ui.local traefik.enable: 'true' volumes: - /var/run/docker.sock:/var/run/docker.sock
Many lines could be defaulted, but this way you do not rely on traefik “magic” and we know what we are doing.
This configuration sets up traefik with its dashboard accessible in http on the standard port (80) on domain traefik-ui.local. You can add this entry to your local dns (/etc/hosts on linux) to try it. Containers are not enabled by default, so we have to explicitly set the label traefik.enable to true to let traefik handle the traffic between internet and this container.
What about api@internal? Well I don’t know for now which is why I created a post on the community forum.
Run docker-compose up
and open http://traefik-ui.local/ to access the dashboard.
Basic auth
You might not want to have this dashboard accessible from everyone. Let’s add basic authentication.
$ htpasswd -c file.htpasswd traefik_user
$ cat file.htpasswd
> traefik_user:$apr1$epoKf5li$QfTMJZOCS/halv3CiIUEu0
The above hashed password is password
In traefik v2, you need to create a basicauth middleware, and assign it to the router you want.
-
... labels: ... traefik.http.middlewares.traefik-dashboard-auth.basicauth.users: traefik_user:$$apr1$$epoKf5li$$QfTMJZOCS/halv3CiIUEu0 traefik.http.routers.traefik-dashboard-http-router.middlewares: traefik-dashboard-auth
-
... labels: ... traefik.frontend.auth.basic.users: traefik_user:$$apr1$$epoKf5li$$QfTMJZOCS/halv3CiIUEu0
Be careful, in yaml you have to double the ‘$’ signs to escape them.
Run docker-compose up
and refresh the dashboard to see the login form popping up.
🐳 github links (v2.1 and v1.7) to the full docker-compose files
Nginx backend container
Now let’s handle some traffic. In the same docker-compose.yml file, we create a nginx service:
If your backend containers and your traefik container are configured in separate docker-compose.yml files, make sure they have one network in common!
-
... services: traefik: ... nginx: image: nginx:1.17.8-alpine labels: traefik.enable: 'true' traefik.http.routers.nginx-http-router.entrypoints: http traefik.http.routers.nginx-http-router.rule: Host(`nginx.local`) traefik.http.services.nginx-service.loadbalancer.server.port: 80
-
... services: traefik: ... nginx: image: nginx:1.17.8-alpine labels: traefik.enable: 'true' traefik.frontend.rule: Host:nginx.local traefik.frontend.entryPoints: http traefik.port: 80
🐳 github links (v2.1 and v1.7) to the full docker-compose files
In details:
- traefik.enable tells traefik to handle service discovery on this container, as containers are not exposed by default.
- traefik use a router to route the traffic to our container. This router matches when http connections (entrypoints directive) are made towards nginx.local (rule directive). Add an entry in your /etc/hosts file to have this domain name resolving in local.
- traefik redirect traffic on port 80 of the container.
Https support
Currently, our configuration support only the http entrypoint. The following diagram pictures how http and https requests can be resolved using traefik new concepts. This will allow TLS termination along with http to https redirection on the dashboard.
Let’s update the configuration accordingly:
-
version: '3.7' services: traefik: image: traefik:v2.1 ports: - 80:80 - 443:443 command: - --entrypoints.http.address=:80 - --entrypoints.https.address=:443 - --providers.docker.exposedByDefault=false - --log.level=DEBUG - --api=true - --api.dashboard=true labels: traefik.enable: 'true' traefik.http.routers.traefik-dashboard-http-router.entrypoints: http traefik.http.routers.traefik-dashboard-http-router.rule: Host(`traefik-ui.local`) traefik.http.middlewares.traefik-dashboard-redirect.redirectscheme.scheme: https #1 traefik.http.routers.traefik-dashboard-http-router.middlewares: traefik-dashboard-redirect #2 traefik.http.middlewares.traefik-dashboard-auth.basicauth.users: traefik_user:$$apr1$$epoKf5li$$QfTMJZOCS/halv3CiIUEu0 traefik.http.routers.traefik-dashboard-https-router.middlewares: traefik-dashboard-auth #3 traefik.http.routers.traefik-dashboard-https-router.service: api@internal #4 traefik.http.routers.traefik-dashboard-https-router.entrypoints: https #5 traefik.http.routers.traefik-dashboard-https-router.tls: 'true' #6 traefik.http.routers.traefik-dashboard-https-router.rule: Host(`traefik-ui.local`) #7 traefik.http.services.traefik-dashboard-service.loadbalancer.server.port: 9999 #8 volumes: - /var/run/docker.sock:/var/run/docker.sock
-
version: '3.7' services: traefik: image: traefik:v1.7 ports: - 80:80 - 443:443 command: - --entrypoints=Name:http Address::80 - --entrypoints=Name:https Address::443 TLS - --docker - --docker.watch - --docker.exposedbydefault=false - --logLevel=DEBUG - --api labels: traefik.enable: 'true' traefik.port: 8080 traefik.frontend.rule: Host:traefik-ui.local traefik.frontend.auth.basic.users: traefik_user:$$apr1$$epoKf5li$$QfTMJZOCS/halv3CiIUEu0 traefik.frontend.redirect.entryPoint: https traefik.frontend.entryPoints: http,https volumes: - /var/run/docker.sock:/var/run/docker.sock
🐳 github links (v2.1 and v1.7) to the full docker-compose files
As you can see, configuring https with redirection on version v2.1 is way more verbose than on version 1.7. At a glance I thought it was unecessarily complicated, but now I think I prefer this new approach. Every line has its purpose, much less gets done under the hood without the user knowledge.
- The first thing we added here (#1) is the traefik-dashboard-redirect middleware. It’s a redirectscheme middelware, redirecting traffic to https.
- Then we attached this middleware to the traefik-dashboard-http-router router (#2)
- Now than http is redirected to https, it’s useless to protect the http route with basic authentication. We keep the labels related to basic authentication, but we attach the middleware to the traefik-dashboard-https-router router instead (#3).
- We also move the service api@internal from the http router to the https router (#4).
- We set the traefik-dashboard-https-router router entrypoint to https obviously (#5).
- Then we tell traefik that router traefik-dashboard-https-router will take care of tls termination (#6). Traefik will generate self-signed default certificates as we didn’t specify a way to resolve them.
- Our https router will match the exact same rule as the http one, i.e. traefik-ui.local domain name (#7)
- Finally we have to tell traefik to load balance the traffic on a dummy port. Without it, you will get an error such as ‘service "traefik-traefik-v2" error: port is missing”’. This part is applicable only for the dashboard as it uses an internal service (an issue is still opened here on this subject.
Nginx backend container with https
Following the same principles, we can handle tls termination and https redirection for the nginx backend. The configuration is very similar as the previous one - basic auth middleware being discarded.
Here is the resulting yaml:
-
... services: traefik: ... nginx: image: nginx:1.17.8-alpine labels: traefik.enable: 'true' traefik.http.services.nginx-service.loadbalancer.server.port: 80 traefik.http.middlewares.nginx-redirect.redirectscheme.scheme: https traefik.http.routers.nginx-http-router.entrypoints: http traefik.http.routers.nginx-http-router.rule: Host(`nginx.local`) traefik.http.routers.nginx-http-router.middlewares: nginx-redirect traefik.http.routers.nginx-https-router.entrypoints: https traefik.http.routers.nginx-https-router.tls: 'true' traefik.http.routers.nginx-https-router.rule: Host(`nginx.local`)
-
... services: traefik: ... nginx: image: nginx:1.17.8-alpine labels: traefik.enable: 'true' traefik.frontend.rule: Host:nginx.local traefik.frontend.entryPoints: http traefik.port: 80
🐳 github links (v2.1 and v1.7) to the full docker-compose files
Let’s encrypt support
Traefik can issue let’s encrypt certificates for you. You only have to choose how trust is going to be established via what is called a challenge.
- We define a certificate resolver on startup thanks to command flags
- Certificates will be stored in /etc/traefik/acme.json (a volume is defined to persist this file)
- The challenge used here will be the most common challenge: HTTP-01 challenge
- For each service reachable on a domain name for which we can generate a certificate, we set the name of certificate resolver to be used (letsencrypt_resolver here)
-
volumes: conf: ... services: traefik: ... command: ... - --certificatesResolvers.letsencrypt_resolver.acme.email=you@mail.com - --certificatesResolvers.letsencrypt_resolver.acme.storage=/etc/traefik/acme.json - --certificatesResolvers.letsencrypt_resolver.acme.httpChallenge.entryPoint=http labels: ... traefik.http.routers.traefik-dashboard-https-router.tls.certresolver: letsencrypt_resolver volumes: - conf:/etc/traefik nginx: image: nginx:1.17.8-alpine labels: ... traefik.http.routers.nginx-https-router.tls.certresolver: letsencrypt_resolver
-
volumes: conf: ... services: traefik: ... command: ... - --acme.email=you@mail.com - --acme.storage=/etc/traefik/acme.json - --acme.entryPoint=http - --acme.onHostRule=true - --acme.httpchallenge.entrypoint=http volumes: - conf:/etc/traefik
🐳 github links (v2.1 and v1.7) to the full docker-compose files
If migrating from traefik v1.7 to traefik v2, you will need to convert the old acme.json file to a new version using the traefik-migration-tool.
# change the url depending on your specs
cd /path/to/cert/traefik/folder
curl -L "https://github.com/containous/traefik-migration-tool/releases/download/v0.12.1/traefik-migration-tool_v0.12.1_linux_amd64.tar.gz" -o traefik-migration-tool.tar.gz
mkdir migration-tool
tar xvzf traefik-migration-tool.tar.gz -C migration-tool/
cd migration-tool
./traefik-migration-tool acme -i /path/to/acme.json -o new_acme.json --resolver letsencrypt_resolver
mv new_acme.json /path/to/acme.json
What about swarm?
With everything you have, using traefik v2 in a docker swarm is a breeze.
In the traefik command:
- Enable swarm mode on the docker provider
- Set the common network shared between containers with traefik labels. This network must be created with the overlay driver (
docker network create --driver=overlay traefik
) - Set the swarm endpoint and the tls certificates if your swarm protects the docker daemon socket.
-
... services: traefik: ... command: ... - --providers.docker.swarmMode=true - --providers.docker.network=traefik # optional below - --providers.docker.endpoint=tcp://swarm.manager.net:5555 - --providers.docker.tls.ca=/etc/traefik/certs/ca.pem - --providers.docker.tls.cert=/etc/traefik/certs/cert.pem - --providers.docker.tls.key=/etc/traefik/certs/key.pem networks: - traefik deploy: placement: constraints: [node.role == manager] networks: traefik: driver: overlay external: true attachable: true
And then, on the backends do not forget the port
-
... services: nginx: ... deploy: labels: ... traefik.http.services.nginx-service.loadbalancer.server.port: 80 networks: - traefik networks: traefik: driver: overlay external: true attachable: true
-
... services: backend_service: ... networks: - traefik networks: traefik: driver: overlay external: true attachable: true
🐳 github links (v2.1 and v1.7) to the full docker-compose files
That is everything you need!
If you found this tutorial helpful, star this repo as a thank you! ⭐