Terraform Essentials II: Cómo desplegar Traefik y Wordpress en Docker con soporte para Lets Encrypt [Entendiendo Argumentos]

Bienvenidos al segundo episodio de la serie Terraform Essentials. En este artículo te mostraré una receta de Terraform que nos servirá para desplegar Traefik, NGINX, Wordpress y MariaDB.

Una vez más, gracias por leer, si caiste derecho a través de Google o un enlace, podés leer el primer artículo de esta serie, donde, explico de manera básica, cómo conectar Terraform y ejecutar una receta en una instancia de Docker local y lanzar un WebServer (NGINX). En ese artículo vimos cómo configurar en los archivos "tf" para que exponga un puerto de un contenedor y como accedemos al mismo.

Terraform Essentials I: Cómo conectarlo con Docker y desplegar un Hello World.
Este es el primer artículo de la serie Terraform Essentials, donde aprenderemos a trabajar con esta herramienta de Infrastructure as Code.

En el segundo artículo, voy a usar la misma instancia de Docker pero para desplegar un ambiente totalmente funcional que involucra a Traefik, como Reverse Proxy, un Servidor Web, que es un NGINX, un contenedor corriendo Wordpress y otro corriendo un MariaDB que podremos usar como Back End de ese Wordpress, todo esto, con la capacidad de tener certificados de Lets Encrypt.

No solo eso, esta receta, te va a permitir tener la redirección automática hacía HTTPS de los recursos expuestos hacía Traefik y también la habilitación del Dashboard para controlar cómo funciona todo con un autenticación básica para acceder al mismo.

Ahora bien, suena bueno, ¿no? Yo se que si, pero lo que les quiero mostrar con el despliegue de cada una de estas aplicaciones con Terraform, no solo es el hecho de tener una receta que funcione y cubra todo esto, sino que veamos y entendamos cada uno de los argumentos que necesitan cada una estas aplicaciones. De esta manera, creo, entenderemos el poder que tiene Terraform.

¿Empezamos?

En el artículo anterior, vimos, desde dónde descargar e Instalar Terraform, así que esa parte me la voy a saltear. La primera parada es el árbol de archivos. Veamos:

-rw-r--r--  1 nacho  staff   747B Feb 29 15:06 maria.tf
-rw-r--r--  1 nacho  staff    95B Feb 25 23:57 provider.tf
-rw-r--r--  1 nacho  staff   3.0K Feb 29 16:23 traefik.tf
-rw-r--r--  1 nacho  staff   519B Feb 29 12:39 webserver.tf
-rw-r--r--  1 nacho  staff   944B Feb 29 15:22 wordpress.tf

FIJENSE... que separe cada carga de trabajo (Contenedor, Aplicación) en un archivo diferente, esto es para que sea fácil analizarlo después, pero podríamos agarrar un "gran" archivo y poner todo ahí.

Analicemos el provider.tf

El "provider.tf", como comenté en el artículo anterior, es el que va a indicar sobre que plataforma vamos a trabajar y como nos vamos a conectar. Si se acuerdan, les mostre que para ese caso, le pegaba a un socket local, así que ahora, te voy a mostrar un ejemplo, de cómo quedaría, conectando por SSH hacía un Host que corre Docker.

# Configure the Docker provider
provider "docker" {
    host = "ssh://root@localhost:puerto"
 }

Ahora bien, continuemos. El siguiente archivo para analizar se llama "traefik.tf", que es nuestro Reverse Proxy y que, con la configuración que veremos, nos va a dar capacidades para desplegar, de manera automática, certificados SSL de Lets Encrypt, como comente más adelante, también tiene una configuración específica para redireccionar el tráfico hacia HTTPS, así como también, la habilitación de su dashboard con una autenticación básica.

Traefik.tf

# Contenedor Traefik
resource "docker_container" "traefik" {
  name  = "cduser_traefik"
  image = "traefik:v2.1.4"
  command = ["--entrypoints.web.address=:80", "--entrypoints.websecure.address=:443", "--providers.docker", "--api", "--api.dashboard=true", "--log.level=ERROR", "--accesslog.filepath=/Users/nacho/terraform/files/traefik/log/access.log", "--accesslog.format=json", "--accesslog.filters.statuscodes=200,300-302", "--accesslog.filters.retryattempts", "--accesslog.filters.minduration=10ms", "--certificatesresolvers.leresolver.acme.httpchallenge=true", "--certificatesresolvers.leresolver.acme.email=nombre@email.com", "--certificatesresolvers.leresolver.acme.storage=/acme.json", "--certificatesresolvers.leresolver.acme.httpchallenge.entrypoint=web"]


# Especificamos los puertos del contenedor que necesita exponer. Traefik necesita si o si el 80 y el 443.

  ports {
    internal = 80
    external = 80
   }

  ports {
    internal = 443
    external = 443
   }

# Creamos los volumenes para que guarde los certificados y la configuración de Traefik sea constante.

  volumes {
    container_path  = "/var/log"
    read_only = false
    host_path = "/Users/nacho/terraform/files/traefik/log"
  }

  volumes {
    container_path  = "/var/run/docker.sock"
    read_only = true
    host_path = "/var/run/docker.sock"
  }

  volumes {
    container_path  = "/acme.json"
    read_only = true
    host_path = "//Users/nacho/terraform/files/traefik/acme/acme.json"
  }

# Labels

# Global Redirect to https

  labels {
        label = "traefik.http.routers.http-catchall.rule"
        value = "hostregexp(`{host:.+}`)"
      }

  labels {
        label = "traefik.http.routers.http-catchall.entrypoints"
        value = "web"
     }

  labels {
        label = "traefik.http.routers.http-catchall.middlewares"
        value = "redirect-to-https"
     }

# Making Dashboard Work with https

  labels {
        label = "traefik.enable"
        value = "true"
      }

  labels {
        label = "traefik.http.routers.traefik.rule"
        value = "Host(`dashboard.cduser.com`)" # esto es un registro en mi /etc/hosts
     }

  labels {
        label = "traefik.http.routers.traefik.service"
        value = "api@internal"
     }

  labels {
        label = "traefik.http.routers.traefik.tls.certresolver"
        value = "leresolver"
      }

  labels {
        label = "traefik.http.routers.traefik.entrypoints"
        value = "websecure" # Si quisieramos que el dashboard funcione con https, aquí debemos cambiar 'web' por 'websecure'
     }

  labels {
        label = "traefik.http.routers.traefik.middlewares"
        value = "authtraefik"
     }

  labels {
        label = "traefik.http.middlewares.authtraefik.basicauth.users"
        value = "nacho:{SHA}KfCVPY4DC/T32ix/QdaKZXgYhkg=" # El usuario aquí es nacho y la contraseña es también nacho.
     }

# middleware redirect

  labels {
        label = "traefik.http.middlewares.redirect-to-https.redirectscheme.scheme"
        value = "https"
     }
}

¡Que pedazo de archivo!. Lo que vemos acá es el despliegue del contenedor, con todos los argumentos que son propios de Docker y que Traefik necesita para correr como corresponde.

Veamos uno por uno estos argumentos, de manera que nos permita entender mejor este archivo.

# Contenedor Traefik
resource "docker_container" "traefik" {
  name  = "cduser_traefik"
  image = "traefik:v2.1.4"
  command = ["--entrypoints.web.address=:80", "--entrypoints.websecure.address=:443", "--providers.docker", "--api", "--api.dashboard=true", "--log.level=ERROR", "--accesslog.filepath=/Users/nacho/terraform/files/traefik/log/access.log", "--accesslog.format=json", "--accesslog.filters.statuscodes=200,300-302", "--accesslog.filters.retryattempts", "--accesslog.filters.minduration=10ms", "--certificatesresolvers.leresolver.acme.httpchallenge=true", "--certificatesresolvers.leresolver.acme.email=nombre@email.com", "--certificatesresolvers.leresolver.acme.storage=/acme.json", "--certificatesresolvers.leresolver.acme.httpchallenge.entrypoint=web"]

En este bloque he definido, el resource que en este caso es "docker_container", le doy una propiedad a ese resource que se llame "traefik". Específico un nombre para el contenedor que se llamará "cduser_traefik" y la imágen a descargar con el argumento "image". Por último, vemos el "command". Este argumento nos servirá para especificar qué configuración tendrá Traefik.

Luego vemos los puertos que necesitamos exponer hacia afuera, en este caso, Traefik escuchará esos dos puertos y en función de la configuración que tengan nuestros contenedores, despachará el tráfico hacia uno u otro. Fijense la sintaxis y como se definen los puertos.

# Especificamos los puertos del contenedor que necesita exponer. Traefik necesita si o si el 80 y el 443.

  ports {
    internal = 80
    external = 80
   }

  ports {
    internal = 443
    external = 443
   }

Traefik también necesita que especifiquemos volúmenes, estos son claves, para guardar los certificados que generemos con Lets Encrypt (acme.json), otro en el cual le exponemos el socket de Docker y por último uno donde vamos a guardar los Logs.

# Creamos los volumenes para que guarde los certificados y la configuración de Traefik sea constante.

  volumes {
    container_path  = "/var/log"
    read_only = false
    host_path = "/Users/nacho/terraform/files/traefik/log"
  }

  volumes {
    container_path  = "/var/run/docker.sock"
    read_only = true
    host_path = "/var/run/docker.sock"
  }

  volumes {
    container_path  = "/acme.json"
    read_only = true
    host_path = "//Users/nacho/terraform/files/traefik/acme/acme.json"
  }

Por último, Traefik necesita de "Labels" y fijense como es la sintaxis de estos (solo pongo algunos para no hacerlo muy largo)

  labels {
        label = "traefik.http.routers.traefik.service"
        value = "api@internal"
     }

  labels {
        label = "traefik.http.routers.traefik.tls.certresolver"
        value = "leresolver"
      }

Hasta acá, vimos como usar argumentos, especificamente, Labels, Volumes, Ports y Command. Ahora, que terminamos de analizar este archivo, pasemos al de la base de datos, a ver que nos encontramos ahí.

maria.tf

# Contenedor
resource "docker_container" "mariadb" {
  name  = "cduser_mariadb"
  image = "mariadb:10.5.1"

# Especificamos el volumen a montar de mariadb. Siempre es recomendable que quede persistente en un volumen la carpeta /var/lib/mysql.

  volumes {
    container_path  = "/var/lib/mysql"
    read_only = false
    host_path = "/Users/nacho/terraform/files/mariadb/lib"
  }


# Especificamos variables de entorno para crear una base, generar un usuario root, una contraseña para ese usuario, el usuario de base de datos para wordpress y su contraseña.

  env = ["MYSQL_DATABASE=wordpress", "MYSQL_USER=user", "MYSQL_PASSWORD=pass", "MYSQL_RANDOM_ROOT_PASSWORD=yes"]
}

Fijense, que acá vemos, el contenedor, el nombre, la imágen que va a descargar y luego el volumen que montaremos para asegurarnos de que la información de ese contenedor sea persistente. Aquí también agregamos algo más: Variables de entorno.

Estás variables, nos permiten pasarle configuración al contenedor, en este caso, vemos cómo estoy creando una base de datos llamada "wordpress", genero un usuario llamado "user" (muy original), un password y una variable para que genere un contraseña de manera aleatoria para el usuario root.

Sigamos. El siguiente archivo es el wordpress.tf.

wordpress.tf

# Contenedor
resource "docker_container" "wordpress" {
  name  = "cduser_wordpress"
  image = "wordpress:php7.4"
  env = ["WORDPRESS_DB=cduser_mariadb", "WORDPRESS_DB_USER=user", "WORDPRESS_DB_PASSWORD=pass", "WORDPRESS_DB_NAME=wordpress"]

# Especificamos el volumen a montar de Wordpress. Siempre es recomendable que quede persistente en un volumen la carpeta wp-content.

  volumes {
    container_path  = "/var/www/html/wp-content"
    read_only = false
    host_path = "/Users/nacho/terraform/files/wordpress/wp-content"
  }


# Especificamos las labels para que podamos consumir este contenedor a través de Traefik con la url a.com

  labels {
     label = "traefik.http.routers.wordpress.rule"
     value = "Host(`c.com`)"
   }

  labels {
     label = "traefik.http.routers.wordpress.entrypoints"
     value = "websecure"
   }

  labels {
     label = "traefik.http.routers.wordpress.tls.certresolver"
     value = "leresolver"
   }
}

En este archivo, no vamos a ver nada que ya no hayamos visto, aunque si quiero que se detengan en dos cosas: Una, los labels, en estos ya vamos a especificar configuración para que Traefik haga su magia. Por ejemplo, el nombre del host (c.com), cuál va a ser el EntryPoint (WebSecure = 443) y lo relacionado a certificado SSL de Lets Encrypt. Lo otro es que no está especificado ningún puerto, en este caso, el contenedor de Wordpress, así como muchos otros, exponen hacia adentro del host de Docker, el puerto 80 y eso para Traefik es suficiente.

Lo mismo con el NGINX. Dicho sea de paso.

webserver.tf

# Contenedor
resource "docker_container" "hello_world" {
  name  = "cduser_hello_world"
  image = "tutum/hello-world"

# Especificamos las labels para que podamos consumir este contenedor a través de Traefik con la url a.com

  labels {
     label = "traefik.http.routers.hello.rule"
     value = "Host(`a.com`)"
   }

  labels {
     label = "traefik.http.routers.hello.entrypoints"
     value = "websecure"
   }

  labels {
     label = "traefik.http.routers.hello.tls.certresolver"
     value = "leresolver"
   }
}

Este archivo es muy parecido al de Wordpress, nada del otro mundo, vemos el host (a.com) y el resto, casi igual, fijense, que los "labels" son diferentes y tiene que ser así para cada contenedor que vayamos a exponer a través de Traefik.

Así que estamos listos, con los ingredientes y sus cantidades revisadas, todo parece estar bien, así que vamos a "cocinar" esta receta.

Cooking

Al igual que en el artículo anterior, vamos a ejecutar primero un...

terraform validate

De esta manera, nos vamos a asegurar que la sintaxis sea la correcta. Si todo esta bien, nos devolverá algo como esto:

Success! The configuration is valid.

Ahora bien, podemos ir con el "terraform plan" para validar todo lo que va a hacer o podemos tomar el atajo de directo escribir "terraform apply", que nos mostrará exactamente lo mismo pero con la oportunidad de confirmarlo tipeando "yes".

terraform apply

Nos devolverá todo esto:

An execution plan has been generated and is shown below.
Resource actions are indicated with the following symbols:
  + create

Terraform will perform the following actions:

  # docker_container.hello_world will be created
  + resource "docker_container" "hello_world" {
      + attach           = false
      + bridge           = (known after apply)
      + command          = (known after apply)
      + container_logs   = (known after apply)
      + entrypoint       = (known after apply)
      + env              = (known after apply)
      + exit_code        = (known after apply)
      + gateway          = (known after apply)
      + hostname         = (known after apply)
      + id               = (known after apply)
      + image            = "tutum/hello-world"
      + ip_address       = (known after apply)
      + ip_prefix_length = (known after apply)
      + ipc_mode         = (known after apply)
      + log_driver       = "json-file"
      + logs             = false
      + must_run         = true
      + name             = "cduser_hello_world"
      + network_data     = (known after apply)
      + read_only        = false
      + restart          = "no"
      + rm               = false
      + shm_size         = (known after apply)
      + start            = true

      + labels {
          + label = "traefik.http.routers.hello.entrypoints"
          + value = "websecure"
        }
      + labels {
          + label = "traefik.http.routers.hello.rule"
          + value = "Host(`a.com`)"
        }
      + labels {
          + label = "traefik.http.routers.hello.tls.certresolver"
          + value = "leresolver"
        }
    }

  # docker_container.mariadb will be created
  + resource "docker_container" "mariadb" {
      + attach           = false
      + bridge           = (known after apply)
      + command          = [
          + "--default-authentication-plugin=mysql_native_password",
        ]
      + container_logs   = (known after apply)
      + entrypoint       = (known after apply)
      + env              = [
          + "MYSQL_DATABASE=wordpress",
          + "MYSQL_PASSWORD=pass",
          + "MYSQL_RANDOM_ROOT_PASSWORD=yes",
          + "MYSQL_USER=user",
        ]
      + exit_code        = (known after apply)
      + gateway          = (known after apply)
      + hostname         = (known after apply)
      + id               = (known after apply)
      + image            = "mariadb:10.5.1"
      + ip_address       = (known after apply)
      + ip_prefix_length = (known after apply)
      + ipc_mode         = (known after apply)
      + log_driver       = "json-file"
      + logs             = false
      + must_run         = true
      + name             = "cduser_mariadb"
      + network_data     = (known after apply)
      + read_only        = false
      + restart          = "no"
      + rm               = false
      + shm_size         = (known after apply)
      + start            = true

      + labels {
          + label = (known after apply)
          + value = (known after apply)
        }

      + volumes {
          + container_path = "/var/lib/mysql"
          + host_path      = "/Users/nacho/terraform/files/mariadb/lib"
          + read_only      = false
        }
    }

  # docker_container.traefik will be created
  + resource "docker_container" "traefik" {
      + attach           = false
      + bridge           = (known after apply)
      + command          = [
          + "--entrypoints.web.address=:80",
          + "--entrypoints.websecure.address=:443",
          + "--providers.docker",
          + "--api",
          + "--api.dashboard=true",
          + "--log.level=ERROR",
          + "--accesslog.filepath=/Users/nacho/terraform/files/traefik/log/access.log",
          + "--accesslog.format=json",
          + "--accesslog.filters.statuscodes=200,300-302",
          + "--accesslog.filters.retryattempts",
          + "--accesslog.filters.minduration=10ms",
          + "--certificatesresolvers.leresolver.acme.httpchallenge=true",
          + "--certificatesresolvers.leresolver.acme.email=nombre@email.com",
          + "--certificatesresolvers.leresolver.acme.storage=/acme.json",
          + "--certificatesresolvers.leresolver.acme.httpchallenge.entrypoint=web",
        ]
      + container_logs   = (known after apply)
      + entrypoint       = (known after apply)
      + env              = (known after apply)
      + exit_code        = (known after apply)
      + gateway          = (known after apply)
      + hostname         = (known after apply)
      + id               = (known after apply)
      + image            = "traefik:v2.1.4"
      + ip_address       = (known after apply)
      + ip_prefix_length = (known after apply)
      + ipc_mode         = (known after apply)
      + log_driver       = "json-file"
      + logs             = false
      + must_run         = true
      + name             = "cduser_traefik"
      + network_data     = (known after apply)
      + read_only        = false
      + restart          = "no"
      + rm               = false
      + shm_size         = (known after apply)
      + start            = true

      + labels {
          + label = "traefik.enable"
          + value = "true"
        }
      + labels {
          + label = "traefik.http.middlewares.authtraefik.basicauth.users"
          + value = "nacho:{SHA}KfCVPY4DC/T32ix/QdaKZXgYhkg="
        }
      + labels {
          + label = "traefik.http.middlewares.redirect-to-https.redirectscheme.scheme"
          + value = "https"
        }
      + labels {
          + label = "traefik.http.routers.http-catchall.entrypoints"
          + value = "web"
        }
      + labels {
          + label = "traefik.http.routers.http-catchall.middlewares"
          + value = "redirect-to-https"
        }
      + labels {
          + label = "traefik.http.routers.http-catchall.rule"
          + value = "hostregexp(`{host:.+}`)"
        }
      + labels {
          + label = "traefik.http.routers.traefik.entrypoints"
          + value = "websecure"
        }
      + labels {
          + label = "traefik.http.routers.traefik.middlewares"
          + value = "authtraefik"
        }
      + labels {
          + label = "traefik.http.routers.traefik.rule"
          + value = "Host(`dashboard.cduser.com`)"
        }
      + labels {
          + label = "traefik.http.routers.traefik.service"
          + value = "api@internal"
        }
      + labels {
          + label = "traefik.http.routers.traefik.tls.certresolver"
          + value = "leresolver"
        }

      + ports {
          + external = 80
          + internal = 80
          + ip       = "0.0.0.0"
          + protocol = "tcp"
        }
      + ports {
          + external = 443
          + internal = 443
          + ip       = "0.0.0.0"
          + protocol = "tcp"
        }

      + volumes {
          + container_path = "/acme.json"
          + host_path      = "//Users/nacho/terraform/files/traefik/acme/acme.json"
          + read_only      = true
        }
      + volumes {
          + container_path = "/var/log"
          + host_path      = "/Users/nacho/terraform/files/traefik/log"
          + read_only      = false
        }
      + volumes {
          + container_path = "/var/run/docker.sock"
          + host_path      = "/var/run/docker.sock"
          + read_only      = true
        }
    }

  # docker_container.wordpress will be created
  + resource "docker_container" "wordpress" {
      + attach           = false
      + bridge           = (known after apply)
      + command          = (known after apply)
      + container_logs   = (known after apply)
      + entrypoint       = (known after apply)
      + env              = [
          + "WORDPRESS_DB=cduser_mariadb",
          + "WORDPRESS_DB_NAME=wordpress",
          + "WORDPRESS_DB_PASSWORD=pass",
          + "WORDPRESS_DB_USER=user",
        ]
      + exit_code        = (known after apply)
      + gateway          = (known after apply)
      + hostname         = (known after apply)
      + id               = (known after apply)
      + image            = "wordpress:php7.4"
      + ip_address       = (known after apply)
      + ip_prefix_length = (known after apply)
      + ipc_mode         = (known after apply)
      + log_driver       = "json-file"
      + logs             = false
      + must_run         = true
      + name             = "cduser_wordpress"
      + network_data     = (known after apply)
      + read_only        = false
      + restart          = "no"
      + rm               = false
      + shm_size         = (known after apply)
      + start            = true

      + labels {
          + label = "traefik.http.routers.wordpress.entrypoints"
          + value = "websecure"
        }
      + labels {
          + label = "traefik.http.routers.wordpress.rule"
          + value = "Host(`c.com`)"
        }
      + labels {
          + label = "traefik.http.routers.wordpress.tls.certresolver"
          + value = "leresolver"
        }

      + volumes {
          + container_path = "/var/www/html/wp-content"
          + host_path      = "/Users/nacho/terraform/files/wordpress/wp-content"
          + read_only      = false
        }
    }

Plan: 4 to add, 0 to change, 0 to destroy.

Do you want to perform these actions?
  Terraform will perform the actions described above.
  Only 'yes' will be accepted to approve.

  Enter a value: yes

Apretamos "enter" y vemos que sucede:

docker_container.hello_world: Creating...
docker_container.mariadb: Creating...
docker_container.wordpress: Creating...
docker_container.traefik: Creating...
docker_container.hello_world: Creation complete after 2s [id=5381ed88f3cf22875b8fb21427518cf42f2f6a123498e01bc3a8fc057dd647b1]
docker_container.traefik: Creation complete after 2s [id=cd2c29f1189d64c23e003911a904c6787385182a3a83de7575f41ae96efe1a58]
docker_container.mariadb: Creation complete after 2s [id=929b97f954e995a27217380aee084a2b578d21f31dc789bf03bf42c02c39b568]
docker_container.wordpress: Creation complete after 2s [id=1923c8b3e05c5f2d5026cfd91392e2b6be515bfb63f5f57de907abd0761ce6ef]

Apply complete! Resources: 4 added, 0 changed, 0 destroyed.

Divino, todo creado, ahora, vamos a ver si todo funciona.

docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                                      NAMES
1923c8b3e05c        wordpress:php7.4    "docker-entrypoint.s…"   30 seconds ago      Up 28 seconds       80/tcp                                     cduser_wordpress
cd2c29f1189d        traefik:v2.1.4      "/entrypoint.sh --en…"   30 seconds ago      Up 28 seconds       0.0.0.0:80->80/tcp, 0.0.0.0:443->443/tcp   cduser_traefik
929b97f954e9        mariadb:10.5.1      "docker-entrypoint.s…"   30 seconds ago      Up 28 seconds       3306/tcp                                   cduser_mariadb
5381ed88f3cf        tutum/hello-world   "/bin/sh -c 'php-fpm…"   30 seconds ago      Up 28 seconds       80/tcp                                     cduser_hello_world

Todo parece estar bien. Veamos que tal el Hello World. A ver si funciona a través de Traefik y responde cuando invoco "a.com"

Veamos el Wordpress (c.com)

Divino también, hasta se conectó con la base de datos y solo debemos especificar el lenguaje y algunos datos más del wordpress.

Por último, veamos si el dashboard, que tenemos "asegurado" con auth basic, funciona y si funciona perfectamente.

Para ir cerrando

En este segundo artículo, viste cómo desplegar Traefik, con soporte para certificados SSL, con su dashboard activado y con la redirección automática hacia HTTPS, así como también el despliegue de Wordpress, MariaDB y un NGINX. Todo esto, de manera totalmente funciona y usando los argumentos de Terraform.

Tal vez, este artículo tiene algún parecido con este, pero la diferencia, es que no ejecute un docker-compose desde Terraform, sino que construí con argumentos y configuración propia y totalmente funcional.

Cómo implementar Traefik 2.1.2 con Docker usando Terraform en DigitalOcean
¡Pa! que título, cuatro tecnologías en un solo tutorial. Les cuento estoyjugando mucho con Terraform en estos últimos días y con Traefik, así que armeuna receta para desplegar este Reverse Proxy en DigitalOcean. Terraform, en mi opinión, es una herramienta maravillosa que te permiteautomatizar e…

Aclaración

Si sos nuevo en esto, tal vez te confunda que use los dominios a.com y c.com, estos dominios no son de mi propiedad, lo único que hago es generar un registro en mi archivo /etc/hosts para que a.com y c.com, así como dashboard.cduser.com para que apunten a mi localhost que es donde está corriendo Docker.

Lo otro para aclarar, es que si bien en la barra de tareas, el navegador acusa "not secure" en realidad, se está navegando por SSL, sucede que Let's Encrypt no tiene forma de validar mi localhost y los dominios que estoy utilizando (a.com, c.com) de forma local.

Recursos

Los archivos para descargar utilizados para este artículo, están en el siguiente GitLab. Este es el artículo II, así que vayan a buscar los archivos a esa carpeta.

Ignacio Van Droogenbroeck / Terraform Essentials
En este proyecto, encontrarás todos los ejemplos que uso en la seríe llamada Terraform Essentials. Espero que te sea de útilidad: Puedes encontrar estos artículos en: https://cduser.com/tag/terraform/

Despedida

Espero que este artículo te haya servidor para entender un poco más cómo funciona Terraform, quédate atento al próximo miércoles, donde te mostraré, como utilizar Terraform con VMware vSphere. Comentarios son más que bienvenidos así como mejoras en el repo de Gitlab.