Terraform Essentials III: Cómo hacer nuestra Infraestructura escalable y reproducible gracias a las variables.

Llegamos al tercer capítulo de esta serie llamada #TerraformEssentials. En este caso, les voy a hablar de las variables y de como estás pueden ayudarnos a que nuestro código e infraestructura sea escalable y reproducible.

Hace unas semanas empecé con esta serie llamada #TerraformEssentials. Hasta ahora vimos dos ejemplos, uno bien sencillo y el otro un poco más completo que incluía la puesta en marcha de un Traefik, Wordpress, MariaDB y alguna que otra cosita más. Le dejo aquí los enlaces:

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.
Terraform Essentials II: Cómo desplegar Traefik y Wordpress en Docker con soporte para Lets Encrypt [Entendiendo Argumentos]
En este artículo te mostraré una receta de Terraform que nos servirá para desplegar Traefik, NGINX, Wordpress y MariaDB con soporte para Lets Encrypt.

Lo que vamos a ver hoy es cómo hacemos que este código, en donde definimos nuestra infraestructura sea reproducible de una manera más, amigable", y que no nos exija editar los "tf" para cada ambiente.

Pensemos en el siguiente escenario: Imagínate el caso donde tengas que replicar la misma configuración para otro ambiente y al tiempo, tengas que hacerla para otro y que por supuesto, los parámetros sean diferentes, que los contenedores o máquinas virtuales tengan nombres y especificaciones diferentes, que se basen en uno u otro template o que incluso los usuarios y contraseñas sean diferentes. ¿Cómo hacemos para replicar todo eso y hacer los cambios? ¿Tengo que modificarlos archivos "TF" cada vez? ¿Al final voy a tener que tener decenas o cientos de archivos "TF" para toda mi infraestructura? y aquí es donde te digo que no. ¿Cómo? gracias a las variables o mejor dicho los archivos ".tfvars" donde vamos a especificarlas.

Estos archivos "tfvars" nos van a permitir especificar variables para que Terraform las consuma y "rellene" con esa información los argumentos en nuestros archivos ".tf" sin necesidad de modificarlos. Solamente veríamos diferentes resultados, cuando consultamos los archivos "tfstate".

Veámoslo con un ejemplo sencillo. Vamos a desplegar un Wordpress con una base de datos MariaDB en Docker y cada uno de los datos que necesitamos para configurar estos contenedores, los vamos a definir a través de un archivo de variables llamado terraform.tfvars.

Nuestro directorio, queda de la siguiente manera:

-rw-r--r--  1 nacho  staff   747B Mar 26 20:19 maria.tf
-rw-r--r--  1 nacho  staff    95B Mar 26 20:19 provider.tf
-rw-r--r--  1 nacho  staff   944B Mar 26 20:19 wordpress.tf
-rw-r--r--  1 nacho  staff    97B Mar 26 21:11 terraform.tfvars

Ahora bien, la sintaxis del archivo "tfvars" es bastante sencilla. Para este tutorial, quedaría de la siguiente manera:

#Vamos definir el proveedor. En este caso, el Docker es local, pero para hacerlo hacia a un Docker remoto, deberíamos especificar la conexión SSH hacia nuestro servidor.

local = "unix:///var/run/docker.sock"

#Vamos a especificar con variables el contenedor de mariadb

contenedor_maria = "cduser_mariadb"
imagen_maria = "mariadb:10.5.1"
password-root = "123456"

#Vamos a especificar con variables el contenedor del wordpress

contenedor = "cduser_wordpress"
imagen = "wordpress:php7.4"

#Vamos a especificar las variables de entorno del wordpress con variables de Terraform, pero también vamos a usar estás variables para la config del mariaDB, esto se llama ser eficientes.

mariadb = "cduser_mariadb"
db-user = "user"
password = "pass"
db = "wordpress"

Vean el código de arriba, comenzamos especificando el host al cual nos vamos a conectar para desplegar estos contenedores.

Luego, específico las variables de entorno de Docker del MariaDB y las "convierto" en variables de Terraform. Hago lo mismo con lo que necesito para el contenedor que va a correr Wordpress.

El archivo maria.tf, quedaría de la siguiente manera:

variable "contenedor_maria" {}
variable "imagen_maria" {}
variable "password-root" {}


# Contenedor
resource "docker_container" "mariadb" {
  name  = "${var.contenedor_maria}"
  image = "${var.imagen_maria}"


# 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=${var.db}", "MYSQL_USER=${var.db-user}", "MYSQL_PASSWORD=${var.password}", "MYSQL_RANDOM_ROOT_PASSWORD=${var.password-root}"]
  command = ["--default-authentication-plugin=mysql_native_password"]

  ports {
    external = 3306
    internal = 3306
  }

}

Noten en el "tf" de acá arriba, que lo que hago en el "root" del archivo es especificar las variables que voy a usar y que va a ir a buscar al "terraform.tfvars". Si, es lo que ustedes piensan, tiene que estar definidas en el archivo "tfvars" y en el "tf"

Y ahora, fijense en el wordpress.tf, que uso algunas variables definidas también en el maria.tf, esto es así porque voy a usar la base de datos que cree y no tiene sentido que haga dos variables para el mismo valor a excepción de la variable para el nombre del contenedor del MariaDB.

variable "contenedor" {}
variable "imagen" {}
variable "mariadb" {}
variable "db-user" {}
variable "password" {}
variable "db" {} 

# Contenedor
resource "docker_container" "recurso" {
  name  = "${var.contenedor}"
  image = "${var.imagen}"
  env = ["WORDPRESS_DB=${var.mariadb}", "WORDPRESS_DB_USER=${var.db-user}", "WORDPRESS_DB_PASSWORD=${var.password}", "WORDPRESS_DB_NAME=${var.db}"]

    ports {
    internal = 80
    external = 80
   }
 }

Lo mismo que el maria.tf, específico en el root del archivo las variables que voy a usar y las ubico en los espacios en donde efectivamente las voy a usar.

Cómo ven en los dos ejemplos, las variables a definir son fáciles de entender, tienen la siguiente sintaxis.

variable "contenedor" {}

Y para invocarlos, quedaría así: Cuando vamos a especificar el argumento entre comillas, ponemos un signo de pesos, abrimos la llave y lo que debemos especificar si o si es la palabra "var", agregamos un "punto", definimos la variable y cerramos la llave.

  name  = "${var.contenedor}"

Vamos a ver si todo esto funciona

Como siempre, la forma de ver si todo esto funciona es probandolo. así que vamos a ejecutar:

terraform apply 

Nos devolverá lo siguiente:

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.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=123456",
          + "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)
        }

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

  # docker_container.recurso will be created
  + resource "docker_container" "recurso" {
      + 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 = (known after apply)
          + value = (known after apply)
        }

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

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


Warning: Interpolation-only expressions are deprecated

  on maria.tf line 8, in resource "docker_container" "mariadb":
   8:   name  = "${var.contenedor_maria}"

Terraform 0.11 and earlier required all non-constant expressions to be
provided via interpolation syntax, but this pattern is now deprecated. To
silence this warning, remove the "${ sequence from the start and the }"
sequence from the end of this expression, leaving just the inner expression.

Template interpolation syntax is still used to construct strings from
expressions when the template includes multiple interpolation sequences or a
mixture of literal strings and interpolations. This deprecation applies only
to templates that consist entirely of a single interpolation sequence.

(and 4 more similar warnings elsewhere)

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

Tipeamos "yes" y veamos...

docker_container.mariadb: Creating...
docker_container.recurso: Creating...
docker_container.recurso: Creation complete after 1s [id=e5748cc303c057c7835fa5c74a6526347d14dc5d85a230a64f12ea9cbd00f92e]
docker_container.mariadb: Creation complete after 1s [id=9ac45903802c1686a15ca5b226a9f3965749b5986992fb6e791902e95b10b918]

Los contenedores se crearon así que vamos a ver que dice el "docker ps".

9ac45903802c        mariadb:10.5.1      "docker-entrypoint.s…"   41 seconds ago      Up 40 seconds       0.0.0.0:3306->3306/tcp   cduser_mariadb
e5748cc303c0        wordpress:php7.4    "docker-entrypoint.s…"   41 seconds ago      Up 40 seconds       0.0.0.0:80->80/tcp       cduser_wordpress

Efectivamente están corriendo y así es como definimos infraestructura en código usando variables.

Para ir cerrando

Espero que este tutorial te haya enseñado algo nuevo, cualquier cosa me podés dejar un comentario acá abajo y lo vamos viendo.

Les dejo los archivos de este proyecto en Gitlab para que puedan reutilizarlos y estudiarlos.

III · master · 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/

Este tutorial también les deja un reto, hay un error, algo falta, no es sobre los variables, pero hay algo que falta definir en los archivos "tf" para que este Wordpress y el MariaDB puedan funcionar "en paz". Espero por sus soluciones aquí abajo. Pueden usar Pastebin para compartir su código.

Pastebin.com - #1 paste tool since 2002!
Pastebin.com is the number one paste tool since 2002. Pastebin is a website where you can store text online for a set period of time.