Terraform AWS Cloud: Gestionarea sănătoasă a infrastructurii

Publicat: 2022-03-11

Scrierea unei aplicații este doar o parte a poveștii. Pentru ca acesta să fie de valoare, trebuie implementat într-un loc unde se poate scala, trebuie să ruleze cu disponibilitate ridicată, are nevoie de copii de rezervă și așa mai departe.

Din ce în ce mai mulți dezvoltatori trebuie să aibă cel puțin o înțelegere a acestui proces de implementare. Acest lucru se manifestă ca, de exemplu, în DevOps, un rol adesea solicitat în zilele noastre, pe măsură ce complexitatea sistemului crește. Nu ne putem lăsa să ignorăm aceste schimbări și trebuie să fim conștienți de cum să proiectăm aplicațiile pentru a fi ușor de implementat.

Acest lucru este și în interesul clienților noștri: ei ne angajează ca experți în domeniile noastre și se așteaptă să livrăm întregul produs, adesea de la început până la sfârșit. Ei au cerințe și sunt adesea ignoranți de stiva pe care rulează soluția lor de afaceri. În cele din urmă, valoarea de afaceri a produsului contează.

Vă prezentăm Terraform

Implementarea și gestionarea infrastructurii nu este un proces simplu. Pe lângă o mulțime de expertiză în domeniu, în continuă schimbare, trebuie să învățăm Un alt instrument sau un nou flux de lucru. Dacă ați amânat acest lucru, acest articol este o șansă pentru a vă familiariza cu o abordare a managementului infrastructurii. Sper că în final vei fi mai încrezător în utilizarea Terraform și vei afla mai multe despre posibilele abordări și provocări. Ar trebui să puteți utiliza acest instrument pentru a începe cel puțin să gestionați o parte a infrastructurii dvs. cloud.

Terraform este un strat de abstracție, da, iar abstracțiile sunt scurse, sunt de acord. Dar, în cele din urmă, suntem în afacerea de a rezolva probleme și de a gestiona abstracțiile. Acesta își propune să ne ofere mai multă minte în sarcinile noastre de zi cu zi.

Obiectivul

Voi explica ce este Terraform, cum se potrivește întregului ecosistem și cum se compară cu alte instrumente similare. Apoi vă voi arăta pașii necesari pentru a configura o configurație Terraform multi-mediu și pregătită pentru producție pentru o echipă. Voi explica elementele de bază ale scrierii configurației Terraform — cum să gestionați complexitatea și să duplicați codul cu module care pot fi partajate.

Exemplele se vor concentra toate pe un singur furnizor de cloud: Amazon Web Services (AWS). Acesta este doar un nor cu care am cea mai mare experiență, dar toate informațiile ar trebui să se aplice și altor nori.

Voi încheia cu câteva note pe care mi-aș fi dorit să le știu când am început: niște probleme de sintaxă, ciudații și cazuri în care Terraform nu ar fi instrumentul meu preferat.

Nu mă voi concentra asupra detaliilor esențiale ale sintaxei. Puteți ajunge rapid la curent cu asta citind documentația și ghidurile fantastice pe care HashiCorp le oferă pentru Terraform. Aș dori să mă concentrez pe lucruri care ar putea să nu fie evidente de la început și pe care mi-aș fi dorit să le fi știut înainte de a începe să lucrez cu Terraform. Sperăm că acest lucru vă va îndrepta în direcția corectă și vă va permite să considerați Terraform ca un instrument de utilizat în proiecte viitoare.

Managementul infrastructurii este greu

Există mai mulți pași implicați în configurarea unui mediu pentru o aplicație în cloud. Dacă nu le notezi pe toate ca liste de verificare detaliate și nu le urmărești îndeaproape, vei face tot timpul greșeli; la urma urmei suntem oameni. Acești pași sunt greu de împărtășit. Trebuie să documentați o mulțime de proceduri manuale, iar documentele pot deveni rapid învechite. Acum înmulțiți toate acestea cu numărul total de medii pentru o singură aplicație: dev , test/qa , stage și prod . De asemenea, trebuie să vă gândiți la securitatea fiecăruia dintre ei.

Nu fiecare instrument are o interfață de utilizare pe care să o pot folosi și să uit de complexitate?

Au interfețe de utilizare — AWS Management Console este un exemplu excelent. Dar acele instrumente fac foarte multe sub capotă. Un clic pe interfața de utilizare poate invoca de fapt o cascadă de modificări care sunt greu de înțeles. De obicei, nu există nicio modalitate de a anula ceea ce ați făcut în interfața de utilizare (instrucțiunile obișnuite „Ești sigur?” nu sunt adesea suficiente). În plus, este întotdeauna o idee bună să avem o a doua pereche de ochi pentru a verifica modificările, dar atunci când folosim interfețele de utilizator, ar trebui să stăm împreună cu această persoană sau să ne verificăm modificările după ce sunt făcute, ceea ce este mai mult un audit decât un revizuire. Fiecare furnizor de cloud are propria sa interfață de utilizare pe care trebuie să o stăpânești. Interfețele de utilizare sunt concepute pentru a fi ușor de utilizat (nu neapărat simple!) și, ca atare, sunt predispuse la abordări delirante, cum ar fi „aceasta este doar o mică modificare” sau o remediere rapidă de producție de care veți uita în 48 de ore. Un astfel de clic manual este, de asemenea, foarte greu de automatizat.

Dar CLI Tools?

Ar fi mai bune decât instrumentele UI pentru cazurile noastre de utilizare. Cu toate acestea, sunteți încă predispus să faceți modificări manual sau să scrieți scripturi bash, care pot scăpa cu ușurință de sub control. În plus, fiecare furnizor are propriile instrumente CLI. Cu aceste instrumente, nu vă puteți vedea modificările înainte de a vă implica. Din fericire, aceasta nu este o problemă nouă și există instrumente care ajută la aceasta.

Orchestrație vs. Managementul configurației

Voi defini câteva categorii de instrumente și practici care sunt utilizate pentru gestionarea infrastructurii. Acestea sunt managementul orchestrației și configurației. Vă puteți gândi la ele ca modele de programare declarative și imperative (gândiți-vă la Prolog vs. Python). Fiecare are propriile sale avantaje și dezavantaje, dar cel mai bine este să le cunoașteți pe toate și să aplicați cel mai bun instrument pentru un anumit loc de muncă. De asemenea, orchestrarea implică de obicei un nivel de abstractizare mai mare decât managementul configurației.

Orchestrație

Orchestrarea seamănă mai mult cu o paradigmă de programare declarativă. Îmi place să cred că sunt dirijorul unei orchestre. Munca unui dirijor este frumos rezumată de Wikipedia (link) ca „arta de a dirija performanța simultană a mai multor jucători sau cântăreți prin utilizarea gestului”. Dirijorii comunică ritmul și tempo-ul, dinamica și indicația muzicii, dar munca reală este executată de muzicieni individuali care sunt experți în a cânta la instrumentele lor muzicale. Fără o astfel de coordonare, ei nu ar putea interpreta o piesă perfectă.

Aici se potrivește Terraform. Îl folosiți pentru a conduce o infrastructură IT: îi spuneți ce trebuie să implementați, iar Terraform le conectează pe toate și efectuează toate apelurile API necesare. Există instrumente similare în acest spațiu; una dintre cele mai cunoscute este AWS Cloud Formation. Are suport mai bun pentru recuperare și rollback-uri în caz de erori decât Terraform, dar și, după părerea mea, o curbă de învățare mai abruptă. De asemenea, nu este independent de cloud: funcționează numai cu AWS.

Managementul configurației

Partea complementară a acestor practici este abordarea managementului configurației. În această paradigmă, specificați pașii exacti pe care trebuie să îi facă un instrument pentru a ajunge la o configurație dată, dorită. Pașii în sine ar putea fi mici și să accepte mai multe sisteme de operare, dar trebuie să vă gândiți activ la ordinea executării lor. Ei nu au conștientizarea stării curente și a împrejurimilor (cu excepția cazului în care le programați cu asta) și, ca atare, vor executa orbește orice pași le oferiți. Acest lucru poate duce la o problemă cunoscută sub numele de deriva de configurare , în care resursele dvs. se vor desincroniza încet cu ceea ce trebuiau să reprezinte inițial, mai ales dacă le-ați făcut unele modificări manuale. Sunt excelente în gestionarea și furnizarea de servicii pe instanțe individuale. Exemple de instrumente care excelează la acest flux de lucru sunt Chef, Puppet, Ansible și Salt.

Orchestrarea impune o abordare a infrastructurii dvs. în care vă tratați resursele ca pe vite, nu ca pe animale de companie. În loc să „alimentați” manual fiecare VPS, puteți să le înlocuiți cu o copie exactă atunci când ceva nu merge bine. Nu vreau să spun că pur și simplu nu-ți pasă și repornești treaba sperând să fie mai bine.

Meme a emisiunii TV IT Crowd cu sloganul lor emblematic: Ați încercat să îl opriți și să îl porniți din nou?

În schimb, ar trebui să investigați și să remediați problema din cod și apoi să o implementați.

Ansible (și alte instrumente CM) pot fi folosite pentru a gestiona infrastructura AWS, dar acest lucru ar implica multă muncă și este mai predispus la erori, mai ales atunci când infrastructura se schimbă des și crește în complexitate.

Un lucru important de reținut este că abordările de orchestrare și de gestionare a configurației nu intră în conflict una cu cealaltă. Sunt compatibili. Este perfect OK să aveți un grup de instanțe EC2 (VPS) într-un grup AutoScaling gestionat de Terraform, dar care rulează o imagine de aplicație AWS (AMI), care este un instantaneu al discului, care a fost pregătit cu pași imperativi, de exemplu, Ansible . Terraform are chiar și un concept de „furnizori” care vă permit să rulați instrumente externe de furnizare odată ce o mașină pornește.

Documentația Terraform face o muncă grozavă de a explica acest lucru în continuare și de a vă ajuta să plasați Terraform în întregul ecosistem.

Ce este Terraform?

Este un instrument open source, creat de HashiCorp, care vă permite să vă codificați infrastructura ca fișiere de configurare declarative care sunt versionate și partajate și pot fi revizuite.

Numele HashiCorp ar trebui să sune un clopoțel - ei fac, de asemenea, Nomad, Vault, Packer, Vagrant și Consul. Dacă ați folosit oricare dintre aceste instrumente, știți deja calitatea documentației, comunitatea vibrantă și utilitatea la care vă puteți aștepta de la soluțiile lor.

Infrastructura ca cod

Terraform este independent de platformă; îl puteți folosi pentru a gestiona servere bare metal sau servere cloud precum AWS, Google Cloud Platform, OpenStack și Azure. În limbajul Terraform, aceștia se numesc furnizori . Puteți obține o idee a amplorii citind o listă completă a furnizorilor acceptați. Pot fi utilizați mai mulți furnizori în același timp, de exemplu atunci când furnizorul A configurează VM-urile pentru dvs., dar furnizorul B configurează și delegă înregistrările DNS.

Înseamnă asta că se poate schimba furnizorii de cloud cu o singură modificare a unui fișier de configurare? Nu, nici nu cred că ți-ai dori asta, cel puțin nu într-un mod automat. Problema este că diferiți furnizori pot avea diferite capacități, diferite oferte, fluxuri, idei etc. Înseamnă că va trebui să utilizați resurse diferite pentru un alt furnizor pentru a exprima același concept. Cu toate acestea, toate acestea pot fi realizate într-o singură sintaxă de configurare familiară și pot face parte dintr-un flux de lucru coeziv.

Părți esențiale ale unei configurații Terraform

  1. Binarul Terraform în sine, pe care trebuie să îl instalați
  2. Fișierele codului sursă, adică configurația dvs
  3. Statul (local sau la distanță) care reprezintă resursele pe care Terraform le gestionează (mai multe despre asta mai târziu)

Scrierea configurației Terraform

Scrieți codul de configurare Terraform în fișiere *.tf folosind limbajul HCL. Există o opțiune de utilizare a formatului JSON ( *.tf.json ), dar este destinată mașinilor și generării automate, mai degrabă decât oamenilor. Vă recomand să rămâneți cu HCL. Nu mă voi scufunda adânc în sintaxa limbajului HCL; documentele oficiale fac o treabă fantastică descriind cum se scrie HCL și cum se utilizează variabilele și interpolările. Voi menționa doar minimul necesar pentru a înțelege exemplele.

În interiorul fișierelor Terraform, aveți de-a face în mare parte cu resurse și surse de date . Resursele reprezintă componente ale infrastructurii dvs., de exemplu, o instanță AWS EC2, o instanță RDS, o înregistrare DNS Route53 sau o regulă dintr-un grup de securitate. Acestea vă permit să le furnizați și să le modificați în interiorul arhitecturii cloud.

Presupunând că ați configurat Terraform, dacă emiteți o terraform apply , codul de mai jos ar crea o instanță EC2 complet funcțională (sunt afișate doar anumite proprietăți):

 resource "aws_instance" "bastion" { ami = "ami-db1688a2" # Amazon Linux 2 LTS Candidate AMI 2017.12.0 (HVM), SSD Volume Type - ami-db1688a2 instance_type = "t2.nano" key_name = "${var.key_name}" subnet_ vpc_security_group_ids = ["${aws_security_group.bastion.id}"] monitoring = "false" associate_public_ip_address = "true" disable_api_termination = "true" tags = { Name = "${var.project_tag}-bastion-${var.env}" Env = "${var.env}" Application ApplicationRole = "Bastion Host" Project = "${var.project_tag}" } }

Pe de altă parte, există surse de date care vă permit să citiți date despre anumite componente fără a le modifica. Doriți să obțineți ID-ul AWS (ARN) al unui certificat emis de ACM? Folosești o sursă de date. Diferența este că sursele de date sunt prefixate cu data_ atunci când se face referire la ele în fișierele de configurare.

 data "aws_acm_certificate" "ssl_cert" { domain = "*.example.com" statuses = ["ISSUED"] }

Cele de mai sus se referă la un certificat ACM SSL emis care poate fi utilizat împreună cu AWS ALB. Înainte de a face toate acestea, trebuie să vă configurați mediul.

Structura folderului

Mediile Terraform (și stările lor) sunt separate prin directoare. Terraform încarcă toate fișierele *.tf dintr-un director într-un singur spațiu de nume, așa că ordinea nu contează. Recomand următoarea structură de directoare:

 /terraform/ |---> default_variables.tf (1) /stage/ (2) |---> terraform.tfvars (3) |---> default_variables.tf (4) |---> terraform.tf (5) |---> env_variables.tf (6) /prod/ /<env_name>/
  1. default_variables.tf – definiți toate variabilele de nivel superior și, opțional, valorile implicite ale acestora. Ele pot fi reutilizate în fiecare mediu (director imbricat) cu legături simbolice.
  2. /stage/ – un director care deține configurația pentru un întreg mediu separat (numit aici stage , dar poate fi orice). Orice modificări făcute în acest folder sunt total independente de alte medii (env-cum ar fi prod ), ceea ce este ceva ce doriți pentru a evita deteriorarea mediului de producție cu modificările făcute în stage !
  3. terraform.tfvars – definiți valori variabile. Fișierele .tfvars sunt similare cu fișierele .env prin faptul că dețin perechile key=val pentru variabilele definite. De exemplu, aceasta specifică profile AWS , AWS key_name și AWS key_path pe care le folosesc. Poate fi ignorat în Git.
  4. default_variables.tf – acesta este un link simbolic către fișierul (2), care ne permite să partajăm variabile independente de env fără a ne repeta.
  5. terraform.tf – aceasta este configurația principală a fiecărui mediu; deține blocul terraform {} care configurează back-end-ul. De asemenea, configurez furnizorii aici.
  6. env_variables.tf – acest fișier conține variabile specifice env. Etichet toate resursele cu Env=<env_name> în AWS, astfel încât acest fișier definește de obicei o singură variabilă: env .

Desigur, aceasta nu este singura modalitate de a vă structura mediul. Acesta este doar ceva care a funcționat bine pentru mine, permițând separarea clară a preocupărilor.

Configurație back-end

Am menționat deja starea Terraform. Aceasta este o parte esențială a fluxului de lucru Terraform. S-ar putea să vă întrebați dacă starea este de fapt necesară. Nu ar putea Terraform să interogheze API-ul AWS tot timpul pentru a obține starea reală a infrastructurii? Ei bine, dacă vă gândiți bine, Terraform trebuie să mențină o mapare între ceea ce gestionează în fișierele de configurare declarative și ceea ce corespund de fapt acele fișiere (în mediul furnizorului de cloud). Rețineți că, în timp ce scrieți fișiere de configurare Terraform, nu vă pasă de ID-urile, de exemplu, ale instanțelor EC2 individuale sau ARN-urile care vor fi create pentru grupurile de securitate pe care le publicați. Pe plan intern, însă, Terraform trebuie să știe că un anumit bloc de resurse reprezintă o resursă concretă cu un ID/ARN. Acest lucru este necesar pentru a detecta schimbările. Mai mult, starea este folosită pentru a urmări dependențele dintre resurse (de asemenea, ceva la care nu trebuie să te gândești de obicei!). Ele sunt folosite pentru a construi un grafic care poate fi (de obicei) paralelizat și executat. Ca întotdeauna, vă recomand să citiți documentația excelentă despre starea Terraform și scopul acesteia.

Deoarece statul este singura sursă de adevăr pentru arhitectura dvs., trebuie să vă asigurați că dvs. și echipa dvs. lucrați mereu la cea mai recentă versiune a acesteia și că nu creați conflicte prin accesul nesincronizat la stare. Nu vrei să rezolvi conflictele de îmbinare din fișierul de stat, crede-mă.

În mod implicit, Terraform stochează starea într-un fișier de pe disc, situat în directorul de lucru curent (al fiecărui env) ca fișier terraform.tfstate . Acest lucru este în regulă dacă știi că vei fi singurul dezvoltator la locul de muncă sau doar înveți și experimentezi cu Terraform. Din punct de vedere tehnic, ați putea face ca acesta să funcționeze într-o echipă, deoarece puteți trimite starea într-un depozit VCS. Dar apoi, ar trebui să vă asigurați că toată lumea lucrează mereu la cea mai recentă versiune a statului și că nimeni nu face modificări în același timp! Aceasta este, în general, o durere de cap majoră și o sfătuiesc cu căldură. În plus, dacă cineva se alătură operațiunii dvs. de dezvoltare unică, va trebui totuși să configurați un loc alternativ pentru stat.

Din fericire, aceasta este o problemă cu soluții bune integrate în Terraform: așa-numita stare la distanță . Pentru ca starea de la distanță să funcționeze, trebuie să configurați back-end-ul folosind unul dintre furnizorii de back-end disponibili. Următorul exemplu de back-end se va baza pe AWS S3 și AWS DynamoDB (bază de date AWS NoSQL). Ai putea folosi doar S3, dar apoi pierzi mecanismul de blocare a stării și verificarea consistenței (nu este recomandat). Dacă ați folosit anterior doar starea locală, configurarea unui back-end de la distanță vă va oferi o opțiune de a vă migra starea prima dată, astfel încât să nu pierdeți nimic. Puteți citi mai multe despre configurarea back-end aici.

Din păcate, există o problemă cu puiul și ouă: găleata S3 și tabelul DynamoDB trebuie să fie create manual. Terraform nu le poate crea automat, deoarece nu există încă o stare! Ei bine, există câteva soluții precum https://github.com/gruntwork-io/terragrunt care automatizează asta folosind AWS CLI, dar nu vreau să mă abat de la subiectul principal al acestei postări de blog.

Lucrurile importante de știut despre configurația backend S3 și DynamoDB sunt:

  1. Activați versiunea pe găleată S3 pentru a fi ferit de erorile umane și Legea lui Murphy.
  2. Tabelul DynamoDB are o limită a ratei de citire și scriere (numită capacitate). Dacă faceți multe modificări la starea de la distanță, asigurați-vă că fie activați DynamoDB AutoScaling pentru acel tabel, fie configurați limite R/W suficient de mari. În caz contrar, Terraform va primi erori HTTP 400 de la API-ul AWS atunci când execută o mulțime de apeluri.

Pentru a rezuma totul, următoarea configurație back-end poate fi plasată în terraform.tf pentru a configura starea de la distanță pe S3 și DynamoDB.

 terraform { # Sometimes you may want to require a certain version of Terraform required_version = ">= 0.11.7" # Stores remote state, required for distributed teams # Bucket & dynamoDB table have to be created manually if they do not exist # See: https://github.com/hashicorp/terraform/issues/12780 backend "s3" { bucket = "my-company-terraform-state" key = "app-name/stage" region = "eu-west-1" # 5/5 R/W Capacity might not be enough for heavy, burst work (resulting in 400s). Consider enabling Auto Scaling on the table. # See: http://docs.aws.amazon.com/amazondynamodb/latest/developerguide/HowItWorks.ProvisionedThroughput.html dynamodb_table = "terraform-state-lock-table" } }

Acestea sunt multe de luat odată, dar amintiți-vă că faceți acest lucru o dată pentru fiecare mediu și apoi puteți uita de el. Dacă aveți nevoie de și mai mult control asupra blocării de stat, există HashiCorp Terraform Enterprise, dar nu o voi acoperi aici.

Furnizorii

Pentru ca acest back-end să fie accesibil și pentru a putea comunica cu furnizorul nostru de cloud, trebuie să configuram așa-numitul furnizor . Următorul bloc poate fi plasat în fișierul terraform.tf (pentru fiecare env):

 provider "aws" { profile = "${var.profile}" region = "${var.region}" version = "~> 1.23.0" }

Variabilele pentru profile și region sunt stocate în fișierul terraform.tfvars , care poate fi ignorat. Variabila de profile se referă la un profil numit care deține acreditări de securitate pentru cloud AWS folosind fișierul standard de acreditări. Rețineți că stabilesc și unele constrângeri de versiune. Nu doriți ca Terraform să vă actualizeze pluginurile furnizorului fără știrea dvs. despre fiecare inițializare back-end. Mai ales având în vedere că există o versiune 2.x a furnizorului AWS care necesită o actualizare atentă.

Inițializare back-end

Fiecare configurație back-end necesită un pas de inițializare la început și de fiecare dată când se face o modificare. Inițializarea configurează, de asemenea, modulele Terraform (mai multe despre acestea mai târziu), așa că atunci când le adăugați, trebuie să rulați din nou și pasul init. Această operațiune poate fi executată în siguranță de mai multe ori. Rețineți că, în timpul inițializării back-end-ului, nu toate variabilele pot fi citite de Terraform pentru a configura starea și nici nu ar trebui să fie din motive de securitate (de exemplu, chei secrete). Pentru a depăși acest lucru și, în cazul nostru, a utiliza un profil AWS diferit de cel implicit, puteți utiliza opțiunea -backend-config cu accepts k=v perechi de variabile. Aceasta se numește configurație parțială.

 terraform init -backend-config=profile=<aws_profile_name>

Fișierele Terraform partajează un domeniu care este limitat la un director dat. Aceasta înseamnă că un subdosar nu este conectat direct la codul directorului părinte. Totuși, definește un modul care permite reutilizarea codului, gestionarea complexității și partajarea.

Fluxul de lucru

Fluxul general de lucru atunci când lucrați cu codul Terraform este următorul:

  1. Scrieți configurația pentru infrastructura dvs.
  2. Vedeți ce schimbări reale va face ( terraform plan ).
  3. Opțional, executați exact modificările pe care le-ați văzut la pasul 2 ( terraform apply ).
  4. GOTO 1

Planul Terraform

Comanda Terraform plan vă va prezenta o listă de modificări care vor fi făcute infrastructurii dvs. la lansarea comenzii de apply . Este sigur să emiti un plan mai multe ori, deoarece acesta în sine nu schimbă nimic.

Cum să citești un plan

Obiectele din Terraform (resurse și surse de date) sunt ușor de identificat după numele lor complet calificate.

  1. În cazul resurselor, ID-ul poate arăta astfel: <resource_type>.<resource_name> — de exemplu, aws_ecs_service.this .
  2. În cazul resurselor din module, avem un nume de modul suplimentar: module.<module_name>.<resource_type>.<resource_name> — de exemplu, module.my_service_ecs_service_task.aws_ecs_service.this .
  3. Surse de date (în interiorul și în afara unui modul): (module.<module_name>).data.<resource_type>.<resource_name> —de exemplu, module.my_service_ecs_service_task.data.aws_ecs_task_definition.this .

Tipul de resursă este specific unui anumit furnizor și include de obicei numele acestuia ( aws_… ). Întreaga listă a resurselor disponibile pentru AWS poate fi găsită în documente.

Există cinci acțiuni pe care un plan le va arăta pentru anumite resurse:

  1. [+] Adaugă – O nouă resursă va fi creată.
  2. [-] Distruge – O resursă va fi complet distrusă.
  3. [~] Modificare pe loc – O resursă va fi modificată și unul sau mai mulți parametri vor fi modificați. Acest lucru este în general sigur. Terraform vă va arăta ce parametri vor fi modificați și cum (dacă este posibil).
  4. [- / +] – Resursa va fi eliminată și apoi recreată cu noi parametri. Acest lucru se întâmplă dacă a fost făcută o modificare la un parametru care nu poate fi modificat în loc. Terraform vă va arăta care modificări forțează o recreare a unei resurse cu următorul comentariu în roșu: (forces new resource) . Acest lucru este potențial periculos, deoarece există o perioadă în care resursa nu va exista deloc. De asemenea, poate sparge alte dependențe conectate. Vă recomand să rezolvați astfel de modificări, cu excepția cazului în care știți care vor fi consecințele sau nu vă pasă de un timp de nefuncționare.
  5. [<=] – O sursă de date va fi citită. Aceasta este o operațiune numai în citire.

Terraform AWS — un exemplu de „plan”

Mai sus este un exemplu de plan. Modificările pe care le-am făcut au fost:

  • S-a schimbat instance_type a primei instanțe de bastion
  • S-a adăugat oa doua instanță de bastion
  • S-a schimbat numele unui grup de securitate

Rețineți că ultima modificare este o regulă de grup de securitate care a detectat automat o modificare a numelui grupului părinte. După părerea mea, întregul plan este foarte ușor de citit. Unele valori ale parametrilor sunt afișate ca <computed> — nu înseamnă că vor fi modificate, ci mai degrabă că nu pot fi preluate și prezentate în această etapă (cum ar fi numele SG care nu a fost încă creat). Rețineți că numai resursele modificate vor fi afișate în timpul comenzii plan . Orice lucru care este omis nu va fi atins.

De obicei, comenzile Terraform acceptă parametri suplimentari. Cel mai important parametru al comenzii plan este opțiunea -out , care vă va salva planul pe disc. Acest plan salvat poate fi apoi executat (exact așa cum a fost salvat) prin comanda aplica. Acest lucru este foarte important, deoarece în caz contrar ar putea exista o schimbare, făcută, de exemplu, de către colegi între emiterea unui plan și emiterea apply . Exemplu:

  1. Emiteți un plan și verificați dacă arată bine.
  2. Cineva schimbă starea, fără să știi.
  3. apply problema și — hopa, a făcut altceva decât ceea ce era planificat!

Din fericire, acest flux de lucru a fost îmbunătățit în Terraform v0.11.0. Din această versiune, comanda de apply vă prezintă automat un plan pe care trebuie să îl aprobați apoi (prin tastând explicit yes ). Avantajul este că, dacă aplicați acest plan, acesta va fi executat exact așa cum este prezentat.

O altă opțiune utilă este -destroy , care vă va arăta planul care va avea ca rezultat distrugerea tuturor resurselor pe care le gestionează Terraform. De asemenea, puteți viza resurse specifice pentru distrugere cu opțiunea -target .

Aplicați Terraform

Când aplicăm un anumit plan, Terraform iese și ne blochează starea pentru a ne asigura exclusivitatea. Apoi trece la schimbarea resurselor și, în final, împinge o stare actualizată. Un lucru de remarcat este că unele resurse durează mai mult până la finalizare decât altele. De exemplu, crearea unei instanțe AWS RDS poate dura mai mult de 12 minute, iar Terraform va aștepta ca aceasta să se termine. Evident, Terraform este suficient de inteligent pentru a nu bloca orice altă operațiune prin aceasta. creează un grafic direcționat al modificărilor solicitate și, dacă nu există interdependențe, folosește paralelismul pentru a accelera execuția.

Importul resurselor

Adesea, Terraform și alte soluții de „configurare ca cod” sunt introduse treptat, într-un mediu deja existent. Tranziția este cu adevărat ușoară. Practic, orice nu este definit în Terraform este lăsat negestionat și neschimbat. Terraform este preocupat doar de ceea ce gestionează. Desigur, este posibil ca acest lucru să introducă probleme - de exemplu, dacă Terraform se bazează pe un punct final care există în afara configurației sale și apoi este distrus manual. Terraform nu știe despre acest lucru și, prin urmare, nu poate recrea această resursă în timpul schimbărilor de stare, ceea ce va duce la erori de la API în timpul execuției planului. Acesta nu este ceva pentru care mi-aș face griji; beneficiile introducerii Terraform depășesc cu mult contra.

Pentru a ușura puțin procesul de introducere, Terraform include comanda de import . Este folosit pentru a introduce resurse externe deja existente în starea Terraform și îi permite să gestioneze acele resurse. Din păcate, Terraform nu poate genera automat configurații pentru acele module importate, cel puțin la momentul scrierii. Există planuri pentru această funcționalitate, dar deocamdată trebuie să scrieți mai întâi definiția resursei în Terraform și apoi să importați această resursă pentru a spune Terraform să înceapă să o gestioneze. Odată importat în stare (și în timpul planificării execuției), veți vedea toate diferențele dintre ceea ce ați scris în fișierele .tf și ceea ce există de fapt în cloud. În acest fel, puteți modifica în continuare configurația. În mod ideal, nu ar trebui să apară modificări, ceea ce ar însemna că configurația Terraform reflectă ceea ce este deja pe nor 1:1.

Module

Modulele sunt o parte esențială a configurației Terraform și vă recomand să le adoptați și să le folosiți frecvent. Ele vă oferă o modalitate de a reutiliza anumite componente. Un exemplu ar fi clusterul AWS ECS, care este folosit pentru a rula containere Docker. Pentru ca un astfel de cluster să funcționeze, aveți nevoie de multe resurse separate pentru a fi configurate: clusterul în sine, configurația de lansare care ar gestiona instanțe EC2 separate, depozite de containere pentru imagini, grupul și politica de scalare automată și așa mai departe. De obicei, trebuie să aveți clustere separate pentru medii și/sau aplicații separate.

O modalitate de a depăși acest lucru ar fi să copiați și să lipiți configurația, dar aceasta este, evident, o soluție miop. Ați face erori făcând cele mai simple actualizări.

Modulele vă permit să încapsulați toate acele resurse separate într-un singur bloc de configurare (numit modul). Modulele definesc intrările și ieșirile care sunt interfețele prin care comunică cu „lumea exterioară” (sau codul de apelare). Mai mult, modulele pot fi imbricate în interiorul altor module, permițându-vă să rotiți rapid medii întregi separate.

Module locale și de la distanță

În mod similar cu statul, puteți avea module locale sau module la distanță. Modulele locale sunt stocate alături de configurația dvs. Terraform (într-un director separat, în afara fiecărui mediu, dar în același depozit). Acest lucru este OK dacă aveți o arhitectură simplă și nu partajați acele module.

Acest lucru, însă, are limitări. Este greu să versionezi acele module și să le partajezi. Versiunea este importantă deoarece s-ar putea să doriți să utilizați versiunea 1.0.0 a modulului dvs. ECS în producție, dar ați dori să experimentați cu v1.1.0 într-un mediu de pregătire. Dacă modulul a fost stocat alături de codul dvs., fiecare modificare a codului modulului s-ar reflecta în fiecare mediu (odată ce apply este rulată), ceea ce de obicei este nedorit.

O abordare la îndemână a modulelor de versiuni este să le puneți pe toate într-un depozit separat, de exemplu, your-company/terraform-modules. Apoi, atunci când faceți referire la acele module în configurația dvs. Terraform, puteți utiliza o legătură VCS ca sursă:

 module "my-module" { source = "[email protected]:your-company/terraform-modules.git//modules/my-module?ref=v1.1.0" ... }

Aici mă refer la o versiune 1.1.0 a modulului meu (cale specifică) pe care o pot testa independent de alte versiuni ale aceluiași modul în medii diferite.

În afară de aceasta, există problema descoperirii și a partajării modulelor. Ar trebui să vă străduiți să scrieți module bine documentate și reutilizabile. De obicei, veți avea diferite configurații Terraform pentru diferite aplicații și poate doriți să partajați același modul între ele. Fără a le extrage într-un repo separat, acest lucru ar fi foarte greu.

Utilizarea modulelor

Modulele pot fi ușor de referit în mediile Terraform prin definirea unui bloc de module special. Iată un exemplu de astfel de bloc pentru un modul ECS ipotetic:

 module "my_service_ecs_cluster" { source = "../modules/ecs_cluster" cluster_name = "my-ecs-service-${var.env}" repository_names = [ "my-ecs-service-${var.env}/api", "my-ecs-service-${var.env}/nginx", "my-ecs-service-${var.env}/docs", ] service_name = "my-ecs-service-${var.env}" ecs_instance_type = "t2.nano" min_size = "1" max_size = "1" use_autoscaling = false alb_target_group_arn = "${module.my_alb.target_group_arn}" subnets = "${local.my_private_subnets}" security_groups = "${aws_security_group.my_ecs.id}" key_name = "${var.key_name}" env_tag = "${var.env}" project_tag = "${var.project_tag}" application_tag = "${var.api_app_tag}" asg_tag = "${var.api_app_tag}-asg" }

Toate opțiunile care sunt transmise (cu excepția unora globale precum source ) au fost definite în configurația acestui modul ca intrări (variabile).

Registrul modulelor

Recent, HashiCorp a lansat un registru oficial al modulului Terraform. Aceasta este o veste grozavă, deoarece acum puteți extrage din cunoștințele comunității care a dezvoltat deja module testate în luptă. În plus, unele dintre ele au insigna „Modul verificat HashiCorp”, ceea ce înseamnă că sunt verificate și întreținute în mod activ și vă oferă un plus de încredere.

Anterior, fie trebuia să-ți scrii propriile module de la zero (și să înveți din greșelile tale), fie să folosești module publicate pe GitHub și în alte locuri, fără nicio garanție cu privire la comportamentul lor (în afară de citirea codului!)

Partajarea datelor între medii

În mod ideal, mediile ar trebui să fie complet separate, chiar și prin utilizarea de conturi AWS diferite. In reality, there are cases when one Terraform environment might use some information in another environment. This is especially true if you are gradually converting your architecture to use Terraform. One example might be that you have a global env that provides certain resources to other envs.

Let's say env global shares data with stage . For this to work, you can define outputs at the main level of the environment like so:

 output "vpc_id" { value = "${module.network.vpc_id}" }

Then, in the stage environment, you define a datasource that points to the remote state of global :

 data "terraform_remote_state" "global" { backend = "s3" config { bucket = "my-app-terraform-state" key = "terraform/global" region = "${var.region}" dynamodb_table = "terraform-state-lock-table" profile = "${var.profile}" } }

Now, you can use this datasource as any other and access all the values that were defined in global 's outputs:

 vpc_

Words of Caution

Terraform has a lot of pros. I use it daily in production environments and consider it stable enough for such work. Having said that, Terraform is still under active development. Thus, you will stumble on bugs and quirks.

Where to Report Issues and Monitor Changes

First of all, remember: Terraform has a separate core repo and repositories for each provider (eg, AWS). If you encounter issues, make sure to check both the core repo and the separate provider repositories for issues and/or opened pull requests with fixes. GitHub is really the best place to search for bugs and fixes as it is very active and welcoming.

This also means that provider plugins are versioned separately, so make sure you follow their changelogs as well as the core one. Most of the bugs I have encountered were resolved by upgrading the AWS provider which already had a fix.

Can't Cheat Your Way out of Cloud Knowledge

You cannot use Terraform to configure and manage infrastructure if you have no knowledge of how a given provider works. I would say this is a misconception and not a downside, since Terraform has been designed to augment and improve the workflow of configuration management and not to be some magic dust that you randomly sprinkle around and—poof! Environments grow! You still need a solid knowledge of a security model of each cloud, how to write, eg, AWS policies, what resources are available, and how they interact.

Prefer Separate Resources That Are Explicitly linked

There are certain resources—for example, the AWS security group or AWS route table—that allow you to configure the security rules and routes respectively, directly inside their own block. This is tempting, as it looks like less work but in fact will cause you trouble. The problems start when you are changing those rules on subsequent passes. The whole resource will be marked as being changed even if only one route/security rule is being introduced. It also gives implicit ordering to those rules and makes it harder to follow the changes. Thankfully, mixing those both approaches is not allowed now (see the note).

Best-practice example, with explicitly linked resources:

 resource "aws_security_group" "my_sg" { name = "${var.app_tag}-my-sg" ... } resource "aws_security_group_rule" "rule_one" { security_group_ ... } resource "aws_security_group_rule" "rule_two" { security_group_ ... }

Terraform plan Doesn't Always Detect Issues and Conflicts

I already mentioned this in the case where you were managing resources with Terraform that were relying on other, unmanaged infrastructure. But there are more trivial examples—for example, you will get an error if your EC2 instance has Termination Protection enabled, even though plan would show you it's OK to destroy it. You can argue that this is what Termination Protection has been designed for, and I agree, but there are more examples of things you can do in theory/on plan but when executed will deadlock or error out. For example, you cannot remove a network interface if something is using it—you get a deadlock without an option to gracefully recover.

Syntax Quirks

There are also quirks related to how HCLv1 (the syntax language Terraform uses) has been designed. It has a couple of frustrating quirks. There is work underway to provide an improved version of the parser for HCLv2. The best way to read on the current limitations and the plan to overcome them is this fantastic blog series. In the meantime, there are workarounds for most of those issues. They are not pretty and they will fail once v0.12 comes out, but hey, it is what it is.

When State Update Fails

It sometimes happens that Terraform is not able to correctly push an updated state. This is usually due to underlying network issues. The solution is to retry the state update instead of running apply again, which will fork the state .

Another issue might happen when state lock (the synchronization primitive that prevents multiple users to update the same state) fails to be taken down by Terraform. This involves running terraform force-unlock with the lock ID to take it down manually.

Thankfully, in case of such problems, Terraform provides you with a good description and steps you need to make to fix it.

Not Everything Is Fun to Manage Through Terraform

There are certain cases where Terraform is not my tool of choice. For example, configuring AWS CodePipeline and CodeBuild projects (AWS equivalent of CI/CD pipeline) is cumbersome when done through Terraform. You need to define each step through very verbose configuration blocks and things like “Login via GitHub” are a lot more complicated than using the UI. Of course, it's still possible if you prefer to have it codified. Well, I guess it's a good candidate for a well-written module!

Same thing goes for managing AWS API Gateway endpoints. In this case, using a dedicated serverless framework would be a better option.

When configuring AWS resources with Terraform, you will find yourself writing a lot of policies. Policies that would otherwise often be auto-generated for you (when using the UI). For those, I'd recommend the AWS Visual Editor and then copying the resulting policy JSON into Terraform.

Concluzie

Using Terraform has been fun and I'll continue doing so. Initial steps can be a bumpy ride, but there are more and more resources that help to ease you in.

I'd definitely recommend taking Terraform for a spin and simply playing with it. Remember, though—be safe and test it out on a non-essential account. If you are eligible for AWS Free Tier, use it as a 12-month free trial. Just be aware it has limitations as to what you can provision. Otherwise, just make sure you spin the cheapest resources, like t3.nano instances.

I highly recommend extensions for Terraform support in various code editors. For Visual Studio Code, there is one with syntax highlighting, formatting, validation and linting support.

It's always valuable to learn new things and evaluate new tools. I found that Terraform helped me immensely in managing my infrastructure. I think working with Terraform will only get easier and more fun, especially once v0.12.0 ships with a major upgrade to the HCL syntax and solve most of the quirks. The traction and community around Terraform are active and vibrant. You can find a lot of great resources on things I didn't manage to cover in a single blogs post, eg, a detailed guide on how to write modules.

Related: Terraform vs. CloudFormation: The Definitive Guide