Introducción
Los módulos de Terraform te permiten agrupar los distintos recursos de tu infraestructura en un único recurso unificado. Puedes reutilizarlos posteriormente con posibles personalizaciones sin tener que repetir las definiciones de recursos cada vez que las necesites, lo cual resulta útil para proyectos grandes con estructuras complejas. Puedes personalizar las instancias de los módulos mediante las variables de entrada que definas y extraer información de ellas mediante las salidas. Además de crear tus propios módulos personalizados, también puedes usar módulos prediseñados que se publican en el registro de Terraform. Los desarrolladores pueden usarlos y personalizarlos mediante entradas, como los módulos que creas, pero su código fuente se almacena dentro y fuera de la nube. En este tutorial, crearás un módulo de Terraform que lanza varios Droplets detrás de un balanceador de carga para mayor redundancia. También usarás las funciones de bucle for_each y count del lenguaje de configuración de Hashicorp (HCL) para implementar varias instancias de módulos personalizados simultáneamente.
Requisitos previos
- Un código de acceso personal a DigitalOcean
- Terraform está instalado en su sistema y se configura un proyecto con el proveedor DO.
- Introducción a los tipos de datos y bucles HCL
- Introducción a las salidas de Terraform y sus usos
Estructura y beneficios del módulo
En esta sección, aprenderá los beneficios que aportan los módulos, dónde se ubican típicamente en un proyecto y cómo deben estructurarse. Los módulos personalizados de Terraform se crean para encapsular componentes conectados que se usan a menudo en proyectos más grandes y se implementan juntos. Son autónomos, reuniendo únicamente los recursos, variables y proveedores que necesitan. Los módulos suelen almacenarse en una carpeta central en la raíz del proyecto, cada uno en su propia subcarpeta. Para mantener una separación clara entre los módulos, diseñe siempre que tengan un único propósito y asegúrese de que nunca contengan submódulos. Empaquetar un solo recurso como módulo puede ser redundante y reducir gradualmente la simplicidad de la arquitectura general. Para proyectos pequeños de desarrollo y pruebas, la combinación de módulos no es necesaria, ya que no aportan mucho progreso en esos casos. Los módulos también ofrecen la ventaja de que las definiciones solo deben modificarse en un lugar, lo cual luego se propaga al resto de la infraestructura.
A continuación, define, usa y personaliza módulos en tus proyectos Terraform.
Crear un módulo
En esta sección, se definen varios Droplets y un Balanceador de Carga como recursos de Terraform y se empaquetan en un módulo. También se configura el módulo resultante mediante entradas de módulo configurables.
Coloque el módulo en un directorio llamado gota-lbLos guardará en un directorio llamado modules. Suponiendo que se encuentra en el directorio terraform-modules que creó como parte de los prerrequisitos, instale ambos a la vez ejecutando:
mkdir -p modules/droplet-lbEl argumento -p indica a mkdir que cree todos los directorios en la ruta proporcionada.
Ir a ello:
cd modules/droplet-lbComo se mencionó en la sección anterior, los módulos contienen los recursos y las variables que utilizan. A partir de Terraform 0.13, también deben contener las definiciones de los proveedores que utilizan. Los módulos no requieren ninguna configuración especial para indicar que el código representa un módulo, ya que Terraform considera cualquier directorio que contenga código HCL como un módulo, incluso el directorio raíz del proyecto.
Las variables definidas en un módulo se exponen como sus entradas y pueden usarse en las definiciones de recursos para personalizarlas. El módulo que cree tendrá dos entradas: el número de droplets a crear y el nombre de su grupo. Para editar un archivo llamado variables.tf Crea y abre donde almacenarás las variables:
nano variables.tfAñade las siguientes líneas:
variable "droplet_count" {}
variable "group_name" {}Guarde y cierre el archivo.
Define el Droplet en un archivo llamado gotitas.tf Lo guardarás. Lo crearás y lo abrirás para editarlo.
nano droplets.tf
Añade las siguientes líneas:
resource "digitalocean_droplet" "droplets" {
count = var.droplet_count
image = "ubuntu-22-04-x64"
name = "${var.group_name}-${count.index}"
region = "fra1"
size = "s-1vcpu-1gb"
lifecycle {
precondition {
condition = var.droplet_count >= 2
error_message = "At least two droplets must be created."
}
}
}Para el parámetro contarSe pasa la variable droplet_count, que especifica cuántas instancias de un recurso se crearán. Su valor se determinará al llamar al módulo desde el código principal del proyecto. El nombre de cada droplet implementado será diferente, lo que se logra añadiendo el índice del droplet actual al nombre del grupo proporcionado. Implementación de droplets en la zona. Fragmento 1 Será y ejecutará Ubuntu 22.04.
Sección Ciclo vital Contiene un Condición previa Se ejecuta antes de que los recursos se implementen. Aquí, verifica que se creen al menos dos estaciones; tener solo una anula la función del balanceador de carga. Otro ejemplo de validaciones se puede encontrar en el repositorio k8s-bootstrapper, que contiene plantillas para configurar un clúster de Kubernetes de DigitalOcean con Terraform. Allí, las validaciones se utilizan para garantizar que el número de nodos del clúster esté dentro del rango permitido.
Cuando haya terminado, guarde y cierre el archivo.
Una vez definidos los Droplets, puede continuar con la creación del Load Balancer. Defina su origen en un archivo llamado lb.tf Lo guardarás. Créalo y ábrelo para editarlo ejecutando lo siguiente:
nano lb.tfAñade su definición de fuente:
resource "digitalocean_loadbalancer" "www-lb" {
name = "lb-${var.group_name}"
region = "fra1"
forwarding_rule {
entry_port = 80
entry_protocol = "http"
target_port = 80
target_protocol = "http"
}
healthcheck {
port = 22
protocol = "tcp"
}
droplet_ids = [
for droplet in digitalocean_droplet.droplets:
droplet.id
]
}Define el balanceador de carga con el nombre del grupo en su nombre para que sea reconocible. Lo define junto con las ubicaciones en la zona. Fragmento 1 Las siguientes dos secciones especifican los puertos y protocolos de destino y de monitoreo.
El bloque droplet_ids resaltado obtiene los ID de los droplets que el balanceador de carga debe gestionar. Dado que hay varios droplets y no se conoce el número de antemano, se utiliza un bucle for para recorrer la colección de droplets (digitalocean_droplet.droplets) y obtener sus ID. Se encierra el bucle for entre corchetes ([]) para que la colección resultante sea una lista.
Guarde y cierre el archivo.
Ahora que ha definido el Droplet, el Balanceador de Carga y las variables para su módulo, debe definir los requisitos del proveedor y especificar qué proveedores utilizará el módulo, incluyendo su versión y ubicación. A partir de Terraform 0.13, los módulos deben definir explícitamente los recursos de proveedores no pertenecientes a Hashicorp que utilizarán. Esto se debe a que no los heredarán del proyecto principal.
Los requisitos del proveedor se definen en un archivo llamado proveedor.tf Lo guardarás. Para editarlo, ejecuta lo siguiente:
nano provider.tfAgregue las siguientes líneas para requerir el proveedor Digitalocean:
terraform {
required_providers {
digitalocean = {
source = "digitalocean/digitalocean"
version = "~> 2.0"
}
}
}Cuando termine, guarde y cierre el archivo. Módulo gota-lb Ahora al proveedor Digitalocean Necesidades.
Los módulos también admiten salidas que permiten extraer información interna sobre el estado de sus recursos. Se define una salida que muestra la dirección IP del balanceador de carga y se guarda en un archivo llamado salidas.tf Guárdalo. Créalo para editarlo:
nano outputs.tfAgregue la siguiente definición:
output "lb_ip" {
value = digitalocean_loadbalancer.www-lb.ip
}Esta salida recuperará la dirección IP del balanceador de carga. Guarde y cierre el archivo.
Módulo gota-lb Ya está completamente funcional y listo para implementar. Lo llamarás desde el código principal, que almacenarás en la raíz del proyecto. Primero, accede a él haciendo doble clic en el directorio de archivos:
cd ../..Luego un archivo llamado principal.tf Crea y abre para editar donde utilizarás el módulo:
nano main.tfAñade las siguientes líneas:
module "groups" {
source = "./modules/droplet-lb"
droplet_count = 3
group_name = "group1"
}
output "loadbalancer-ip" {
value = module.groups.lb_ip
}En este anuncio, el módulo gota-lb Llamas al directorio de origen, que se encuentra en el directorio especificado. La entrada que proporciona es recuento de gotas y nombre_del_grupo Lo configura para que se establezca en el grupo1 para poder distinguir entre instancias más adelante.
Dado que la salida IP del balanceador de carga se define en un módulo, no se muestra automáticamente al implementar el proyecto. La solución es crear otra salida recuperando su valor (loadbalancer_ip).
Cuando termine, guarde y cierre el archivo.
Ejecute el módulo ejecutando:
terraform initEl resultado será el siguiente:
OutputInitializing modules...
- groups in modules/droplet-lb
Initializing the backend...
Initializing provider plugins...
- Finding digitalocean/digitalocean versions matching "~> 2.0"...
- Installing digitalocean/digitalocean v2.34.1...
- Installed digitalocean/digitalocean v2.34.1 (signed by a HashiCorp partner, key ID F82037E524B9C0E8)
...
Terraform has been successfully initialized!
You may now begin working with Terraform. Try running "terraform plan" to see
any changes that are required for your infrastructure. All Terraform commands
should now work.
If you ever set or change modules or backend configuration for Terraform,
rerun this command to reinitialize your working directory. If you forget, other
commands will detect it and remind you to do so if necessary.Puede programar el proyecto para ver qué acciones realizará Terraform tras la ejecución:
terraform plan -var "do_token=${DO_PAT}"La salida será similar a esto:
Output...
Terraform used the selected providers to generate the following execution plan. Resource actions are
indicated with the following symbols:
+ create
Terraform will perform the following actions:
# module.groups.digitalocean_droplet.droplets[0] will be created
+ resource "digitalocean_droplet" "droplets" {
...
+ name = "group1-0"
...
}
# module.groups.digitalocean_droplet.droplets[1] will be created
+ resource "digitalocean_droplet" "droplets" {
...
+ name = "group1-1"
...
}
# module.groups.digitalocean_droplet.droplets[2] will be created
+ resource "digitalocean_droplet" "droplets" {
...
+ name = "group1-2"
...
}
# module.groups.digitalocean_loadbalancer.www-lb will be created
+ resource "digitalocean_loadbalancer" "www-lb" {
...
+ name = "lb-group1"
...
}
Plan: 4 to add, 0 to change, 0 to destroy.
...Esta salida explica que Terraform crea tres gotas llamadas group1-0, group1-1 y group1-2, y también crea un balanceador de carga llamado group1-lb que maneja el tráfico hacia y desde las tres gotas.
Puede implementar el proyecto en la nube ejecutando lo siguiente:
terraform apply -var "do_token=${DO_PAT}"Cuando se le solicite, responda "Sí". El resultado mostrará todas las acciones y también la dirección IP del balanceador de carga:
Outputmodule.groups.digitalocean_droplet.droplets[1]: Creating...
module.groups.digitalocean_droplet.droplets[0]: Creating...
module.groups.digitalocean_droplet.droplets[2]: Creating...
...
Apply complete! Resources: 4 added, 0 changed, 0 destroyed.
Outputs:
loadbalancer-ip = ip_addressHa creado un módulo que contiene una cantidad configurable de droplets y un balanceador de carga que se configura automáticamente para administrar su tráfico entrante y saliente.
Cambiar el nombre de los recursos implementados
En la sección anterior, implementó el módulo que definió y lo llamó Grupo. Si desea cambiar su nombre, simplemente renombrar la llamada del módulo no producirá los resultados esperados. Cambiar el nombre de la llamada provocará que Terraform destruya y vuelva a crear los recursos, lo que provocará un tiempo de inactividad excesivo.
Por ejemplo, principal.tf Ábrelo para editarlo ejecutando:
nano main.tfCambie el nombre del módulo de grupos a group_renamed como se especifica:
module "groups_renamed" {
source = "./modules/droplet-lb"
droplet_count = 3
group_name = "group1"
}
output "loadbalancer-ip" {
value = module.groups_renamed.lb_ip
}Guarde y cierre el archivo. Luego, inicialice el proyecto de nuevo:
terraform initAhora puedes planificar el proyecto:
terraform plan -var "do_token=${DO_PAT}"El resultado será largo pero se verá así:
Output...
Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
+ create
- destroy
Terraform will perform the following actions:
# module.groups.digitalocean_droplet.droplets[0] will be destroyed
...
# module.groups_renamed.digitalocean_droplet.droplets[0] will be created
...Terraform requiere que destruyas las instancias existentes y crees nuevas. Esto es destructivo e innecesario y puede provocar fallos inesperados.
En su lugar, puede indicar a Terraform que migre los recursos antiguos con el nuevo nombre usando el bloque migrado. Abra main.tf para editarlo y agregue las siguientes líneas al final del archivo:
moved {
from = module.groups
to = module.groups_renamed
}Cuando hayas terminado, guarda y cierra el archivo.
Ahora puedes planificar el proyecto:
terraform plan -var "do_token=${DO_PAT}"Cuando programa con el bloque movido en main.tf, Terraform quiere mover los recursos en lugar de recrearlos:
OutputTerraform will perform the following actions:
# module.groups.digitalocean_droplet.droplets[0] has moved to module.groups_renamed.digitalocean_droplet.droplets[0]
...
# module.groups.digitalocean_droplet.droplets[1] has moved to module.groups_renamed.digitalocean_droplet.droplets[1]
...Los recursos móviles cambian su ubicación en el modo Terraform, lo que significa que los recursos reales en la nube no se modifican, destruyen ni recrean.
Dado que cambiará significativamente la configuración en el siguiente paso, actualice los recursos implementados ejecutando:
terraform destroy -var "do_token=${DO_PAT}"Cuando te preguntaron, Sí Ingresar. La salida termina con:
Output...
Destroy complete! Resources: 4 destroyed.En esta sección, renombraste recursos en tu proyecto Terraform sin destruirlos. Ahora implementarás varias instancias de un módulo desde el mismo código usando for_each y count.
Implementación de múltiples instancias de módulos
En esta sección, utilizará count y for_each para implementar el módulo droplet-lb varias veces con personalización.
Usando el conteo
Una forma de implementar varias instancias de un módulo simultáneamente es pasar un recuento al parámetro count, que está disponible automáticamente para cada módulo. Abra main.tf para editarlo:
nano main.tfCámbielo a lo siguiente eliminando la definición de salida existente y el bloque movido:
module "groups" {
source = "./modules/droplet-lb"
count = 3
droplet_count = 3
group_name = "group1-${count.index}"
}Al establecer el conteo en 3, se le indica a Terraform que implemente el módulo tres veces, cada una con un nombre de grupo diferente. Al terminar, guarde y cierre el archivo.
Programe la implementación ejecutando:
terraform plan -var "do_token=${DO_PAT}"La salida será larga y se verá así:
Output...
Terraform used the selected providers to generate the following execution plan. Resource actions are
indicated with the following symbols:
+ create
Terraform will perform the following actions:
# module.groups[0].digitalocean_droplet.droplets[0] will be created
...
# module.groups[0].digitalocean_droplet.droplets[1] will be created
...
# module.groups[0].digitalocean_droplet.droplets[2] will be created
...
# module.groups[0].digitalocean_loadbalancer.www-lb will be created
...
# module.groups[1].digitalocean_droplet.droplets[0] will be created
...
# module.groups[1].digitalocean_droplet.droplets[1] will be created
...
# module.groups[1].digitalocean_droplet.droplets[2] will be created
...
# module.groups[1].digitalocean_loadbalancer.www-lb will be created
...
# module.groups[2].digitalocean_droplet.droplets[0] will be created
...
# module.groups[2].digitalocean_droplet.droplets[1] will be created
...
# module.groups[2].digitalocean_droplet.droplets[2] will be created
...
# module.groups[2].digitalocean_loadbalancer.www-lb will be created
...
Plan: 12 to add, 0 to change, 0 to destroy.
...Terraform explica en la salida que cada una de las tres instancias del módulo tiene tres Droplets y un Load Balancer asociados a ellas.
Usando for_each
Puede utilizar for_each para los módulos cuando necesite una personalización de instancias más compleja o cuando la cantidad de instancias dependa de datos de terceros (a menudo proporcionados como un mapa) que no se conocen al escribir el código.
Ahora define un mapa que empareje los nombres de grupo con el número de droplets e implemente instancias droplet-lb basándose en él. Abre main.tf para editarlo ejecutando:
nano main.tfCambie el archivo de la siguiente manera:
variable "group_counts" {
type = map
default = {
"group1" = 1
"group2" = 3
}
}
module "groups" {
source = "./modules/droplet-lb"
for_each = var.group_counts
droplet_count = each.value
group_name = each.key
}Primero, se define un mapa llamado group_counts que contiene el número de gotas en un grupo dado. A continuación, se llama al módulo droplet-lb, pero se especifica que el bucle for_each debe operar en var.group_counts, el mapa definido anteriormente. droplet_count toma each.value, el valor del par actual, que representa el número de gotas del grupo actual. group_name toma el nombre del grupo.
Cuando termine, guarde y cierre el archivo.
Intente aplicar la configuración ejecutando lo siguiente:
terraform plan -var "do_token=${DO_PAT}"La salida muestra los detalles de las acciones de Terraform para crear estos dos grupos con Droplets y Load Balancers:
Output...
Terraform used the selected providers to generate the following execution plan. Resource actions are
indicated with the following symbols:
+ create
Terraform will perform the following actions:
# module.groups["group1"].digitalocean_droplet.droplets[0] will be created
...
# module.groups["group1"].digitalocean_loadbalancer.www-lb will be created
...
# module.groups["group2"].digitalocean_droplet.droplets[0] will be created
...
# module.groups["group2"].digitalocean_droplet.droplets[1] will be created
...
# module.groups["group2"].digitalocean_droplet.droplets[2] will be created
...
# module.groups["group2"].digitalocean_loadbalancer.www-lb will be created
...En este punto, ha utilizado count y for_each para implementar múltiples instancias personalizadas de un módulo desde el mismo código.
Resultado
En este tutorial, creó e implementó módulos de Terraform. Usó módulos para agrupar recursos relacionados lógicamente y los personalizó para implementar múltiples instancias de una definición de código central. También usó salidas para mostrar las propiedades de los recursos en el módulo.









