O introducere în Terraform pentru începători – Tutorial Terraform

Ce este Terraform și de ce ar trebui să te intereseze?

Te-ai întrebat vreodată ce este Terraform? Haide să explorăm împreună acest instrument esențial în lumea DevOps.

Infrastructura ca și cod (IaC) a devenit un concept fundamental pentru profesioniștii DevOps. Aceasta reprezintă modalitatea de a gestiona și de a aloca întreaga infrastructură IT, atât cea fizică, cât și virtuală, prin intermediul fișierelor de definiție lizibile de mașini. Practic, este o abordare a operațiunilor inspirată din ingineria software. IaC facilitează automatizarea completă a centrelor de date prin utilizarea scripturilor de programare.

Deși Infrastructura ca și Cod oferă numeroase beneficii, aceasta vine și cu anumite provocări:

  • Necesitatea învățării limbajelor de codificare.
  • Dificultatea de a anticipa impactul modificărilor.
  • Complexitatea anulării schimbărilor.
  • Probleme în monitorizarea modificărilor.
  • Imposibilitatea automatizării anumitor resurse.
  • Gestionarea diferitelor medii de infrastructură.

Terraform a fost creat tocmai pentru a soluționa aceste provocări.

Definiția Terraform

Terraform este un instrument open-source de Infrastructură ca și Cod, dezvoltat de HashiCorp. Este folosit pentru a defini și a aloca infrastructura completă utilizând un limbaj declarativ ușor de învățat.

Terraform permite stocarea configurației infrastructurii cloud sub formă de cod, având o funcționalitate similară cu instrumente precum CloudFormation, care este utilizat pentru automatizarea infrastructurii AWS. Însă, spre deosebire de CloudFormation, Terraform poate fi utilizat pe multiple platforme cloud.

Iată câteva dintre avantajele utilizării Terraform:

  • Facilitează orchestrarea, nu doar gestionarea configurației.
  • Suportă numeroși furnizori, precum AWS, Azure, GCP, DigitalOcean și mulți alții.
  • Asigură o infrastructură imuabilă, unde configurația se schimbă fără probleme.
  • Utilizează un limbaj ușor de înțeles, HCL (HashiCorp Configuration Language).
  • Este ușor de portat între diferiți furnizori.
  • Funcționează pe o arhitectură client, eliminând necesitatea gestionării adiționale a configurației pe un server.

Concepte fundamentale Terraform

Iată o listă a conceptelor și terminologiei de bază utilizate în Terraform:

  • Variabile: Cunoscute și ca variabile de intrare, sunt perechi cheie-valoare folosite de modulele Terraform pentru a permite personalizarea.
  • Furnizor: Este un plugin care permite interacțiunea cu API-urile de servicii și accesarea resurselor aferente.
  • Modul: Un director care conține șabloane Terraform, unde sunt definite toate configurațiile.
  • Stare: Informații stocate în cache despre infrastructura gestionată de Terraform și configurațiile asociate.
  • Resurse: Blocuri de cod care descriu unul sau mai multe obiecte de infrastructură (instanțe de calcul, rețele virtuale etc.) utilizate în configurare și management.
  • Sursa de date: Mecanism implementat de furnizori pentru a returna informații despre obiectele externe Terraform.
  • Valori de ieșire: Valori returnate de un modul Terraform, utilizabile de alte configurații.
  • Plan: Etapa în care se determină ce trebuie creat, actualizat sau distrus pentru a alinia starea actuală a infrastructurii cu cea dorită.
  • Aplicare: Etapa în care se implementează modificările asupra stării actuale a infrastructurii pentru a atinge starea dorită.

Ciclul de viață Terraform

Ciclul de viață Terraform este format din etapele: inițializare, planificare, aplicare și distrugere.

  • `terraform init`: Inițializează directorul de lucru, care conține fișierele de configurare.
  • `terraform plan`: Generează un plan de execuție pentru a atinge starea dorită a infrastructurii, luând în considerare modificările din fișierele de configurare.
  • `terraform apply`: Implementează modificările asupra infrastructurii conform planului, aducând infrastructura la starea dorită.
  • `terraform destroy`: Șterge toate resursele de infrastructură, marcate ca fiind „contaminate” după faza de aplicare.

Cum funcționează Terraform?

Arhitectura Terraform este alcătuită din două componente majore:

Nucleul Terraform

Nucleul Terraform utilizează două surse de intrare pentru funcționarea sa:

Prima sursă este configurația Terraform, definită de utilizator, unde se specifică ce anume trebuie creat sau alocat. A doua sursă este „starea”, în care Terraform menține o evidență a configurației actuale a infrastructurii.

Nucleul Terraform analizează aceste intrări și stabilește un plan de acțiune. Acesta compară starea actuală cu configurația dorită și calculează ce trebuie creat, actualizat sau șters pentru a aduce infrastructura la starea dorită.

Furnizorii

A doua componentă a arhitecturii o reprezintă furnizorii de tehnologii specifice. Aceștia pot fi furnizori de servicii cloud, precum AWS, Azure, GCP, sau alte platforme de infrastructură. De asemenea, pot fi furnizori pentru componente de nivel înalt, cum ar fi Kubernetes sau alte instrumente platform-as-a-service, chiar și software ca instrument de autoservire.

Aceștia oferă posibilitatea de a crea infrastructură la diverse niveluri. De exemplu, se poate crea infrastructura AWS, apoi se poate implementa Kubernetes deasupra acesteia și, ulterior, se pot crea servicii și componente în cadrul clusterului Kubernetes.

Terraform dispune de peste o sută de furnizori pentru diverse tehnologii, fiecare oferind acces la resursele specifice. Prin intermediul furnizorului AWS, de exemplu, se pot accesa sute de resurse precum instanțe EC2 sau utilizatori AWS. Similar, furnizorul Kubernetes permite accesul la resurse precum servicii, implementări și spații de nume.

Astfel, Terraform ajută la alocarea și gestionarea completă a configurației aplicației, de la infrastructură până la nivelul aplicației.

Hai să trecem la partea practică. 👨‍💻

Vom instala Terraform pe Ubuntu și vom aloca o infrastructură de bază.

Instalarea Terraform

Începem prin a descărca cel mai recent pachet Terraform.

Accesați pagina oficială de descărcare pentru a obține cea mai recentă versiune adecvată sistemului dvs. de operare.

[email protected]:~$ wget https://releases.hashicorp.com/terraform/0.13.0/terraform_0.13.0_linux_amd64.zip
--2020-08-14 16:55:38--  https://releases.hashicorp.com/terraform/0.13.0/terraform_0.13.0_linux_amd64.zip
Resolving releases.hashicorp.com (releases.hashicorp.com)... 151.101.153.183, 2a04:4e42:24::439
Connecting to releases.hashicorp.com (releases.hashicorp.com)|151.101.153.183|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: 34851622 (33M) [application/zip]
Saving to: ‘terraform_0.13.0_linux_amd64.zip’

terraform_0.13.0_linux_amd64.zip     100%[=================================================================>]  33.24M  90.3KB/s    in 5m 28s

2020-08-14 17:01:06 (104 KB/s) - ‘terraform_0.13.0_linux_amd64.zip’ saved [34851622/34851622]

Extrageți pachetul descărcat.

[email protected]:~$ unzip terraform_0.13.0_linux_amd64.zip
Archive:  terraform_0.13.0_linux_amd64.zip
  inflating: terraform

Mutați executabilul Terraform în calea de mai jos. Apoi, verificați versiunea Terraform.

[email protected]:~$ sudo mv terraform /usr/local/bin/
[sudo] password for tipstrick.ro:
[email protected]:~$ terraform -v
Terraform v0.13.0

Mai jos puteți vedea lista comenzilor disponibile în Terraform pentru execuție.

[email protected]:~$ terraform
Usage: terraform [-version] [-help] <command> [args]

The available commands for execution are listed below.
The most common, useful commands are shown first, followed by
less common or more advanced commands. If you're just getting
started with Terraform, stick with the common commands. For the
other commands, please read the help and docs before usage.

Common commands:
    apply              Builds or changes infrastructure
    console            Interactive console for Terraform interpolations
    destroy            Destroy Terraform-managed infrastructure
    env                Workspace management
    fmt                Rewrites config files to canonical format
    get                Download and install modules for the configuration
    graph              Create a visual graph of Terraform resources
    import             Import existing infrastructure into Terraform
    init               Initialize a Terraform working directory
    login              Obtain and save credentials for a remote host
    logout             Remove locally-stored credentials for a remote host
    output             Read an output from a state file
    plan               Generate and show an execution plan
    providers          Prints a tree of the providers used in the configuration
    refresh            Update local state file against real resources
    show               Inspect Terraform state or plan
    taint              Manually mark a resource for recreation
    untaint            Manually unmark a resource as tainted
    validate           Validates the Terraform files
    version            Prints the Terraform version
    workspace          Workspace management

All other commands:
    0.12upgrade        Rewrites pre-0.12 module source code for v0.12
    0.13upgrade        Rewrites pre-0.13 module source code for v0.13
    debug              Debug output management (experimental)
    force-unlock       Manually unlock the terraform state
    push               Obsolete command for Terraform Enterprise legacy (v1)
    state              Advanced state management

Alocarea unei instanțe AWS EC2 cu Terraform

În această demonstrație, vom lansa o nouă instanță AWS EC2 folosind Terraform.

Începem prin a crea un director de lucru pentru demonstrația noastră cu Terraform.

[email protected]:~$ mkdir terraform_demo

Navigăm în acest director și creăm un fișier de configurare Terraform, unde definim furnizorul și resursele necesare pentru lansarea instanței AWS EC2.

[email protected]:~$ cd terraform_demo/
[email protected]:~/terraform_demo$ gedit awsec2.tf

provider "aws" {
  access_key = "B5KG6Fe5GUKIATUF5UD"
  secret_key = "R4gb65y56GBF6765ejYSJA4YtaZ+T6GY7H"
  region     = "us-west-2"
}

resource "aws_instance" "terraform_demo" {
  ami           = "ami-0a634ae95e11c6f91"
  instance_type = "t2.micro"
}

Notă: Am înlocuit cheile de acces și secrete pentru a menține securitatea. Trebuie să le utilizați pe cele proprii.

În configurația de mai sus, specificăm furnizorul ca fiind AWS. În cadrul acestuia, furnizăm datele de autentificare ale utilizatorului AWS, precum și regiunea în care va fi lansată instanța.

În secțiunea resurselor, indicăm AMI-ul pentru Ubuntu (ami-0a634ae95e11c6f91) și definim tipul instanței ca fiind t2.micro.

După cum se poate observa, fișierul de configurare este ușor de citit și de înțeles, chiar și pentru cineva care nu este un programator expert.

`terraform init`

Primul pas este inițializarea Terraform.

[email protected]:~/terraform_demo$ terraform init

Initializing the backend...

Initializing provider plugins...
- Using previously-installed hashicorp/aws v3.2.0

The following providers do not have any version constraints in configuration,
so the latest version was installed.

To prevent automatic upgrades to new major versions that may contain breaking
changes, we recommend adding version constraints in a required_providers block
in your configuration, with the constraint strings suggested below.

* hashicorp/aws: version = "~> 3.2.0"

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.

`terraform plan`

Următoarea etapă este planificarea, în care se va genera planul de execuție pentru crearea și alocarea infrastructurii.

[email protected]:~/terraform_demo$ terraform plan
Refreshing Terraform state in-memory prior to plan...
The refreshed state will be used to calculate this plan, but will not be
persisted to local or remote state storage.

------------------------------------------------------------------------

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:

  # aws_instance.terraform_demo will be created
  + resource "aws_instance" "terraform_demo" {
      + ami                                  = "ami-0a634ae95e11c6f91"
      + arn                                  = (known after apply)
      + associate_public_ip_address          = (known after apply)
      + availability_zone                    = (known after apply)
      + cpu_core_count                       = (known after apply)
      + cpu_threads_per_core                 = (known after apply)
      + get_password_data                    = false
      + host_id                              = (known after apply)
      + id                                   = (known after apply)
      + instance_state                       = (known after apply)
      + instance_type                        = "t2.micro"
      + ipv6_address_count                   = (known after apply)
      + ipv6_addresses                       = (known after apply)
      + key_name                             = (known after apply)
      + outpost_arn                          = (known after apply)
      + password_data                        = (known after apply)
      + placement_group                      = (known after apply)
      + primary_network_interface_id         = (known after apply)
      + private_dns                          = (known after apply)
      + private_ip                           = (known after apply)
      + public_dns                           = (known after apply)
      + public_ip                            = (known after apply)
      + secondary_private_ips                = (known after apply)
      + security_groups                      = (known after apply)
      + source_dest_check                    = true
      + subnet_id                            = (known after apply)
      + tenancy                              = (known after apply)
      + volume_tags                          = (known after apply)
      + vpc_security_group_ids               = (known after apply)

      + ebs_block_device {
          + delete_on_termination            = (known after apply)
          + device_name                      = (known after apply)
          + encrypted                        = (known after apply)
          + iops                             = (known after apply)
          + kms_key_id                       = (known after apply)
          + snapshot_id                      = (known after apply)
          + volume_id                        = (known after apply)
          + volume_size                      = (known after apply)
          + volume_type                      = (known after apply)
        }

      + ephemeral_block_device {
          + device_name                      = (known after apply)
          + no_device                        = (known after apply)
          + virtual_name                     = (known after apply)
        }

      + metadata_options {
          + http_endpoint                    = (known after apply)
          + http_put_response_hop_limit       = (known after apply)
          + http_tokens                      = (known after apply)
        }

      + network_interface {
          + delete_on_termination            = (known after apply)
          + device_index                     = (known after apply)
          + network_interface_id             = (known after apply)
        }

      + root_block_device {
          + delete_on_termination            = (known after apply)
          + device_name                      = (known after apply)
          + encrypted                        = (known after apply)
          + iops                             = (known after apply)
          + kms_key_id                       = (known after apply)
          + volume_id                        = (known after apply)
          + volume_size                      = (known after apply)
          + volume_type                      = (known after apply)
        }
    }

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

------------------------------------------------------------------------

Note: You didn't specify an "-out" parameter to save this plan, so Terraform
can't guarantee that exactly these actions will be performed if
"terraform apply" is subsequently run.

`terraform apply`

În etapa de aplicare, se va executa fișierul de configurare și se va lansa instanța AWS EC2. Când rulați comanda de aplicare, veți fi întrebat „Doriți să efectuați aceste acțiuni?”. Trebuie să scrieți `yes` și să apăsați Enter.

[email protected]:~/terraform_demo$ terraform apply

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:

  # aws_instance.terraform_demo will be created
  + resource "aws_instance" "terraform_demo" {
      + ami                                  = "ami-0a634ae95e11c6f91"
      + arn                                  = (known after apply)
      + associate_public_ip_address          = (known after apply)
      + availability_zone                    = (known after apply)
      + cpu_core_count                       = (known after apply)
      + cpu_threads_per_core                 = (known after apply)
      + get_password_data                    = false
      + host_id                              = (known after apply)
      + id                                   = (known after apply)
      + instance_state                       = (known after apply)
      + instance_type                        = "t2.micro"
      + ipv6_address_count                   = (known after apply)
      + ipv6_addresses                       = (known after apply)
      + key_name                             = (known after apply)
      + outpost_arn                          = (known after apply)
      + password_data                        = (known after apply)
      + placement_group                      = (known after apply)
      + primary_network_interface_id         = (known after apply)
      + private_dns                          = (known after apply)
      + private_ip                           = (known after apply)
      + public_dns                           = (known after apply)
      + public_ip                            = (known after apply)
      + secondary_private_ips                = (known after apply)
      + security_groups                      = (known after apply)
      + source_dest_check                    = true
      + subnet_id                            = (known after apply)
      + tenancy                              = (known after apply)
      + volume_tags                          = (known after apply)
      + vpc_security_group_ids               = (known after apply)

      + ebs_block_device {
          + delete_on_termination            = (known after apply)
          + device_name                      = (known after apply)
          + encrypted                        = (known after apply)
          + iops                             = (known after apply)
          + kms_key_id                       = (known after apply)
          + snapshot_id                      = (known after apply)
          + volume_id                        = (known after apply)
          + volume_size                      = (known after apply)
          + volume_type                      = (known after apply)
        }

      + ephemeral_block_device {
          + device_name                      = (known after apply)
          + no_device                        = (known after apply)
          + virtual_name                     = (known after apply)
        }

      + metadata_options {
          + http_endpoint                    = (known after apply)
          + http_put_response_hop_limit       = (known after apply)
          + http_tokens                      = (known after apply)
        }

      + network_interface {
          + delete_on_termination            = (known after apply)
          + device_index                     = (known after apply)
          + network_interface_id             = (known after apply)
        }

      + root_block_device {
          + delete_on_termination            = (known after apply)
          + device_name                      = (known after apply)
          + encrypted                        = (known after apply)
          + iops                             = (known after apply)
          + kms_key_id                       = (known after apply)
          + volume_id                        = (known after apply)
          + volume_size                      = (known after apply)
          + volume_type                      = (known after apply)
        }
    }

Plan: 1 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

aws_instance.terraform_demo: Creating...
aws_instance.terraform_demo: Still creating... [10s elapsed]
aws_instance.terraform_demo: Still creating... [20s elapsed]
aws_instance.terraform_demo: Still creating... [30s elapsed]
aws_instance.terraform_demo: Still creating... [40s elapsed]
aws_instance.terraform_demo: Creation complete after 44s [id=i-0eec33286ea4b0740]

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

Accesați tabloul de bord AWS EC2 și veți observa că s-a creat o nouă instanță, având ID-ul specificat la sfârșitul comenzii `apply`.

Felicitări! Ați lansat cu succes o instanță AWS EC2 folosind Terraform.

`terraform destroy`

În final, dacă doriți să ștergeți infrastructura, trebuie să executați comanda `destroy`.

[email protected]:~/terraform_demo$ terraform destroy
aws_instance.terraform_demo: Refreshing state... [id=i-0eec33286ea4b0740]

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

Terraform will perform the following actions:

  # aws_instance.terraform_demo will be destroyed
  - resource "aws_instance" "terraform_demo" {
      - ami                                  = "ami-0a634ae95e11c6f91" -> null
      - arn                                  = "arn:aws:ec2:us-west-2:259212389929:instance/i-0eec33286ea4b0740" -> null
      - associate_public_ip_address          = true -> null
      - availability_zone                    = "us-west-2c" -> null
      - cpu_core_count                       = 1 -> null
      - cpu_threads_per_core                 = 1 -> null
      - disable_api_termination              = false -> null
      - ebs_optimized                        = false -> null
      - get_password_data                    = false -> null
      - hibernation                          = false -> null
      - id                                   = "i-0eec33286ea4b0740" -> null
      - instance_state                       = "running" -> null
      - instance_type                        = "t2.micro" -> null
      - ipv6_address_count                   = 0 -> null
      - ipv6_addresses                       = [] -> null
      - monitoring                           = false -> null
      - primary_network_interface_id         = "eni-02a46f2802fd15634" -> null
      - private_dns                          = "ip-172-31-13-160.us-west-2.compute.internal" -> null
      - private_ip                           = "172.31.13.160" -> null
      - public_dns                           = "ec2-34-221-77-94.us-west-2.compute.amazonaws.com" -> null
      - public_ip                            = "34.221.77.94" -> null
      - secondary_private_ips                = [] -> null
      - security_groups                      = [
          - "default",
        ] -> null
      - source_dest_check                    = true -> null
      - subnet_id                            = "subnet-5551200c" -> null
      - tags                                 = {} -> null
      - tenancy                              = "default" -> null
      - volume_tags                          = {} -> null
      - vpc_security_group_ids               = [
          - "sg-b5b480d1",
        ] -> null

      - credit_specification {
          - cpu_credits                        = "standard" -> null
        }

      - metadata_options {
          - http_endpoint                    = "enabled" -> null
          - http_put_response_hop_limit       = 1 -> null
          - http_tokens                      = "optional" -> null
        }

      - root_block_device {
          - delete_on_termination            = true -> null
          - device_name                      = "/dev/sda1" -> null
          - encrypted                        = false -> null
          - iops                             = 100 -> null
          - volume_id                        = "vol-0be2673afff6b1a86" -> null
          - volume_size                      = 8 -> null
          - volume_type                      = "gp2" -> null
        }
    }

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

Do you really want to destroy all resources?
  Terraform will destroy all your managed infrastructure, as shown above.
  There is no undo. Only 'yes' will be accepted to confirm.

  Enter a value: yes

aws_instance.terraform_demo: Destroying... [id=i-0eec33286ea4b0740]
aws_instance.terraform_demo: Still destroying... [id=i-0eec33286ea4b0740, 10s elapsed]
aws_instance.terraform_demo: Still destroying... [id=i-0eec33286ea4b0740, 20s elapsed]
aws_instance.terraform_demo: Still destroying... [id=i-0eec33286ea4b0740, 30s elapsed]
aws_instance.terraform_demo: Destruction complete after 34s

Destroy complete! Resources: 1 destroyed.

Verificând din nou tabloul de bord EC2, veți observa că instanța a fost terminată.

Concluzie

Sper că cele de mai sus v-au oferit o idee clară despre cum să începeți cu Terraform. Vă încurajez să încercați exemplul prezentat.

De asemenea, vă sugerez să explorați și alte instrumente de automatizare a infrastructurii.

Dacă sunteți interesat să aprofundați cunoștințele, vă recomand să consultați Cursul „Learn DevOps with Terraform”.