Πώς λειτουργεί το Terraform

8 λεπτά
Κοινό
αρχάριος, έχεις διαβάσει το μάθημα 01
Διάρκεια
30 έως 40 λεπτά
Ενότητα
1/7
Στόχος
να ονομάζεις τα τέσσερα κομμάτια του Terraform, να διαβάζεις ένα μπλοκ HCL λέξη προς λέξη, και να αναγνωρίζεις στο τερματικό τις φράσεις που εμφανίζουν τα init, plan, apply και destroy όταν όλα πάνε καλά

Μια συνολική εικόνα

Το Terraform συνδέει τέσσερα στοιχεία:

  • Η διαμόρφωση περιγράφει το αποτέλεσμα που θέλεις.
  • Το Terraform διαβάζει αυτή τη διαμόρφωση και κατασκευάζει ένα γράφημα εξαρτήσεων.
  • Οι providers μεταφράζουν τα αιτήματα του Terraform σε κλήσεις προς τα API των πλατφορμών.
  • Το state συνδέει τα μπλοκ του κώδικά σου με τα πραγματικά, απομακρυσμένα αντικείμενα.

Στην εικόνα του μαθήματος 01: η διαμόρφωση είναι το σχέδιο, το state είναι η καταγραφή, οι providers είναι τα επαγγέλματα, και το CLI είναι ο αρχιτέκτονας.

Η γλώσσα HCL

Το Terraform χρησιμοποιεί κυρίως το HashiCorp Configuration Language, ή HCL. Ο κώδικας οργανώνεται σε μπλοκ.

hcl
resource "aws_s3_bucket" "logs" {
  bucket = var.bucket_name

  tags = {
    Environment = var.environment
    ManagedBy   = "Terraform"
  }
}

Σε αυτό το παράδειγμα:

  • το resource ανακοινώνει ένα αντικείμενο που το Terraform πρέπει να διαχειριστεί·
  • το aws_s3_bucket είναι ο τύπος που παρέχει ο provider AWS·
  • το logs είναι το τοπικό όνομα που χρησιμοποιείται σε αυτό το module·
  • τα bucket και tags είναι ορίσματα·
  • τα var.bucket_name και var.environment προέρχονται από μεταβλητές.

Ο τύπος και το τοπικό όνομα σχηματίζουν μαζί τη διεύθυνση του πόρου: aws_s3_bucket.logs. Με αυτό το όνομα θα τον ξαναβρίσκεις σε ένα plan, στο terraform state list και στις αναφορές άλλων μπλοκ.

Provider

Ένας provider είναι ένα plugin που γνωρίζει το API μιας πλατφόρμας και παρέχει τύπους πόρων και πηγές δεδομένων. Το Terraform εγκαθιστά τους απαιτούμενους providers κατά το terraform init. Οι περιορισμοί έκδοσης και το αρχείο κλειδώματος κάνουν τις εκτελέσεις πιο προβλέψιμες. Επίσημη αναφορά — Απαιτήσεις των providers

hcl
terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 6.0"
    }
  }
}

provider "aws" {
  region = "ca-central-1"
}

Η έκδοση του Terraform CLI και η έκδοση ενός provider είναι δύο διαφορετικά πράγματα. Τις ελέγχεις ξεχωριστά: το terraform version δείχνει την πρώτη (Terraform v1.12.2 στο μηχάνημα του μαθήματος), το .terraform.lock.hcl παγιώνει τη δεύτερη.

Πόρος και data source

Ένας πόρος (resource) αντιπροσωπεύει συνήθως ένα αντικείμενο που το Terraform δημιουργεί ή διαχειρίζεται.

hcl
resource "aws_security_group" "web" {
  name = "web"
}

Μια data source διαβάζει ένα αντικείμενο ή μια πληροφορία που υπάρχει ήδη, χωρίς να γίνεται αυτόματα ιδιοκτήτης της.

hcl
data "aws_vpc" "default" {
  default = true
}

Στη συνέχεια μπορείς να αναφερθείς στο data.aws_vpc.default.id μέσα σε έναν πόρο.

Μεταβλητές, τοπικές τιμές και outputs

Οι μεταβλητές είναι οι διαμορφώσιμες είσοδοι:

hcl
variable "environment" {
  type        = string
  description = "Nom de l'environnement"

  validation {
    condition     = contains(["dev", "test", "prod"], var.environment)
    error_message = "L'environnement doit être dev, test ou prod."
  }
}

Τα locals υπολογίζουν εσωτερικές τιμές ώστε να αποφεύγονται οι επαναλήψεις:

hcl
locals {
  prefix = "application-${var.environment}"
}

Τα outputs εκθέτουν χρήσιμα αποτελέσματα:

hcl
output "bucket_id" {
  description = "Identifiant du bucket"
  value       = aws_s3_bucket.logs.id
}

Ένα έργο χωρίς μπλοκ output δεν έχει τίποτα να εμφανίσει: το terraform output απαντά τότε Warning: No outputs found. Αυτή είναι η περίπτωση του έργου 01· τα outputs έρχονται στο έργο 02.

Το γράφημα εξαρτήσεων

Το Terraform εντοπίζει μια εξάρτηση όταν ένας πόρος αναφέρεται σε άλλον.

hcl
resource "aws_s3_object" "journal" {
  bucket = aws_s3_bucket.logs.id
  key    = "journal.txt"
  source = "journal.txt"
}

Το αντικείμενο εξαρτάται από το bucket. Το Terraform πρέπει λοιπόν να δημιουργήσει το bucket πριν στείλει το αρχείο. Οι πόροι χωρίς εξάρτηση μεταξύ τους μπορούν να επεξεργαστούν παράλληλα. Η σειρά των μπλοκ στο αρχείο δεν έχει καμία σημασία: μόνο η αναφορά μετράει.

Η κεντρική ροή εργασίας

Η HashiCorp συνοψίζει τη ροή εργασίας του Terraform σε τρία στάδια: Write, Plan, Apply. Επίσημη αναφορά — Core workflow

Πριν από το πρώτο από αυτά τα στάδια, υπάρχει μία μοναδική κίνηση ανά φάκελο: το terraform init. Οι παρακάτω έξοδοι είναι αυτές του έργου 01 αυτής της ενότητας, καταγραμμένες με το Terraform 1.12.2 στο μηχάνημα του μαθήματος· θα τις ξαναβρείς λέξη προς λέξη όταν το κάνεις εσύ ο ίδιος.

Init — προετοιμασία του φακέλου

powershell
terraform init
text
Initializing the backend...
Initializing provider plugins...
- Finding hashicorp/local versions matching "~> 2.5"...
- Installing hashicorp/local v2.9.1...
- Installed hashicorp/local v2.9.1 (signed by HashiCorp)
Terraform has created a lock file .terraform.lock.hcl to record the provider
selections it made above. …

Terraform has been successfully initialized!

Τρία πράγματα συμβαίνουν: το Terraform κατεβάζει τον provider (Installing hashicorp/local v2.9.1), τον τοποθετεί στον κρυφό φάκελο .terraform/, και γράφει το .terraform.lock.hcl για να διατηρήσει την επιλεγμένη έκδοση. Η φράση που πρέπει να περιμένεις είναι Terraform has been successfully initialized!. Χωρίς init, κάθε άλλη εντολή σταματά: Error: Inconsistent dependency lock file για το plan, Error: Missing required provider για το validate, και οι δύο σου λένε τι να κάνεις (run: terraform init).

Write — γραφή

Γράφεις ή τροποποιείς τα αρχεία .tf, μετά συνήθως εκτελείς:

powershell
terraform fmt
terraform validate

Το terraform fmt ευθυγραμμίζει εκ νέου τα κενά και την εσοχή· εμφανίζει το όνομα των αρχείων που τροποποίησε, και τίποτα απολύτως αν όλα ήταν ήδη καθαρά. Το terraform validate ελέγχει τη σύνταξη και τη συνέπεια των μπλοκ χωρίς να αγγίξει τίποτα, και απαντά:

text
Success! The configuration is valid.

Πιάνει τα ορθογραφικά λάθη στα ονόματα ορισμάτων (An argument named "contenu" is not expected here. Did you mean "content"?) και τα ξεχασμένα άγκιστρα (Error: Unclosed configuration block). Δεν ελέγχει αν το αποτέλεσμα θα ήταν μια καλή αρχιτεκτονική.

Plan — πρόβλεψη

Το Terraform συγκρίνει την επιθυμητή κατάσταση με ό,τι γνωρίζει για την υποδομή:

powershell
terraform plan
text
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:

  # local_file.message will be created
  + resource "local_file" "message" {
      + content              = "Bonjour, ce fichier a été créé avec Terraform."
      + content_md5          = (known after apply)
      + filename             = "./message.txt"
      + id                   = (known after apply)

    }

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

Το plan απαντά σε τρεις ερωτήσεις, και η τελευταία γραμμή τις συνοψίζει σε τρεις αριθμούς:

  • Τι θα δημιουργηθεί; (to add)
  • Τι θα τροποποιηθεί; (to change)
  • Τι θα καταστραφεί ή θα αντικατασταθεί; (to destroy)

Κάθε γραμμή ιδιότητας φέρει το σύμβολο της ενέργειας. Το (known after apply) σηματοδοτεί μια τιμή που το Terraform δεν μπορεί να γνωρίζει πριν δημιουργήσει το αντικείμενο: εδώ το αναγνωριστικό του αρχείου και τα αποτυπώματα του περιεχομένου του.

Apply — εφαρμογή

Μετά την αναθεώρηση:

powershell
terraform apply

Το Terraform εμφανίζει ξανά το plan και μετά ζητά τη συμφωνία σου:

text
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

local_file.message: Creating...
local_file.message: Creation complete after 0s [id=ff6b93dc92e7e1d2ba4f9dad3cc16e03ac649b45]

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

Μόνο το ολόκληρο yes γίνεται δεκτό· το y, το Y ή μια κενή γραμμή δίνουν Apply cancelled. και τίποτα δεν αγγίζεται. Το Terraform καλεί στη συνέχεια τους providers με τη σειρά που υποδεικνύουν οι εξαρτήσεις, και μετά ενημερώνει το state. Η τελευταία γραμμή επαναλαμβάνει τους τρεις αριθμούς του plan: Apply complete! Resources: 1 added, 0 changed, 0 destroyed.

Αν ξανατρέξεις το terraform plan χωρίς να έχεις αλλάξει τίποτα, η προσφορά είναι άδεια:

text
No changes. Your infrastructure matches the configuration.

Αυτή είναι η ιδεμποτεντικότητα του μαθήματος 01, όπως φαίνεται στο τερματικό.

Destroy — αναίρεση όλων

powershell
terraform destroy

Το destroy είναι ένα apply του οποίου το plan περιέχει μόνο -. Η ερώτηση που θέτεται είναι πιο επιτακτική (Do you really want to destroy all resources? … There is no undo.), η αναμενόμενη απάντηση είναι πάντα yes, και η τελευταία γραμμή είναι:

text
Destroy complete! Resources: 1 destroyed.

Μετά από ένα destroy, το terraform state list δεν εμφανίζει πλέον τίποτα. Σε αυτό το μάθημα, κάθε έργο τελειώνει έτσι.

Πλήρης κύκλος

Τα τέσσερα σύμβολα ενός plan

ΣύμβολοΕνέργειαΦράση στο planΤι αλλάζει για εσένα
+δημιουργίαwill be createdΈνα νέο αντικείμενο· τίποτα υπάρχον δεν αγγίζεται.
~τροποποίηση επί τόπουwill be updated in-placeΤο αντικείμενο παραμένει, μια ιδιότητα αλλάζει.
-/+αντικατάστασηmust be replacedΤο αντικείμενο καταστρέφεται και μετά ανακατασκευάζεται· το περιεχόμενό του και το αναγνωριστικό του εξαφανίζονται.
-καταστροφήwill be destroyedΤο αντικείμενο εξαφανίζεται.

Δηλωτικό δεν σημαίνει μαγικό

Το Terraform καθορίζει πώς θα φτάσει στην αιτούμενη κατάσταση, αλλά η ακριβής συμπεριφορά εξαρτάται από κάθε provider. Ορισμένα ορίσματα μπορούν να τροποποιηθούν επί τόπου· άλλα υποχρεώνουν σε αντικατάσταση του πόρου. Ο provider local του έργου 01 δίνει ένα σαφές παράδειγμα: η αλλαγή της γραμμής content ενός local_file δεν παράγει ~, αλλά μια αντικατάσταση -/+. Ακολουθεί το πραγματικό plan, καταγραμμένο μετά την τροποποίηση του κειμένου:

text
  # local_file.message must be replaced
-/+ resource "local_file" "message" {
      ~ content              = "Bonjour, ce fichier a été créé avec Terraform." -> "Deuxième version du fichier créée avec Terraform." # forces replacement
      ~ content_md5          = "6daf774f0eb6d3da439c871afec7cf90" -> (known after apply)
      ~ id                   = "ff6b93dc92e7e1d2ba4f9dad3cc16e03ac649b45" -> (known after apply)
        # (3 unchanged attributes hidden)
    }

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

Διάβασε τις τρεις ενδείξεις: must be replaced στον τίτλο, # forces replacement στο τέλος της γραμμής content, και 1 to add, 0 to change, 1 to destroy στη σύνοψη. Για ένα αρχείο κειμένου, η λεπτή διάκριση δεν έχει συνέπειες. Για μια βάση δεδομένων, το -/+ σημαίνει απώλεια δεδομένων: γι' αυτό το plan πρέπει να διαβάζεται, ακόμη και όταν η αλλαγή κώδικα φαίνεται μικρή.

Η ουσιαστική διαφορά ανάμεσα στο ~ και το -/+. Το ~ πριν από το content λέει ότι η τιμή αλλάζει. Το -/+ πριν από το resource λέει πώς θα το χειριστεί το Terraform: καταστροφή, μετά ανακατασκευή. Όταν μια γραμμή ~ φέρει # forces replacement, αυτή είναι που προκάλεσε το -/+ ολόκληρου του μπλοκ.

Η ουσία

  1. Τέσσερα κομμάτια: η διαμόρφωση περιγράφει, το CLI συγκρίνει, οι providers καλούν τα API, το state θυμάται.
  2. Ένα μπλοκ resource "type" "nom" έχει μια διεύθυνση, type.nom, την οποία ξαναβρίσκεις στο plan και στο state.
  3. init μία φορά ανά φάκελο (Terraform has been successfully initialized!), μετά fmt, validate (Success! The configuration is valid.), plan (Plan: 1 to add, 0 to change, 0 to destroy.), apply με yes (Apply complete! Resources: 1 added, 0 changed, 0 destroyed.), και destroy στο τέλος της συνεδρίας (Destroy complete! Resources: 1 destroyed.).
  4. Τέσσερα σύμβολα: το + δημιουργεί, το ~ τροποποιεί επί τόπου, το -/+ καταστρέφει και μετά ανακατασκευάζει, το - καταστρέφει. Η σύνοψη Plan: N to add, N to change, N to destroy. τα μετράει.
  5. Ο provider αποφασίζει αν μια αλλαγή είναι ~ ή -/+· το local_file του έργου 01 αντικαθίσταται μόλις αλλάξει το content.

Ερωτήσεις κατανόησης

  1. Ποιο στοιχείο επικοινωνεί απευθείας με το API του AWS ή του GitHub;
  2. Γιατί πρέπει να εκτελεστεί το terraform init, και ποια αρχεία δημιουργεί;
  3. Ποια διαφορά υπάρχει ανάμεσα σε έναν πόρο και μια data source;
  4. Πώς ξέρει το Terraform ότι ένα αντικείμενο S3 εξαρτάται από ένα bucket;
  5. Στο plan του έργου 01 μετά την τροποποίηση του content, ποιες τρεις ενδείξεις λένε ότι πρόκειται για αντικατάσταση και όχι για τροποποίηση επί τόπου;
  6. Τι απαντά το Terraform αν πληκτρολογήσεις y αντί για yes;