Cómo desplegar Traefik v 2.x en Docker Swarm
En este artículo, te muestro cómo desplegar mi reverse proxy favorito (Traefik) en mi orquestador de Docker favorito (Docker Swarm).
En algún lado de mi cabeza, tenía la idea de que este artículo ya lo había escrito, pero no y como quiero comenzar a alimentar el repo "awesome-swarm" en Github, creo que este ejemplo es excelente para comenzar.
Bueno, lo que vamos a hacer es desplegar Traefik v2.x en Docker Swarm, con soporte SSL con Let's Encrypt y nada más. En otro momento, voy a hablar sobre cómo agregar el acceso al dashboard de Traefik pero en este caso, vamos a mantenerlo lo más simple posible pero totalmente funcional.
Preparando todo...
Si aún no tenés tu ambiente en Docker Swarm, te recomiendo que leas el siguiente artículo, en el mismo explico como armar un cluster de tres equipos corriendo Docker Swarm. Este cluster se compone de un Manager y dos Workers.
Si ya esta el ambiente, lo primero que vamos a hacer es crear un archivo llamado acme.json. Este archivo es muy importante porque va a almacenar los certificados de Let's Encrypt que gestione Traefik.
$ touch acme.json
Ahora le vamos a ajustar los permisos, ya que no debe quedar muy abierto, para eso ejecutamos lo siguiente:
$ chmod 640 acme.json
El siguiente pre requisito que debemos cumplir es generar una red del tipo "overlay", en este caso, le vamos a llamar "publica" pero puede tener el nombre que quieras.
$ docker network create -d overlay publica
A desplegar se ha dicho
Una vez que nos aseguramos de que lo de arriba está todo ok, vamos a proceder a hacer el despliegue, para este caso particular, la configuración del archivo "traefik.yml" va a quedar de la siguiente manera:
version: "3.3"
services:
traefik:
image: "traefik:latest"
command:
- --entrypoints.web.address=:80
- --entrypoints.websecure.address=:443
- --providers.docker=true
- --providers.docker.swarmMode=true
- --providers.docker.exposedbydefault=false
- --providers.docker.network=publica
- --api
- --log.level=DEBUG
- --certificatesresolvers.leresolver.acme.httpchallenge=true
- --certificatesresolvers.leresolver.acme.email=nacho@cduser.com
- --certificatesresolvers.leresolver.acme.storage=/acme.json
- --certificatesresolvers.leresolver.acme.httpchallenge.entrypoint=web
ports:
- "80:80"
- "443:443"
networks:
- publica
volumes:
- "/var/run/docker.sock:/var/run/docker.sock:ro"
- "./acme.json:/acme.json"
deploy:
labels:
# global redirect to https
- "traefik.http.routers.http-catchall.rule=hostregexp(`{host:.+}`)"
- "traefik.http.routers.http-catchall.entrypoints=web"
- "traefik.http.routers.http-catchall.middlewares=redirect-to-https"
- "traefik.http.routers.http-catchall.middlewares=redirect-to-https@docker"
# middleware redirect
- "traefik.http.middlewares.redirect-to-https.redirectscheme.scheme=https"
networks:
publica:
external: true
Lo siguiente que ejecutamos será:
$ docker stack deploy -c traefik.yml traefik
La terminal nos debería devolver algo como esto:
Creating service traefik_traefik
Para revisar si efectivamente el servicio se creó y el mismo está corriendo ejecutamos lo siguiente:
$ docker service ls
La respuesta debería ser algo como esto:
ID NAME MODE REPLICAS IMAGE PORTS
n2rcb0t4365p traefik_traefik replicated 1/1 traefik:latest *:80->80/tcp, *:443->443/tcp
Como ven, todo hasta ahora parece estar bien, lo siguiente que vamos a hacer es probar con un contenedor, un simple hello world, para ver si efectivamente nuestro soporte y redirección hacia "https" funciona.
Vamos a ver si anda todo...
Para probar, como comenté mas arriba, vamos a desplegar un "hello world", el mismo va a tener esta configuración y lo voy a llamar hello_world.yml.
En esta configuración, como ven, defino un "router" para la navegación http y https:
version: "3.3"
services:
hello_world:
image: tutum/hello-world
networks:
- publica
deploy:
labels:
- "traefik.enable=true"
- "traefik.http.routers.hello.rule=Host(`hello.cduser.com`)"
- "traefik.http.routers.hello.entrypoints=web"
- "traefik.http.routers.hello-secured.rule=Host(`hello.cduser.com`)"
- "traefik.http.routers.hello-secured.entrypoints=websecure"
- "traefik.http.routers.hello-secured.tls.certresolver=leresolver"
- "traefik.http.services.hello.loadbalancer.server.port=80" # Siempre debemos definir un puerto.
- "traefik.http.routers.hello.middlewares=hello-redirect"
- "traefik.http.middlewares.hello-redirect.redirectscheme.scheme=https"
networks:
publica:
external: true
Ejecutamos lo siguiente de esta manera:
$ docker stack deploy -c hello_world.yml apps
Se crea el servicio y una vez que ejecutamos...
$ docker service ls
Nos devolverá el nuevo servicio, junto con el que creamos anteriormente, el Traefik:
ID NAME MODE REPLICAS IMAGE PORTS
izooxkc9v7mb apps_hello_world replicated 1/1 tutum/hello-world:latest
n2rcb0t4365p traefik_traefik replicated 1/1 traefik:latest *:80->80/tcp, *:443->443/tcp
Si todo sale bien, en mi caso, cuando visite la URL http://hello.cduser.com debería redirigirme hacia https://hello.cduser.com
Una forma de comprobar esto es haciendo un "curl" de la siguiente manera:
curl -vvI hello.cduser.com
La devolución de eso debería ser algo como esto:
* Trying 127.0.0.1:80...
* TCP_NODELAY set
* Connected to hello.cduser.com (127.0.0.1) port 80 (#0)
> HEAD / HTTP/1.1
> Host: hello.cduser.com
> User-Agent: curl/7.68.0
> Accept: */*
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 307 Temporary Redirect
HTTP/1.1 307 Temporary Redirect
< Location: https://hello.cduser.com/
Location: https://hello.cduser.com/
< Date: Mon, 22 Jun 2020 02:10:02 GMT
Date: Mon, 22 Jun 2020 02:10:02 GMT
< Content-Length: 18
Content-Length: 18
< Content-Type: text/plain; charset=utf-8
Content-Type: text/plain; charset=utf-8
<
* Connection #0 to host hello.cduser.com left intact
Como ven, en este caso, la redirección anduvo más que bien.
Conclusión
Cómo ven, no es muy complicado y en tan solo un par de minutos, tenemos listo nuestro Reverse Proxy / Balanceador, listo para trabajar con Docker Swarm, con soporte y redirección HTTPS.
Si tenés alguna duda o consulta, no dudes en dejar un comentario aquí abajo.