Come costruire un modulo Terraform personalizzato

L’autore ha selezionato il Fondo per il Software Libero e Open Source per ricevere una donazione come parte del programma Scrivi per le Donazioni.

Introduzione

I moduli di Terraform ti permettono di raggruppare risorse distinte della tua infrastruttura in una singola risorsa unificata. Puoi riutilizzarli in seguito con personalizzazioni possibili, senza ripetere le definizioni delle risorse ogni volta che ne hai bisogno, il che è vantaggioso per progetti di grandi dimensioni e strutturati in modo complesso. Puoi personalizzare le istanze dei moduli utilizzando le variabili di input che definisci e estrarre informazioni da esse utilizzando gli output. Oltre a creare i tuoi moduli personalizzati, puoi anche utilizzare i moduli predefiniti pubblicati pubblicamente presso il Registro di Terraform. Gli sviluppatori possono utilizzarli e personalizzarli utilizzando input come i moduli che crei, ma il loro codice sorgente è memorizzato e estratto dal cloud.

In questo tutorial, creerai un modulo Terraform che configurerà più Droplet dietro un Bilanciatore di Carico per la ridondanza. Utilizzerai anche le funzionalità di loop for_each e count del linguaggio di configurazione Hashicorp (HCL) per distribuire contemporaneamente più istanze personalizzate del modulo.

Prerequisiti

Nota: Questo tutorial è stato specificamente testato con Terraform 1.1.3.

Struttura del Modulo e Benefici

In questa sezione, imparerai quali benefici portano i moduli, dove vengono solitamente posizionati nel progetto e come dovrebbero essere strutturati.

I moduli personalizzati di Terraform vengono creati per incapsulare componenti collegati che vengono utilizzati e distribuiti insieme frequentemente in progetti più grandi. Sono autocontenuti, includendo solo le risorse, le variabili e i provider di cui hanno bisogno.

I moduli sono tipicamente memorizzati in una cartella centrale alla radice del progetto, ognuno nella rispettiva sotto-cartella. Per mantenere una separazione pulita tra i moduli, è sempre consigliabile progettarli in modo da avere un unico scopo e assicurarsi che non contengano mai sottomoduli.

È utile creare moduli dai tuoi schemi di risorse quando ti ritrovi a ripeterli con personalizzazioni poco frequenti. Incapsulare una singola risorsa come modulo può essere superfluo e gradualmente rimuove la semplicità dell’architettura complessiva.

Per piccoli progetti di sviluppo e test, incorporare moduli non è necessario perché non portano molti miglioramenti in quei casi. Con la loro capacità di personalizzazione, i moduli sono l’elemento costitutivo dei progetti strutturati in modo complesso. Gli sviluppatori utilizzano i moduli per progetti più grandi a causa dei significativi vantaggi nel evitare la duplicazione del codice. I moduli offrono anche il beneficio che le definizioni necessitano solo di una modifica in un unico punto, che verrà poi propagata attraverso il resto dell’infrastruttura.

Successivamente definirai, utilizzerai e personalizzerai moduli nei tuoi progetti Terraform.

Creazione di un Modulo

In questa sezione, definirai più Droplets e un Bilanciatore di Carico come risorse Terraform e li confezionerai in un modulo. Renderai inoltre il modulo risultante personalizzabile utilizzando input del modulo.

Salverai il modulo in una directory chiamata droplet-lb, sotto una directory chiamata modules. Supponendo di essere nella directory terraform-modules che hai creato come parte dei prerequisiti, creane entrambe contemporaneamente eseguendo:

  1. mkdir -p modules/droplet-lb

L’argomento -p istruisce mkdir a creare tutte le directory nel percorso fornito.

Naviga fino ad essa:

  1. cd modules/droplet-lb

Come è stato notato nella sezione precedente, i moduli contengono le risorse e le variabili che utilizzano. A partire da Terraform 0.13, devono anche includere definizioni dei provider che utilizzano. I moduli non richiedono alcuna configurazione speciale per notare che il codice rappresenta un modulo, poiché Terraform considera ogni directory contenente codice HCL come un modulo, anche la directory radice del progetto.

Le variabili definite in un modulo sono esposte come suoi input e possono essere utilizzate nelle definizioni delle risorse per personalizzarle. Il modulo che creerai avrà due input: il numero di Droplets da creare e il nome del loro gruppo. Crea e apri per la modifica un file chiamato variables.tf dove salverai le variabili:

  1. nano variables.tf

Aggiungi le seguenti righe:

modules/droplet-lb/variables.tf
variable "droplet_count" {}
variable "group_name" {}

Salva e chiudi il file.

Salverai la definizione del Droplet in un file chiamato droplets.tf. Crea e aprilo per modificarlo:

  1. nano droplets.tf

Aggiungi le seguenti righe:

modules/droplet-lb/droplets.tf
resource "digitalocean_droplet" "droplets" {
  count  = var.droplet_count
  image  = "ubuntu-20-04-x64"
  name   = "${var.group_name}-${count.index}"
  region = "fra1"
  size   = "s-1vcpu-1gb"
}

Per il parametro count, che specifica quante istanze di una risorsa creare, passi la variabile droplet_count. Il suo valore sarà specificato quando il modulo viene chiamato dal codice del progetto principale. Il nome di ciascun Droplet distribuito sarà diverso, che ottieni aggiungendo l’indice del Droplet corrente al nome del gruppo fornito. Il deployment dei Droplets sarà nella regione fra1 e funzioneranno con Ubuntu 20.04.

Quando hai finito, salva e chiudi il file.

Con i Droplets ora definiti, puoi passare alla creazione del Bilanciamento del Carico. Salverai la definizione della sua risorsa in un file chiamato lb.tf. Crea e aprilo per modificarlo eseguendo:

  1. nano lb.tf

Aggiungi la sua definizione di risorsa:

modules/droplet-lb/lb.tf
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
  ]
}

Definisci il Bilanciamento del Carico con il nome del gruppo nel suo nome per renderlo distinguibile. Lo distribuisci nella regione fra1 insieme ai Droplets. Le prossime due sezioni specificano le porte e i protocolli di destinazione e di monitoraggio.

Il blocco droplet_ids evidenziato prende gli ID dei Droplet, che devono essere gestiti dal Load Balancer. Poiché ci sono più Droplet e il loro numero non è noto in anticipo, si utilizza un ciclo for per attraversare la raccolta di Droplet (digitalocean_droplet.droplets) e prendere i loro ID. Si racchiude il ciclo for tra parentesi quadre ([]) in modo che la raccolta risultante sia una lista.

Salva e chiudi il file.

Hai ora definito il Droplet, il Load Balancer e le variabili per il tuo modulo. Dovrai definire i requisiti del provider, specificando quali provider utilizza il modulo, incluso la loro versione e dove sono situati. Dal momento che Terraform 0.13, i moduli devono definire esplicitamente le fonti dei provider non mantenuti da HashiCorp; questo perché non li ereditano dal progetto principale.

Salva il requisito del provider in un file chiamato provider.tf. Crealo per la modifica eseguendo:

  1. nano provider.tf

Aggiungi le seguenti righe per richiedere il provider digitalocean:

modules/droplet-lb/provider.tf
terraform {
  required_providers {
    digitalocean = {
      source = "digitalocean/digitalocean"
      version = "~> 2.0"
    }
  }
}

Salva e chiudi il file quando hai finito. Il modulo droplet-lb ora richiede il provider digitalocean.

I moduli supportano anche gli output, che è possibile utilizzare per estrarre informazioni interne sullo stato delle loro risorse. Definirai un output che espone l’indirizzo IP del Load Balancer e lo salverai in un file chiamato outputs.tf. Crealo per la modifica:

  1. nano outputs.tf

Aggiungi la seguente definizione:

modules/droplet-lb/outputs.tf
output "lb_ip" {
  value = digitalocean_loadbalancer.www-lb.ip
}

Questo output recupera l’indirizzo IP del Load Balancer. Salva e chiudi il file.

Il modulo droplet-lb è ora completo dal punto di vista funzionale e pronto per essere distribuito. Lo chiamerai dal codice principale, che memorizzerai nella radice del progetto. Prima, naviga verso di essa attraversando due volte la directory dei file:

  1. cd ../..

Quindi, crea e apri per la modifica un file chiamato main.tf, in cui utilizzerai il modulo:

  1. nano main.tf

Aggiungi le seguenti righe:

main.tf
module "groups" {
  source = "./modules/droplet-lb"

  droplet_count = 3
  group_name    = "group1"
}

output "loadbalancer-ip" {
  value = module.groups.lb_ip
}

In questa dichiarazione richiami il modulo droplet-lb situato nella directory specificata come source. Configuri l’input che fornisce, droplet_count e group_name, impostato su group1 in modo da poter successivamente distinguere tra le istanze.

Dato che l’output dell’IP del bilanciamento del carico è definito in un modulo, non verrà mostrato automaticamente quando si applica il progetto. La soluzione a questo è creare un altro output che recuperi il suo valore (loadbalancer_ip).

Salva e chiudi il file quando hai finito.

Inizializza il modulo eseguendo:

  1. terraform init

L’output sarà simile a questo:

Output
Initializing modules... - groups in modules/droplet-lb Initializing the backend... Initializing provider plugins... - Finding digitalocean/digitalocean versions matching "~> 2.0"... - Installing digitalocean/digitalocean v2.19.0... - Installed digitalocean/digitalocean v2.19.0 (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.

Puoi provare a pianificare il progetto per vedere quali azioni Terraform eseguirebbe eseguendo:

  1. terraform plan -var "do_token=${DO_PAT}"

L’output sarà simile a questo:

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: # Il modulo.groups.digitalocean_droplet.droplets[0] verrà creato + resource "digitalocean_droplet" "droplets" { ... + name = "group1-0" ... } # Il modulo.groups.digitalocean_droplet.droplets[1] verrà creato + resource "digitalocean_droplet" "droplets" { ... + name = "group1-1" ... } # Il modulo.groups.digitalocean_droplet.droplets[2] verrà creato + resource "digitalocean_droplet" "droplets" { ... + name = "group1-2" ... } # Il modulo.groups.digitalocean_loadbalancer.www-lb verrà creato + resource "digitalocean_loadbalancer" "www-lb" { ... + name = "lb-group1" ... } Plan: 4 to add, 0 to change, 0 to destroy. ...

Questo output dettaglia che Terraform creerà tre Droplets, chiamati gruppo1-0, gruppo1-1 e gruppo1-2, e creerà anche un Bilanciatore di Carico chiamato gruppo1-lb, che gestirà il traffico da e verso i tre Droplets.

Puoi provare ad applicare il progetto al cloud eseguendo:

  1. terraform apply -var "do_token=${DO_PAT}"

Inserisci quando richiesto. L’output mostrerà tutte le azioni e verrà mostrato anche l’indirizzo IP del Bilanciatore di Carico:

Output
module.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_address

Hai creato un modulo contenente un numero personalizzabile di Droplets e un Bilanciatore di Carico che verrà configurato automaticamente per gestire il traffico in ingresso e in uscita.

Rinominare Risorse Distribuite

Nella sezione precedente, hai distribuito il modulo che hai definito e lo hai chiamato gruppi. Se desideri cambiare il suo nome, rinominare semplicemente la chiamata al modulo non produrrà i risultati attesi. Rinominare la chiamata provocherà Terraform a distruggere e ricreare risorse, causando tempi di inattività eccessivi.

Per esempio, apri main.tf per la modifica eseguendo:

  1. nano main.tf

Rinomina il modulo gruppi in gruppi_rinominati, come evidenziato:

main.tf
module "groups_renamed" {
  source = "./modules/droplet-lb"

  droplet_count = 3
  group_name    = "group1"
}

output "loadbalancer-ip" {
  value = module.groups_renamed.lb_ip
}

Salva e chiudi il file. Quindi, inizializza nuovamente il progetto:

  1. terraform init

Adesso puoi pianificare il progetto:

  1. terraform plan -var "do_token=${DO_PAT}"

L’output sarà lungo, ma assomiglierà a questo:

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] sarà distrutto ... # module.groups_rinominati.digitalocean_droplet.droplets[0] sarà creato ...

Terraform ti chiederà di distruggere le istanze esistenti e crearne di nuove. Questo è distruttivo e non necessario, e può causare tempi di inattività indesiderati.

Invece, utilizzando il blocco moved, puoi istruire Terraform a spostare le vecchie risorse sotto il nuovo nome. Apri main.tf per la modifica e aggiungi le seguenti righe alla fine del file:

moved {
  from = module.groups
  to   = module.groups_renamed
}

Quando hai finito, salva e chiudi il file.

Adesso puoi pianificare il progetto:

  1. terraform plan -var "do_token=${DO_PAT}"

Quando pianifichi con il blocco moved presente in main.tf, Terraform desidera spostare le risorse anziché ricrearle:

Output
Terraform will perform the following actions: Il modulo `module.groups.digitalocean_droplet.droplets[0]` è stato spostato al modulo `module.groups_renamed.digitalocean_droplet.droplets[0]` ... Il modulo `module.groups.digitalocean_droplet.droplets[1]` è stato spostato al modulo `module.groups_renamed.digitalocean_droplet.droplets[1]` ...

Spostare le risorse cambia la loro posizione nello stato di Terraform, il che significa che le risorse cloud effettive non verranno modificate, distrutte o ricreate.

Dato che modificherai significativamente la configurazione nel prossimo passaggio, distruggi le risorse distribuite eseguendo:

  1. terraform destroy -var "do_token=${DO_PAT}"

Inserisci quando richiesto. L’output terminerà con:

Output
... Destroy complete! Resources: 4 destroyed.

In questa sezione, hai rinominato le risorse nel tuo progetto Terraform senza distruggerle nel processo. Ora distribuirai più istanze di un modulo dallo stesso codice usando for_each e count.

Deploy di più istanze del modulo

In questa sezione, utilizzerai count e for_each per distribuire il modulo droplet-lb più volte con personalizzazioni.

Utilizzo di count

Un modo per distribuire più istanze dello stesso modulo contemporaneamente è passare quanti al parametro count, che è automaticamente disponibile per ogni modulo. Apri main.tf per modificarlo:

  1. nano main.tf

Modificalo in questo modo, rimuovendo la definizione dell’output esistente e il blocco moved:

main.tf
module "groups" {
  source = "./modules/droplet-lb"

  count  = 3

  droplet_count = 3
  group_name    = "group1-${count.index}"
}

Impostando count su 3, istruisci Terraform a distribuire il modulo tre volte, ognuna con un nome di gruppo diverso. Quando hai finito, salva e chiudi il file.

Pianifica la distribuzione eseguendo:

  1. terraform plan -var "do_token=${DO_PAT}"

L’output sarà lungo e assomiglierà a questo:

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: # Il modulo.groups[0].digitalocean_droplet.droplets[0] verrà creato ... # Il modulo.groups[0].digitalocean_droplet.droplets[1] verrà creato ... # Il modulo.groups[0].digitalocean_droplet.droplets[2] verrà creato ... # Il modulo.groups[0].digitalocean_loadbalancer.www-lb verrà creato ... # Il modulo.groups[1].digitalocean_droplet.droplets[0] verrà creato ... # Il modulo.groups[1].digitalocean_droplet.droplets[1] verrà creato ... # Il modulo.groups[1].digitalocean_droplet.droplets[2] verrà creato ... # Il modulo.groups[1].digitalocean_loadbalancer.www-lb verrà creato ... # Il modulo.groups[2].digitalocean_droplet.droplets[0] verrà creato ... # Il modulo.groups[2].digitalocean_droplet.droplets[1] verrà creato ... # Il modulo.groups[2].digitalocean_droplet.droplets[2] verrà creato ... # Il modulo.groups[2].digitalocean_loadbalancer.www-lb verrà creato ... Plan: 12 to add, 0 to change, 0 to destroy. ...

I dettagli di Terraform nell’output indicano che ciascuna delle tre istanze del modulo avrebbe tre Droplets e un Load Balancer associato a esse.

Utilizzando for_each

Puoi utilizzare for_each per i moduli quando è necessaria una personalizzazione dell’istanza più complessa o quando il numero di istanze dipende da dati di terze parti (spesso presentati come mappe) che non sono noti durante la scrittura del codice.

Ora definirai una mappa che associa i nomi dei gruppi ai conteggi di Droplet e distribuirai le istanze di droplet-lb in base ad essa. Apri main.tf per la modifica eseguendo:

  1. nano main.tf

Modifica il file in modo che sembri così:

main.tf
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
}

Prima definisci una mappa chiamata group_counts che contiene quante Droplets dovrebbe avere un dato gruppo. Quindi, invochi il modulo droplet-lb, ma specifica che il loop for_each dovrebbe operare su var.group_counts, la mappa che hai definito appena prima. droplet_count prende each.value, il valore della coppia corrente, che è il conteggio delle Droplets per il gruppo corrente. group_name riceve il nome del gruppo.

Salva e chiudi il file quando hai finito.

Prova ad applicare la configurazione eseguendo:

  1. terraform plan -var "do_token=${DO_PAT}"

L’output dettaglierà le azioni che Terraform intraprenderebbe per creare i due gruppi con i loro Droplets e Load Balancer:

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: # Il modulo.groups["gruppo1"].digitalocean_droplet.droplets[0] verrà creato ... # Il modulo.groups["gruppo1"].digitalocean_loadbalancer.www-lb verrà creato ... # Il modulo.groups["gruppo2"].digitalocean_droplet.droplets[0] verrà creato ... # Il modulo.groups["gruppo2"].digitalocean_droplet.droplets[1] verrà creato ... # Il modulo.groups["gruppo2"].digitalocean_droplet.droplets[2] verrà creato ... # Il modulo.groups["gruppo2"].digitalocean_loadbalancer.www-lb verrà creato ...

In questo passaggio, hai utilizzato count e for_each per distribuire più istanze personalizzate dello stesso modulo dallo stesso codice.

Conclusion

In questo tutorial, hai creato e distribuito moduli Terraform. Hai utilizzato i moduli per raggruppare risorse logicamente collegate e personalizzarle al fine di distribuire più istanze diverse da una definizione di codice centrale. Hai inoltre utilizzato gli output per mostrare gli attributi delle risorse contenute nel modulo.

Se desideri saperne di più su Terraform, dai un’occhiata alla nostra serie Come Gestire l’Infrastruttura con Terraform.

Source:
https://www.digitalocean.com/community/tutorials/how-to-build-a-custom-terraform-module