Hoe u een aangepaste Terraform-module kunt bouwen

De auteur heeft het Free and Open Source Fund geselecteerd om een donatie te ontvangen als onderdeel van het Write for Donations-programma.

Inleiding

Terraform-modules stellen u in staat om verschillende resources van uw infrastructuur te groeperen in één, geünificeerde resource. U kunt ze later hergebruiken met mogelijke aanpassingen, zonder de resource-definities telkens opnieuw te hoeven herhalen wanneer u ze nodig heeft, wat voordelig is voor grote en complex gestructureerde projecten. U kunt module-instanties aanpassen met behulp van door u gedefinieerde invoervariabelen en informatie uit hen halen met behulp van uitvoer. Naast het maken van uw eigen aangepaste modules, kunt u ook de kant-en-klare modules gebruiken die openbaar zijn gepubliceerd in de Terraform-registratie. Ontwikkelaars kunnen ze gebruiken en aanpassen met invoer zoals de modules die u maakt, maar hun broncode wordt opgeslagen in en opgehaald uit de cloud.

In deze zelfstudie maakt u een Terraform-module die meerdere Droplets achter een Load Balancer instelt voor redundantie. U zult ook de for_each en count loopfuncties van de HashiCorp Configuration Language (HCL) gebruiken om meerdere aangepaste instanties van de module tegelijk te implementeren.

Vereisten

Opmerking: Deze zelfstudie is specifiek getest met Terraform 1.1.3.

Module Structuur en Voordelen

In deze sectie leer je welke voordelen modules bieden, waar ze meestal in het project worden geplaatst, en hoe ze gestructureerd moeten zijn.

Op maat gemaakte Terraform-modules worden gemaakt om verbonden componenten te omvatten die vaak samen worden gebruikt en ingezet in grotere projecten. Ze zijn zelfstandig en bundelen alleen de bronnen, variabelen en providers die ze nodig hebben.

Modules worden meestal opgeslagen in een centrale map aan de root van het project, elk in zijn respectievelijke submap eronder. Om een schone scheiding tussen modules te behouden, ontwerp ze altijd met een enkel doel en zorg ervoor dat ze nooit submodules bevatten.

Het is handig om modules te maken van je bronstructuren wanneer je merkt dat je ze herhaalt met zeldzame aanpassingen. Het verpakken van een enkele bron als module kan overbodig zijn en verwijdert geleidelijk de eenvoud van de algehele architectuur.

Voor kleine ontwikkelings- en testprojecten is het niet nodig om modules op te nemen omdat ze in die gevallen niet veel verbetering bieden. Met hun mogelijkheid tot aanpassing zijn modules het bouwelement van complex gestructureerde projecten. Ontwikkelaars gebruiken modules voor grotere projecten vanwege de aanzienlijke voordelen bij het vermijden van code duplicatie. Modules bieden ook het voordeel dat definities slechts op één plaats hoeven te worden gewijzigd, waarna ze worden doorgevoerd in de rest van de infrastructuur.

Volgende zul je modules definiëren, gebruiken en aanpassen in je Terraform-projecten.

Het maken van een Module

In deze sectie zul je meerdere Droplets en een Load Balancer definiëren als Terraform-resources en ze verpakken in een module. Je zult ook de resulterende module aanpasbaar maken met behulp van module-invoer.

Je zult de module opslaan in een map genaamd droplet-lb, onder een map genaamd modules. Als je ervan uitgaat dat je je bevindt in de terraform-modules-map die je hebt aangemaakt als onderdeel van de vereisten, maak beide dan in één keer aan door het volgende uit te voeren:

  1. mkdir -p modules/droplet-lb

Het -p-argument geeft aan mkdir opdracht om alle mappen in het opgegeven pad te maken.

Navigeer ernaartoe:

  1. cd modules/droplet-lb

Zoals werd opgemerkt in de vorige sectie, bevatten modules de resources en variabelen die ze gebruiken. Vanaf Terraform 0.13 moeten ze ook definities bevatten van de providers die ze gebruiken. Modules vereisen geen speciale configuratie om aan te geven dat de code een module vertegenwoordigt, omdat Terraform elke map die HCL-code bevat beschouwt als een module, zelfs de hoofdmap van het project.

Variabelen die zijn gedefinieerd in een module worden blootgesteld als de invoer ervan en kunnen worden gebruikt in resource-definities om ze aan te passen. De module die je gaat maken, zal twee invoeren hebben: het aantal Droplets om te maken en de naam van hun groep. Maak een bestand genaamd variables.tf aan en open het voor bewerking, waar je de variabelen zult opslaan:

  1. nano variables.tf

Voeg de volgende regels toe:

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

Sla het bestand op en sluit het af.

Je zult de Droplet-definitie opslaan in een bestand met de naam droplets.tf. Maak het aan en open het voor bewerking:

  1. nano droplets.tf

Voeg de volgende regels toe:

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"
}

Voor de parameter count, die aangeeft hoeveel instanties van een bron moeten worden gemaakt, geef je de variabele droplet_count door. De waarde ervan wordt gespecificeerd wanneer de module wordt aangeroepen vanuit de hoofdprojectcode. De naam van elke ingezette Droplet zal verschillend zijn, wat je bereikt door het indexnummer van de huidige Droplet toe te voegen aan de meegeleverde groepsnaam. Het implementeren van de Droplets zal plaatsvinden in de regio fra1 en ze zullen Ubuntu 20.04 uitvoeren.

Als je klaar bent, sla het bestand op en sluit het af.

Met de Droplets nu gedefinieerd, kun je doorgaan met het maken van de Load Balancer. Je zult de definitie van de bron opslaan in een bestand met de naam lb.tf. Maak het aan en open het voor bewerking door het volgende uit te voeren:

  1. nano lb.tf

Voeg de definitie van de bron toe:

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
  ]
}

Je definieert de Load Balancer met de groepsnaam in de naam om het onderscheidbaar te maken. Je implementeert het in de regio fra1 samen met de Droplets. De volgende twee secties specificeren de doel- en bewakingspoorten en protocollen.

De gemarkeerde droplet_ids blok neemt de ID’s van de Droplets aan die beheerd moeten worden door de Load Balancer. Aangezien er meerdere Droplets zijn en hun aantal niet van tevoren bekend is, gebruik je een for-lus om door de verzameling Droplets (digitalocean_droplet.droplets) te traverseren en hun ID’s te nemen. Je omringt de for-lus met haakjes ([]) zodat de resulterende verzameling een lijst zal zijn.

Sla het bestand op en sluit het af.

Je hebt nu de Droplet, Load Balancer en variabelen voor je module gedefinieerd. Je moet de providervereisten definiëren, waarbij je aangeeft welke providers de module gebruikt, inclusief hun versie en waar ze zich bevinden. Sinds Terraform 0.13 moeten modules expliciet de bronnen van niet door Hashicorp onderhouden providers definiëren; dit komt doordat ze deze niet erven van het ouderproject.

Je slaat de providervereisten op in een bestand met de naam provider.tf. Maak het klaar voor bewerking door het volgende uit te voeren:

  1. nano provider.tf

Voeg de volgende regels toe om de digitalocean provider te vereisen:

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

Sla het bestand op en sluit het af wanneer je klaar bent. De droplet-lb module vereist nu de digitalocean provider.

Modules ondersteunen ook uitvoer, die je kunt gebruiken om interne informatie over de status van hun resources te extraheren. Je zult een uitvoer definiëren die het IP-adres van de Load Balancer blootlegt, en deze opslaan in een bestand met de naam outputs.tf. Maak het klaar voor bewerking:

  1. nano outputs.tf

Voeg de volgende definitie toe:

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

Deze uitvoer haalt het IP-adres van de Load Balancer op. Sla het bestand op en sluit het af.

De module droplet-lb is nu functioneel compleet en klaar voor implementatie. U zult het aanroepen vanuit de hoofdcode, die u zult opslaan in de hoofdmap van het project. Navigeer eerst daar naartoe door twee keer omhoog te gaan door uw bestandsdirectory:

  1. cd ../..

Vervolgens maakt u een bestand genaamd main.tf aan en opent u het voor bewerking, waarin u de module zult gebruiken:

  1. nano main.tf

Voeg de volgende regels toe:

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

  droplet_count = 3
  group_name    = "group1"
}

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

In deze verklaring roept u de module droplet-lb aan die zich bevindt in de opgegeven map als source. U configureert de invoer die het biedt, droplet_count en group_name, die is ingesteld op group1 zodat u later onderscheid kunt maken tussen instanties.

Aangezien de uitvoer van het Load Balancer IP is gedefinieerd in een module, wordt deze niet automatisch weergegeven wanneer u het project toepast. De oplossing hiervoor is om een andere uitvoer te maken die de waarde ophaalt (loadbalancer_ip).

Sla het bestand op en sluit het af wanneer u klaar bent.

Initialiseer de module door het volgende uit te voeren:

  1. terraform init

De uitvoer ziet er als volgt uit:

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.

U kunt proberen het project te plannen om te zien welke acties Terraform zou uitvoeren door het volgende uit te voeren:

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

De uitvoer zal vergelijkbaar zijn met dit:

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

Deze uitvoer geeft aan dat Terraform drie Droplets zal aanmaken, genaamd group1-0, group1-1 en group1-2, en ook een Load Balancer genaamd group1-lb, die het verkeer naar en van de drie Droplets zal beheren.

Je kunt proberen het project toe te passen op de cloud door het volgende uit te voeren:

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

Voer ja in wanneer daarom wordt gevraagd. De uitvoer zal alle acties tonen en het IP-adres van de Load Balancer zal ook worden getoond:

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

Je hebt een module gemaakt met een aanpasbaar aantal Droplets en een Load Balancer die automatisch wordt geconfigureerd om het inkomende en uitgaande verkeer te beheren.

Hernoemen van Gedeployde Bronnen

In de vorige sectie heb je de module geïmplementeerd die je hebt gedefinieerd en heb je deze groups genoemd. Als je ooit de naam wilt wijzigen, zal het eenvoudigweg hernoemen van de module-aanroep niet de verwachte resultaten opleveren. Het hernoemen van de aanroep zal Terraform dwingen bronnen te vernietigen en opnieuw te maken, wat leidt tot overmatige downtime.

Bijvoorbeeld, open main.tf om te bewerken door het volgende uit te voeren:

  1. nano main.tf

Hernoem de module groups naar groups_renamed, zoals hieronder gemarkeerd:

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

  droplet_count = 3
  group_name    = "group1"
}

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

Sla het bestand op en sluit het af. Initialiseer vervolgens het project opnieuw:

  1. terraform init

Je kunt nu het project plannen:

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

De uitvoer zal lang zijn, maar er zal iets vergelijkbaars uitzien als dit:

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] zal worden vernietigd ... # module.groups_renamed.digitalocean_droplet.droplets[0] zal worden gemaakt ...

Terraform zal je vragen de bestaande instanties te vernietigen en nieuwe te maken. Dit is destructief en onnodig, en kan leiden tot ongewenste downtime.

In plaats daarvan kun je met behulp van het moved-blok Terraform instrueren om oude bronnen onder de nieuwe naam te verplaatsen. Open main.tf om te bewerken en voeg de volgende regels toe aan het einde van het bestand:

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

Als je klaar bent, sla je het bestand op en sluit je het af.

Je kunt nu het project plannen:

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

Wanneer je plant met het moved-blok aanwezig in main.tf, wil Terraform de bronnen verplaatsen in plaats van ze opnieuw aan te maken:

Output
Terraform will perform the following actions: # module.groups.digitalocean_droplet.droplets[0] is verplaatst naar module.groups_renamed.digitalocean_droplet.droplets[0] ... # module.groups.digitalocean_droplet.droplets[1] is verplaatst naar module.groups_renamed.digitalocean_droplet.droplets[1] ...

Het verplaatsen van resources verandert hun positie in de Terraform-state, wat betekent dat de daadwerkelijke cloudresources niet worden gewijzigd, vernietigd of opnieuw gemaakt.

Omdat je de configuratie aanzienlijk zult wijzigen in de volgende stap, vernietig je de ingezette resources door het volgende uit te voeren:

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

Voer ja in wanneer hierom wordt gevraagd. De uitvoer eindigt met:

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

In deze sectie heb je resources hernoemd in je Terraform-project zonder ze daarbij te vernietigen. Je gaat nu meerdere instanties van een module vanuit dezelfde code implementeren met behulp van for_each en count.

Het implementeren van meerdere module-instanties

In deze sectie gebruik je count en for_each om de droplet-lb-module meerdere keren te implementeren met aanpassingen.

Gebruik van count

Een manier om meerdere exemplaren van dezelfde module tegelijk te implementeren, is door het aantal door te geven aan de count-parameter, die automatisch beschikbaar is voor elke module. Open main.tf om te bewerken:

  1. nano main.tf

Wijzig het zodat het er zo uitziet, verwijder de bestaande outputdefinitie en het verplaatste-blok:

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

  count  = 3

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

Door count in te stellen op 3, geef je Terraform de opdracht om de module drie keer te implementeren, elk met een andere groepsnaam. Wanneer je klaar bent, sla het bestand op en sluit het.

Plan de implementatie door het volgende uit te voeren:

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

De uitvoer zal lang zijn en er zal zo uitzien:

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] zal worden aangemaakt ... # module.groups[0].digitalocean_droplet.droplets[1] zal worden aangemaakt ... # module.groups[0].digitalocean_droplet.droplets[2] zal worden aangemaakt ... # module.groups[0].digitalocean_loadbalancer.www-lb zal worden aangemaakt ... # module.groups[1].digitalocean_droplet.droplets[0] zal worden aangemaakt ... # module.groups[1].digitalocean_droplet.droplets[1] zal worden aangemaakt ... # module.groups[1].digitalocean_droplet.droplets[2] zal worden aangemaakt ... # module.groups[1].digitalocean_loadbalancer.www-lb zal worden aangemaakt ... # module.groups[2].digitalocean_droplet.droplets[0] zal worden aangemaakt ... # module.groups[2].digitalocean_droplet.droplets[1] zal worden aangemaakt ... # module.groups[2].digitalocean_droplet.droplets[2] zal worden aangemaakt ... # module.groups[2].digitalocean_loadbalancer.www-lb zal worden aangemaakt ... Plan: 12 to add, 0 to change, 0 to destroy. ...

Terraform geeft details in de uitvoer dat elk van de drie module-instanties drie Droplets en een Load Balancer zou hebben die eraan zijn gekoppeld.

Met behulp van for_each

Je kunt for_each gebruiken voor modules wanneer je complexere instantieaanpassingen nodig hebt, of wanneer het aantal instanties afhankelijk is van externe gegevens (vaak gepresenteerd als kaarten) die niet bekend zijn tijdens het schrijven van de code.

Je gaat nu een kaart definiëren die groepsnamen koppelt aan Droplet-tellingen en instanties van droplet-lb volgens die kaart implementeren. Open main.tf om te bewerken door het volgende uit te voeren:

  1. nano main.tf

Wijzig het bestand zodat het er als volgt uitziet:

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
}

Je definieert eerst een kaart genaamd group_counts die aangeeft hoeveel Droplets een bepaalde groep moet hebben. Vervolgens roep je de module droplet-lb aan, maar geef je aan dat de for_each-lus moet werken op var.group_counts, de kaart die je net daarvoor hebt gedefinieerd. droplet_count neemt each.value, de waarde van het huidige paar, die het aantal Droplets voor de huidige groep is. group_name ontvangt de naam van de groep.

Sla het bestand op en sluit het af wanneer je klaar bent.

Probeer de configuratie toe te passen door het volgende uit te voeren:

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

De uitvoer zal gedetailleerd beschrijven welke acties Terraform zou ondernemen om de twee groepen met hun Droplets en Load Balancers te maken:

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["groep1"].digitalocean_droplet.droplets[0] zal worden aangemaakt ... # module.groups["groep1"].digitalocean_loadbalancer.www-lb zal worden aangemaakt ... # module.groups["groep2"].digitalocean_droplet.droplets[0] zal worden aangemaakt ... # module.groups["groep2"].digitalocean_droplet.droplets[1] zal worden aangemaakt ... # module.groups["groep2"].digitalocean_droplet.droplets[2] zal worden aangemaakt ... # module.groups["groep2"].digitalocean_loadbalancer.www-lb zal worden aangemaakt ...

In deze stap heb je count en for_each gebruikt om meerdere aangepaste instanties van dezelfde module vanuit dezelfde code te implementeren.

Conclusie

In deze tutorial heb je Terraform-modules gemaakt en geïmplementeerd. Je hebt modules gebruikt om logisch gekoppelde bronnen samen te voegen en aangepast om meerdere verschillende instanties vanuit een centrale code-definitie te implementeren. Je hebt ook uitvoer gebruikt om attributen van bronnen die in de module zijn opgenomen te tonen.

Als je meer wilt weten over Terraform, bekijk dan onze serie Hoe infrastructuur te beheren met Terraform.

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