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.

xe-nvdk/awesome-swarm
These samples provide a starting point for how to integrate different services using in Docker Swarm. You’re invited to contribute. - xe-nvdk/awesome-swarm

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.

Llevando a Docker más allá con Docker Swarm
En este artículo, te voy a mostrar cómo crear un cluster de Docker Swarm. Fácil, rápido y efectivo.

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.