Izpostavili bomo nekaj pasti, vključno s tistimi, povezanimi s cikli, if izrazi in metodami uvajanja, pa tudi z bolj splošnimi težavami, ki se nanašajo na Terraform na splošno:
- parametra count in for_each imata omejitve;
- omejitve uvajanja z ničelnim časom ustavitve;
- tudi dober načrt se lahko izkaže za neuspešnega;
- refaktoring ima lahko svoje pasti;
- odložena doslednost se ujema… z odlaganjem.
Parametra count in for_each imata omejitve
V primerih v tem poglavju se parameter count in izraz for_each aktivno uporabljata v ciklih in pogojni logiki. Dobro delujeta, vendar imata dve pomembni omejitvi, o katerih je treba vedeti.
- V count in for_each ni mogoče sklicevati na nobene izhodne spremenljivke vira.
- count in for_each ne moreta biti uporabljena v konfiguraciji modula.
V count in for_each ni mogoče sklicevati na nobene izhodne spremenljivke vira
Predstavljajte si, da je treba uvajati več EC2 strežnikov in iz nekega razloga nočete uporabljati ASG. Vaša koda je lahko taka:
resource "aws_instance" "example_1" {
count = 3
ami = "ami-0c55b159cbfafe1f0"
instance_type = "t2.micro"
}
Poglejmo jih zaporedoma.
Ker je parametru count dodeljena statična vrednost, ta koda deluje brez težav: ko izvedete ukaz apply, bo ustvarila tri EC2 strežnike. Ampak, kaj storiti, če želite uvajati en strežnik v vsaki dostopni coni (Availability Zone ali AZ) znotraj trenutne AWS regije? Vaša koda lahko naloži seznam con iz virov podatkov aws_availability_zones in nato 'ciklično' preide skozi vsako izmed njih ter ustvarja EC2 strežnik z uporabo parametra count in dostopa do tabele po indeksu:
resource "aws_instance" "example_2" {
count = length(data.aws_availability_zones.all.names)
availability_zone = data.aws_availability_zones.all.names[count.index]
ami = "ami-0c55b159cbfafe1f0"
instance_type = "t2.micro"
}
data "aws_availability_zones" "all" {}Ta koda bo prav tako odlično delovala, ker se parameter count lahko brez težav sklicuje na vire podatkov. Ampak, kaj se zgodi, če število strežnikov, ki jih morate ustvariti, temelji na izhodu nekega vira? Da to pokažemo, je najlažje vzeti vir random_integer, ki, kot lahko uganete po imenu, vrača naključno celo število:
resource "random_integer" "num_instances" {
min = 1
max = 3
}Ta koda generira naključno število od 1 do 3. Poglejmo, kaj se zgodi, če poskusimo uporabiti izhod result tega vira v parametru count vira aws_instance:
resource "aws_instance" "example_3" {
count = random_integer.num_instances.result
ami = "ami-0c55b159cbfafe1f0"
instance_type = "t2.micro"
}Če izvedete ta koda terraform plan, boste dobili naslednjo napako:
Error: Invalid count argument
on main.tf line 30, in resource "aws_instance" "example_3":
30: count = random_integer.num_instances.result
Vrednost "count" je odvisna od atributov vira, ki jih ni mogoče določiti do izvedbe, zato Terraform ne more napovedati, koliko primerkov bo ustvarjenih. Da bi to obšli, uporabite argument -target, da najprej uporabite samo vire, na katerih count temelji.Terraform zahteva, da se count in for_each izračunata v fazi načrtovanja, preden se ustvarijo ali spremenijo kateri koli viri. To pomeni, da se count in for_each lahko sklicujeta na literale, spremenljivke, podatkovne vire in celo sezname virov (pod pogojem, da je njihova dolžina znana v času načrtovanja), vendar ne na izračunane izhodne spremenljivke vira.
count in for_each ne moremo uporabljati v konfiguraciji modula
Kdaj pa kdaj vas bo morda zamikalo, da bi dodali parameter count v konfiguracije modula:
module "count_example" {
source = "..\/..\/..\/..\/modules\/services\/webserver-cluster"
count = 3
cluster_name = "terraform-up-and-running-example"
server_port = 8080
instance_type = "t2.micro"
}Ta koda poskuša uporabiti count znotraj modula, da ustvari tri kopije vira webserver-cluster. Morda boste tudi želeli, da bo povezava modula obvezna glede na kakšno boolovo stanje, tako da dodelite njegovemu parametru count vrednost 0. Takšna koda se zdi povsem smiselna, toda kot rezultat izvajanja terraform plan boste prejeli to napako:
Error: Reserved argument name in module block
on main.tf line 13, in module "count_example":
13: count = 3
Ime "count" je rezervirano za uporabo v prihodnji različici Terraform-a.Na žalost ob izidu Terraform 0.12.6 uporaba count ali for_each v viru modula ni podprta. Glede na opombe ob izdaji Terraform 0.12 (http:\/\/bit.ly\/3257bv4) podjetje HashiCorp načrtuje, da bo to funkcionalnost dodalo v prihodnosti, zato, odvisno od tega, kdaj berete to knjigo, je lahko že na voljo. Da bi izvedeli zagotovo, .
Omejitve namestitev z ničelno časovno zamudo
Uporaba bloka create_before_destroy skupaj z ASG je odličen način za organizacijo razporeditev z ničelnim časom mrtvega delovanja, razen enega odtenka: pravila avtomatskega skaliranja ob tem niso podprta. Ali pa, da smo natančnejši, to ponastavi velikost ASG nazaj na min_size pri vsakem razporejanju, kar je lahko problem, če ste uporabili pravila avtomatskega skaliranja za povečanje števila delujočih strežnikov.
Na primer, modul webserver-cluster vsebuje nekaj virov aws_autoscaling_schedule, ki zjutraj ob 9. uri povečajo število strežnikov v grozdu s dveh na deset. Če izvedete razporeditev, recimo, ob 11. uri, se nova skupina ASG ne bo naložila z desetimi, temveč le z dvema strežnikoma in v tem stanju ostane do 9. ure naslednjega dne.
To omejitev je mogoče obiti na več načinov.
- Spremenite parameter recurrence v aws_autoscaling_schedule z 0 9 * * * („izvajati ob 9. uri“) na nekaj takega, kot je 0-59 9-17 * * * („izvajati vsako minuto od 9. do 17. ure“). Če je v ASG že deset strežnikov, ponovna izvedba tega pravila avtomatskega skaliranja ne bo spremenila ničesar, kar je tisto, kar potrebujemo. Toda, če je skupina ASG pravkar razporejena, to pravilo zagotavlja, da bo največ v minuti število njenega strežnika doseglo deset. To ni ravno eleganten pristop, in velika nihanja s desetih na dva strežnika in nazaj lahko prav tako povzročijo težave uporabnikom.
- Ustvarite prilagojen skript, ki uporablja API AWS za določitev števila aktivnih strežnikov v ASG, ga pokličite z zunanjim virom podatkov (glej točko „Zunanji vir podatkov“ na str. 249) in dodelite parameter desired_capacity skupine ASG vrednosti, ki jo vrne ta skript. Tako se bo vsaka nova instanca ASG vedno zagnala z enako kapaciteto, kot je bila predhodno določena v našem Terraform kodi, kar pa oteži njeno vzdrževanje.
Seveda, v idealnem primeru bi morala imeti Terraform vgrajeno podporo za razporeditve z ničelnim časom mrtvega delovanja, vendar je stanje do maja 2019 ekipa HashiCorp ni nameravala dodati te funkcionalnosti ().
Pravilni načrt se lahko neuspešno izvede
Včasih, ko izvedete ukaz plan, dobite povsem ustrezen načrt razporeditve, vendar ukaz apply vrne napako. Poskusite, na primer, dodati vir aws_iam_user z enakim imenom, ki ste ga uporabili za uporabnika IAM, ki ste ga ustvarili prej v poglavju 2:
resource "aws_iam_user" "existing_user" {
# Vstavite tukaj ime že obstoječega uporabnika IAM,
# da se preizkusite v uporabi ukaza terraform import
name = "yevgeniy.brikman"
}Zdaj, če izvedete ukaz plan, bo Terraform na prvi pogled izdal povsem razumen načrt razporeditve:
Terraform will perform the following actions:
# aws_iam_user.existing_user will be created
+ resource "aws_iam_user" "existing_user" {
+ arn = (known after apply)
+ force_destroy = false
+ id = (known after apply)
+ name = "yevgeniy.brikman"
+ path = "\/"
+ unique_id = (known after apply)
}
Plan: 1 to add, 0 to change, 0 to destroy.Če izvedete ukaz apply, dobite naslednjo napako:
Error: Error creating IAM User yevgeniy.brikman: EntityAlreadyExists:
User with name yevgeniy.brikman already exists.
on main.tf line 10, in resource "aws_iam_user" "existing_user":
10: resource "aws_iam_user" "existing_user" {Težava je seveda v tem, da uporabnik IAM s tem imenom že obstaja. In to se lahko zgodi ne samo z uporabniki IAM, ampak tudi skoraj z vsakim virom. Mogoče je nekdo to obliko ustvaril ročno ali s pomočjo ukazne vrstice, toda kako koli že, ujemanje identifikatorjev vodi do konfliktov. Ta napaka ima več različic, ki pogosto presenetijo začetnike v Terraformu.
Ključni trenutek je, da ukaz terraform plan upošteva samo tiste vire, ki so navedeni v datoteki stanja Terraform. Če so viri ustvarjeni z nekim drugim načinom (na primer ročno, s klikom na gumb v konzoli AWS), ne pridejo v datoteko stanja in zato Terraform ne bo upošteval pri izvajanju ukaza plan. Na koncu se bo na prvi pogled pravilen načrt izkazal za neuspešnega.
Iz tega lahko potegnemo dva zaključka.
- Če ste že začeli delati s Terraform, ne uporabljajte ničesar drugega. Če del vašega omrežja upravlja Terraform, ga ni več mogoče spreminjati ročno. V nasprotnem primeru ne ogrožate le prejemanja čudnih napak Terraform, temveč tudi uničujete mnoge prednosti IaC, saj koda ne bo več točna predstavitev vaše infrastrukture.
- Če imate že kakšno infrastrukturo, uporabite ukaz import. Če začnete uporabljati Terraform z obstoječo infrastrukturo, jo lahko dodate v stanje s pomočjo ukaza terraform import. Tako bo Terraform vedel, katero infrastrukturo je treba upravljati. Ukaz import sprejema dva argumenta. Prvi je naslov vira v vaših konfiguracijskih datotekah. Tu velja enaka sintaksa kot pri povezavah na vire: _. (na primer aws_iam_user.existing_user). Drugi argument je identifikacijska številka vira, ki ga je treba uvoziti. Recimo, da je za ID vira aws_iam_user uporabniško ime (na primer yevgeniy.brikman), ID vira aws_instance pa bo identifikator strežnika EC2 (na primer i-190e22e5). Kako uvoziti vir, je običajno navedeno v dokumentaciji na dnu njegove strani.
Spodaj je prikazan ukaz import, ki omogoča sinhronizacijo vira aws_iam_user, ki ste ga dodali v svojo konfiguracijo Terraform skupaj z uporabnikom IAM v poglavju 2 (seveda namesto yevgeniy.brikman vstavite svoje ime):
$ terraform import aws_iam_user.existing_user yevgeniy.brikmanTerraform se bo obrnil na API AWS, da bi našel vašega uporabnika IAM in ustvaril povezavo med njim in virom aws_iam_user.existing_user v vaši konfiguraciji Terraform. Od tega trenutka naprej bo Terraform vedel, da uporabnik IAM že obstaja, in ne bo poskušal ustvariti novega.
Treba je opozoriti, da lahko, če imate že veliko virov, ki jih želite uvoziti v Terraform, ročno pisanje kode in uvoz vsakega od njih zaporedno postane zamudno opravilo. Zato je smiselno razmisliti o orodju, kot je Terraforming (http://terraforming.dtan4.net/), ki lahko samodejno uvaža kodo in stanje iz računa AWS.
Refaktoring ima lahko svoje pasti
Refaktoring je razširjena praksa v programiranju, kjer spremenite notranjo strukturo kode, ne da bi spremenili zunanje vedenje. To je potrebno, da postane koda bolj razumljiva, urejena in enostavna za vzdrževanje. Refaktoring je nepogrešljiva metoda, ki jo je treba redno uporabljati. Toda ko gre za Terraform ali katero koli drugo sredstvo IaC, je treba izjemno paziti, kaj se misli s „zunanjim vedenjem“ dela kode, drugače se lahko pojavijo nepričakovane težave.
Na primer, razširjena oblika refaktoriranja je zamenjava imen spremenljivk ali funkcij z bolj razumljivimi. Mnoge IDE imajo vgrajeno podporo za refaktoring in lahko samodejno preimenujejo spremenljivke in funkcije v okviru celotnega projekta. V programskih jezikih splošne rabe je to trivialna procedura, o kateri ni treba razmišljati, vendar je v Terraformu s tem treba biti izjemno previden, sicer lahko pride do motenj v delovanju.
Na primer, modul webserver-cluster ima vhodno spremenljivko cluster_name:
variable "cluster_name" { description = "Ime, ki se uporablja za vse vire grozda" type = string }Predstavljajte si, da ste začeli uporabljati ta modul za izvajanje mikroservisa z imenom foo. Kasneje ste želeli preimenovati svojo storitev v bar. Ta sprememba se morda zdi trivialna, vendar lahko v resnici povzroči motnje v delovanju.
Dejstvo je, da modul webserver-cluster uporablja spremenljivko cluster_name v številnih virih, vključno s parametrom name dveh varnostnih skupin in ALB:
resource "aws_lb" "example" { name = var.cluster_name load_balancer_type = "application" subnets = data.aws_subnet_ids.default.ids security_groups = [aws_security_group.alb.id] }Če spremenite parameter name v kakem viru, Terraform odstrani staro različico tega vira in namesto nje ustvari novo. Ampak če je tak vir ALB, boste v obdobju med njegovo odstranitvijo in nalaganjem nove različice brez mehanizma za preusmerjanje prometa na vaš spletni strežnik. Enako velja, če se odstrani varnostna skupina, vaši strežniki začnejo zavračati vsak omrežni promet, dokler nova skupina ne bo ustvarjena.
Še ena vrsta refaktoringa, ki vas lahko zanima, je sprememba identifikatorja Terraform. Uporabimo kot primer vir aws_security_group v modulu webserver-cluster:
resource "aws_security_group" "instance" { # (...) }Identifikator tega vira se imenuje instance. Predstavljajte si, da ste med refaktoringom sklenili spremeniti njegovo ime v bolj razumljivo (po vašem mnenju) ime cluster_instance:
resource "aws_security_group" "cluster_instance" { # (...) }Kaj se na koncu zgodi? Pravilno: motnja v delovanju.
Terraform povezuje ID varje resurs s identifikatorom oblaka. Na primjer, iam_user se povezuje s IAM korisničkim ID-om u AWS-u, a aws_instance s ID-om AWS EC2 servera. Ako promijenite identifikator resursa (recimo, sa instance na cluster_instance, kao u slučaju aws_security_group), za Terraform to izgleda kao da ste obrisali staru resurs i dodali novu. Kada primijenite te promjene, Terraform će obrisati staru sigurnosnu grupu i stvoriti drugu, a vaši serveri će početi odbijati sav mrežni promet.
Evo četiri osnovne lekcije koje trebate izvući iz ove rasprave.
- Uvijek koristite komandu plan. Njeno korištenje može otkriti sve te prepreke. Pažljivo pregledajte njen izlaz i obratite pažnju na situacije kada Terraform planira izbrisati resurse za koje vjerovatno ne bi trebali biti obrisani.
- Stvarajte prije nego što obrišete. Ako želite zamijeniti resurs, dobro razmislite da li trebate stvoriti zamjenu prije nego što obrišete original. Ako je odgovor potvrdan, create_before_destroy može biti od pomoći. Isti rezultat možete postići ručno, obavljajući dva koraka: prvo dodajte novi resurs u konfiguraciju i pokrenite komandu apply, a zatim obrišite stari resurs iz konfiguracije i ponovo upotrijebite komandu apply.
- Promjena identifikatora zahtijeva promjenu stanja. Ako želite promijeniti identifikator povezan s resursom (npr. preimenovati aws_security_group s instance na cluster_instance), a da pritom ne izbrišete resurs i ne stvorite novu verziju, morate ažurirati datoteku stanja Terraform-a. Nikada nemojte to raditi ručno - umjesto toga koristite komandu terraform state. Kada preimenujete identifikatore, trebate izvršiti komandu terraform state mv, koja ima sljedeću sintaksu:
terraform state mvORIGINAL_REFERENCE je izraz koji se odnosi na resurs u njegovom trenutnom obliku, a NEW_REFERENCE je mjesto na koje ga želite premjestiti. Na primjer, prilikom preimenovanja grupe aws_security_group s instance na cluster_instance treba izvršiti sljedeću komandu:
$ terraform state mv aws_security_group.instance aws_security_group.cluster_instanceTako da obavestite Terraform da bi stanje koje se prethodno odnosilo na aws_security_group.instance sada trebalo da bude povezano sa aws_security_group.cluster_instance. Ako nakon preimenovanja i pokretanja ove komande terraform plan ne prikaže nikakve promene, uradili ste sve ispravno.
- Neke parametre nije moguće menjati. Parametri mnogih resursa su nepromenljivi. Ako pokušate da ih promenite, Terraform će obrisati stari resurs i kreirati novi umesto njega. Na stranici svakog resursa obično je naznačeno šta se dešava prilikom promene određenog parametra, stoga se uvek konsultujte sa dokumentacijom. Uvek koristite komandu plan i razmotrite ostvarivost primene strategije create_before_destroy.
Odložena doslednost slaže se… sa odlaganjem
API nekih provajdera u oblaku, kao što je AWS, su asinhroni i imaju odloženu doslednost. Asinhronost znači da interfejs može odmah vratiti odgovor, ne čekajući završetak tražene radnje. Odložena doslednost znači da može proći vreme za širenje promena kroz ceo sistem; dok se to dešava, vaši odgovori mogu biti neskladni i zavisiti od toga koji izvor podataka odgovara na vaše API pozive.
Zamislite, na primer, da šaljete API poziv AWS-u tražeći da kreira EC2 server. API će gotovo odmah vratiti 'uspešan' odgovor (201 Created), ne čekajući da se sam server zapravo kreira. Ako pokušate da se povežete odmah, gotovo će sigurno ne uspeti, jer AWS još uvek inicijalizuje resurse ili, alternativno, server se još nije pokrenuo. Štaviše, ako napravite još jedan poziv da dobijete informacije o ovom serveru, može se javiti greška (404 Not Found). Stvar je u tome što se informacije o ovom EC2 serveru još uvek mogu širiti preko AWS-a, i da bi postali dostupni svuda, potrebno je sačekati nekoliko sekundi.
Kod svake upotrebe asinhronog API-ja sa odloženom doslednošću, trebalo bi da povremeno ponavljate svoj zahtev dok se radnja ne završi i ne proširi kroz sistem. Nažalost, AWS SDK ne pruža dobre alate za to, a projekat Terraform je ranije trpeo od mnogih grešaka kao što je 6813 (https://github.com/hashicorp/terraform/issues/6813):
$ terraform apply aws_subnet.private-persistence.2: InvalidSubnetID.NotFound: The subnet ID 'subnet-xxxxxxx' does not existZ drugimi besedami, ustvarjate vir (na primer podnet) in nato poskušate pridobiti informacije o njem (kot je ID pravkar ustvarjenega podmrežja), a Terraform jih ne more najti. Večina takšnih napak (vključno z 6813) je že bila odpravljena, a se občasno še vedno pojavljajo, še posebej ko Terraform doda podporo novemu tipu virov. To je frustrirajuće, vendar v večini primerov ne povzroča nobene škode. Ob ponovnem izvajanju terraform apply bi moralo vse delovati, saj bo do takrat informacija že razširjena po sistemu.
Ta odlomek je vzet iz knjige Evgenija Brikmana .
Izvor: habr.com
