Add how to install traefik blog post
This commit is contained in:
parent
4e9c793538
commit
5b66e5f248
1 changed files with 347 additions and 0 deletions
347
content/posts/how-to-install-traefik-2-docker-swarm.md
Normal file
347
content/posts/how-to-install-traefik-2-docker-swarm.md
Normal file
|
@ -0,0 +1,347 @@
|
||||||
|
+++
|
||||||
|
title = "How to install Traefik 2.x on a Docker Swarm"
|
||||||
|
date = "2019-10-21"
|
||||||
|
author = "Aloïs Micard"
|
||||||
|
authorTwitter = "" #do not include @
|
||||||
|
cover = ""
|
||||||
|
tags = ["Docker Swarm", "Dev Ops"]
|
||||||
|
keywords = ["", ""]
|
||||||
|
description = ""
|
||||||
|
showFullContent = false
|
||||||
|
+++
|
||||||
|
|
||||||
|
have recently migrated my production docker swarm from Traefik 1.7 to Traefik 2.0 and since I cannot found a good tutorial I have decided to write one. So in this tutorial you'll learn how to deploy Traefik with HTTPS support on a docker swarm.
|
||||||
|
|
||||||
|
Please note that I won't explain what Traefik is since it may needs his own article and I will focus on the deployment and configuration. This tutorial will also assume that you have a working docker swarm.
|
||||||
|
|
||||||
|
This tutorial will be part of a series regarding Docker Swarm, I'll write other articles to explain how to expose Traefik dashboard securely, deploy Portainer, etc...
|
||||||
|
|
||||||
|
# Install Traefik
|
||||||
|
|
||||||
|
Please note that Traefik will need to be deployed on a manager node on your swarm. You'll also need to make sure that your firewall on this node is correctly setup to allow both port 80 and 443 (http / https) from outside. This is important because Traefik will listen on these ports for incoming traffic.
|
||||||
|
|
||||||
|
The first thing before creating the config file is to create a docker swarm network that will be used by Traefik to watch for services to expose. This can be done in one command:
|
||||||
|
|
||||||
|
```
|
||||||
|
docker network create --driver=overlay traefik-public
|
||||||
|
```
|
||||||
|
|
||||||
|
This will create an [overlay network](https://docs.docker.com/network/overlay/) named 'traefik-public' on the swarm.
|
||||||
|
|
||||||
|
Now we are going to create the docker compose file to deploy Traefik.
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
version: '3'
|
||||||
|
|
||||||
|
services:
|
||||||
|
reverse-proxy:
|
||||||
|
image: traefik:v2.0.2
|
||||||
|
command:
|
||||||
|
- "--providers.docker.endpoint=unix:///var/run/docker.sock"
|
||||||
|
- "--providers.docker.swarmMode=true"
|
||||||
|
- "--providers.docker.exposedbydefault=false"
|
||||||
|
- "--providers.docker.network=traefik-public"
|
||||||
|
- "--entrypoints.web.address=:80"
|
||||||
|
ports:
|
||||||
|
- 80:80
|
||||||
|
volumes:
|
||||||
|
# So that Traefik can listen to the Docker events
|
||||||
|
- /var/run/docker.sock:/var/run/docker.sock:ro
|
||||||
|
networks:
|
||||||
|
- traefik-public
|
||||||
|
deploy:
|
||||||
|
placement:
|
||||||
|
constraints:
|
||||||
|
- node.role == manager
|
||||||
|
|
||||||
|
networks:
|
||||||
|
traefik-public:
|
||||||
|
external: true
|
||||||
|
```
|
||||||
|
|
||||||
|
This is the minimal amount of config needed to deploy a working Traefik instance.
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
Let me explain in details what's going on:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
- "--providers.docker.endpoint=unix:///var/run/docker.sock"
|
||||||
|
```
|
||||||
|
|
||||||
|
Here we are telling to Traefik to listen to the unix docker socket.
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
- "--providers.docker.swarmMode=true"
|
||||||
|
```
|
||||||
|
|
||||||
|
Enable swarm mode support by setting swarmMode to true.
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
- "--providers.docker.exposedbydefault=false"
|
||||||
|
```
|
||||||
|
|
||||||
|
Little security: tell Traefik to not expose container by default: only expose container that are explicitly enabled (using label traefik.enabled).
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
- "--providers.docker.network=traefik-public"
|
||||||
|
```
|
||||||
|
|
||||||
|
Tell Traefik to dial with exposed containers using traefik-public network. (the one created earlier)
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
- "--entrypoints.web.address=:80"
|
||||||
|
```
|
||||||
|
|
||||||
|
Create an entrypoint named **web** exposed on port 80. This entrypoint will be used to indicate on which port your application should be exposed.
|
||||||
|
|
||||||
|
This configuration is enough to get started. You can deploy Traefik using the following command:
|
||||||
|
|
||||||
|
|
||||||
|
```sh
|
||||||
|
docker stack deploy traefik -c traefik.yaml
|
||||||
|
```
|
||||||
|
|
||||||
|
Great. Now we will deploy something.
|
||||||
|
|
||||||
|
# Deploy and expose a hello-world container
|
||||||
|
|
||||||
|
Now it's time to deploy something on your swarm to test the configuration. For this example we are going to deploy tutum/hello-world a little container with an apache service that display an "Hello World" page.
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
version: '3'
|
||||||
|
services:
|
||||||
|
helloworld:
|
||||||
|
image: tutum/hello-world:latest
|
||||||
|
networks:
|
||||||
|
- traefik-public
|
||||||
|
deploy:
|
||||||
|
labels:
|
||||||
|
- "traefik.enable=true"
|
||||||
|
- "traefik.http.routers.helloworld.rule=Host(`helloworld.local`)"
|
||||||
|
- "traefik.http.routers.helloworld.entrypoints=web"
|
||||||
|
- "traefik.http.services.helloworld.loadbalancer.server.port=80"
|
||||||
|
networks:
|
||||||
|
traefik-public:
|
||||||
|
external: true
|
||||||
|
```
|
||||||
|
|
||||||
|
The labels sections is read by Traefik to get the configuration of the container and to create the needed components to expose it.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
- "traefik.enable=true"
|
||||||
|
```
|
||||||
|
|
||||||
|
This flag tells Traefik to expose the container, needed because we explicitly disable container auto exposition with providers.docker.exposedbydefault.
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
- "traefik.http.routers.helloworld.rule=Host(`localhost`)"
|
||||||
|
```
|
||||||
|
|
||||||
|
Create a Host Matching rule. Here we tell Traefik to redirect all traffic coming with host localhost to this container.
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
- "traefik.http.routers.helloworld.entrypoints=web"
|
||||||
|
```
|
||||||
|
|
||||||
|
Tells Traefik that this container will be exposed using the web entrypoint. (the name correspond with the entrypoint previously created in Traefik).
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
- "traefik.http.services.helloworld.loadbalancer.server.port=80"
|
||||||
|
```
|
||||||
|
|
||||||
|
Indicate to Traefik that the container expose the port 80 internally. This is needed by Traefik to act as a proxy.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
Now if you access the page at http://localhost you'll be redirect to Traefik that will proxify the content of the helloworld container.
|
||||||
|
|
||||||
|
# Add HTTPS support
|
||||||
|
|
||||||
|
If you have followed this tutorial carefully you should now have a working Traefik instance and a helloworld service running and accessible on http://localhost. This is working but not secure: you should always use HTTPS when possible. Thankfully this can be done easily in Traefik.
|
||||||
|
|
||||||
|
In this tutorial we are going to use the [HTTP challenge](https://letsencrypt.org/docs/challenge-types/) to automatically generate a Letsencrypt certificate.
|
||||||
|
|
||||||
|
I won't go in the details to explain how the HTTP-01 challenge work, but basically all you have to do is to add/update the A record of your DNS zone to point to your docker swarm manager IP address. (where Traefik is exposed).
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
version: '3'
|
||||||
|
|
||||||
|
services:
|
||||||
|
reverse-proxy:
|
||||||
|
image: traefik:v2.0.2
|
||||||
|
command:
|
||||||
|
- "--providers.docker.endpoint=unix:///var/run/docker.sock"
|
||||||
|
- "--providers.docker.swarmMode=true"
|
||||||
|
- "--providers.docker.exposedbydefault=false"
|
||||||
|
- "--providers.docker.network=traefik-public"
|
||||||
|
- "--entrypoints.web.address=:80"
|
||||||
|
- "--entrypoints.websecure.address=:443"
|
||||||
|
- "--certificatesresolvers.letsencryptresolver.acme.httpchallenge=true"
|
||||||
|
- "--certificatesresolvers.letsencryptresolver.acme.httpchallenge.entrypoint=web"
|
||||||
|
- "--certificatesresolvers.letsencryptresolver.acme.email=user@domaine.com"
|
||||||
|
- "--certificatesresolvers.letsencryptresolver.acme.storage=/letsencrypt/acme.json"
|
||||||
|
ports:
|
||||||
|
- 80:80
|
||||||
|
- 443:443
|
||||||
|
volumes:
|
||||||
|
# To persist certificates
|
||||||
|
- traefik-certificates:/letsencrypt
|
||||||
|
# So that Traefik can listen to the Docker events
|
||||||
|
- /var/run/docker.sock:/var/run/docker.sock:ro
|
||||||
|
networks:
|
||||||
|
- traefik-public
|
||||||
|
deploy:
|
||||||
|
placement:
|
||||||
|
constraints:
|
||||||
|
- node.role == manager
|
||||||
|
volumes:
|
||||||
|
traefik-certificates:
|
||||||
|
networks:
|
||||||
|
traefik-public:
|
||||||
|
external: true
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
I will explain the new parts:
|
||||||
|
|
||||||
|
```
|
||||||
|
- "--entrypoints.websecure.address=:443"
|
||||||
|
```
|
||||||
|
|
||||||
|
Create an entrypoint named **websecure** exposed on port **443** (https). This entrypoint will be used to indicate on which port your application should be exposed.
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
- "--certificatesresolvers.letsencryptresolver.acme.httpchallenge=true"
|
||||||
|
```
|
||||||
|
|
||||||
|
Tell Traefik that the challenge named **letsencryptresolver** will use the http challenge.
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
- "--certificatesresolvers.letsencryptresolver.acme.httpchallenge.entrypoint=web"
|
||||||
|
```
|
||||||
|
|
||||||
|
We are going to expose the http challenge over http (obvious since we have no certificate yet)
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
- "--certificatesresolvers.letsencryptresolver.acme.email=user@domaine.com"
|
||||||
|
```
|
||||||
|
|
||||||
|
The email used to create a letsencrypt account
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
- "--certificatesresolvers.letsencryptresolver.acme.storage=/letsencrypt/acme.json"
|
||||||
|
```
|
||||||
|
|
||||||
|
Where do Traefik will persist the certificate. This location should be bound to a [volume](https://docs.docker.com/storage/volumes/) to be persisted between container restart. Here we are saving to /letsencrypt/ directory, who is mounted as volume traefik-certificates (see traefik.yaml for details)
|
||||||
|
|
||||||
|
## Update hello-world to use HTTPS config
|
||||||
|
|
||||||
|
Now we are going to update the hello-world deployment configuration file in order to expose it using HTTPS.
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
version: '3'
|
||||||
|
services:
|
||||||
|
helloworld:
|
||||||
|
image: tutum/hello-world:latest
|
||||||
|
networks:
|
||||||
|
- traefik-public
|
||||||
|
deploy:
|
||||||
|
labels:
|
||||||
|
- "traefik.enable=true"
|
||||||
|
- "traefik.http.routers.helloworld.rule=Host(`helloworld.local`)"
|
||||||
|
- "traefik.http.routers.helloworld.entrypoints=websecure"
|
||||||
|
- "traefik.http.routers.helloworld.tls.certresolver=letsencryptresolver"
|
||||||
|
- "traefik.http.services.helloworld.loadbalancer.server.port=80"
|
||||||
|
networks:
|
||||||
|
traefik-public:
|
||||||
|
external: true
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
There is only two things to change:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
- "traefik.http.routers.helloworld.entrypoints=websecure"
|
||||||
|
```
|
||||||
|
|
||||||
|
To indicate to Traefik that we want to expose our container using the previously declared websecure entrypoint.
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
- "traefik.http.routers.helloworld.tls.certresolver=letsencryptresolver"
|
||||||
|
```
|
||||||
|
|
||||||
|
To specify which certificate resolver we wanna use. Here we are using the letsencryptresolver declared before.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
Easy right? Once the stack is redeployed Traefik will then ask Letsencrypt to generate a SSL certificate for the domain helloworld.local and Traefik will use it when exposing your application.
|
||||||
|
|
||||||
|
N.B: Of course this example won't work since you cannot proove that you own the helloworld.local domain. (.local is a reserved TLD used for local area network)
|
||||||
|
|
||||||
|
## Bonus: Create an automatic HTTPS redirect
|
||||||
|
|
||||||
|
If you want to redirect all HTTP traffic to HTTPS it can be done by easily by using a Middleware. Just add the following labels to to the Traefik configuration file.
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
labels:
|
||||||
|
- "traefik.enable=true"
|
||||||
|
- "traefik.http.routers.http-catchall.rule=hostregexp(`{host:.+}`)"
|
||||||
|
- "traefik.http.routers.http-catchall.entrypoints=web"
|
||||||
|
- "traefik.http.routers.http-catchall.middlewares=redirect-to-https@docker"
|
||||||
|
- "traefik.http.middlewares.redirect-to-https.redirectscheme.scheme=https"
|
||||||
|
```
|
||||||
|
|
||||||
|
It will create a router named *http-catchall* that will intercept all HTTP request (using the hostregexp) and will forward it to the router named redirect-to-https. This router will perform a redirection to the HTTPS scheme.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
Here's the final stack to deploy a Traefik instance with HTTPS support and HTTP -> HTTPS global redirection
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
version: '3'
|
||||||
|
|
||||||
|
services:
|
||||||
|
reverse-proxy:
|
||||||
|
image: traefik:v2.0.2
|
||||||
|
command:
|
||||||
|
- "--providers.docker.endpoint=unix:///var/run/docker.sock"
|
||||||
|
- "--providers.docker.swarmMode=true"
|
||||||
|
- "--providers.docker.exposedbydefault=false"
|
||||||
|
- "--providers.docker.network=traefik-public"
|
||||||
|
- "--entrypoints.web.address=:80"
|
||||||
|
- "--entrypoints.websecure.address=:443"
|
||||||
|
- "--certificatesresolvers.letsencryptresolver.acme.httpchallenge=true"
|
||||||
|
- "--certificatesresolvers.letsencryptresolver.acme.httpchallenge.entrypoint=web"
|
||||||
|
- "--certificatesresolvers.letsencryptresolver.acme.email=user@domaine.com"
|
||||||
|
- "--certificatesresolvers.letsencryptresolver.acme.storage=/letsencrypt/acme.json"
|
||||||
|
ports:
|
||||||
|
- 80:80
|
||||||
|
- 443:443
|
||||||
|
volumes:
|
||||||
|
- traefik-certificates:/letsencrypt
|
||||||
|
- /var/run/docker.sock:/var/run/docker.sock:ro
|
||||||
|
networks:
|
||||||
|
- traefik-public
|
||||||
|
deploy:
|
||||||
|
placement:
|
||||||
|
constraints:
|
||||||
|
- node.role == manager
|
||||||
|
labels:
|
||||||
|
- "traefik.enable=true"
|
||||||
|
- "traefik.http.routers.http-catchall.rule=hostregexp(`{host:.+}`)"
|
||||||
|
- "traefik.http.routers.http-catchall.entrypoints=web"
|
||||||
|
- "traefik.http.routers.http-catchall.middlewares=redirect-to-https@docker"
|
||||||
|
- "traefik.http.middlewares.redirect-to-https.redirectscheme.scheme=https"
|
||||||
|
volumes:
|
||||||
|
traefik-certificates:
|
||||||
|
networks:
|
||||||
|
traefik-public:
|
||||||
|
external: true
|
||||||
|
```
|
||||||
|
|
||||||
|
Long live Docker Swarm and Happy hacking !
|
Loading…
Add table
Add a link
Reference in a new issue