Vytvoríme niekoľko potenciálnych problémov, vrátane tých, ktoré súvisia s cyklami, podmienkami if a metódami nasadzovania, ako aj s všeobecnejšími problémami, ktoré sa týkajú Terraformu ako celku:
- parametre count a for_each majú obmedzenia;
- obmedzenia nasadzovania s nulovým časom výpadku;
- aj dobrý plán môže byť neúspešný;
- refaktoring môže mať svoje úskalia;
- odložená konzistencia súvisí… s odkladaním.
Parametre count a for_each majú obmedzenia
V príkladoch tejto kapitoly sa parameter count a výraz for_each aktívne používajú v cykloch a podmienkovej logike. Fungujú dobre, ale existujú dve dôležité obmedzenia, o ktorých je potrebné vedieť.
- V count a for_each nie je možné odkazovať na žiadne výstupné premenné zdroja.
- count a for_each nie je možné použiť v konfigurácii modulu.
V count a for_each nie je možné odkazovať na žiadne výstupné premenné zdroja
Predstavte si, že potrebujete nasadiť niekoľko serverov EC2 a z akéhokoľvek dôvodu nechcete používať ASG. Váš kód by mohol vyzerať takto:
resource "aws_instance" "example_1" {
count = 3
ami = "ami-0c55b159cbfafe1f0"
instance_type = "t2.micro"
}
Poďme ich preskúmať jeden po druhom.
Keďže parametru count je priradená statická hodnota, tento kód bude fungovať bez problémov: keď spustíte príkaz apply, vytvorí tri servery EC2. Ale čo ak by ste chceli nasadiť jeden server v každej dostupnej zóne (Availability Zone alebo AZ) v rámci aktuálneho regiónu AWS? Môžete zabezpečiť, aby váš kód načítal zoznam zón zo zdroja dát aws_availability_zones a potom "cyklicky" prešiel každou z nich a vytvoril v nej server EC2 pomocou parametra count a prístupu k poľa podľa indexu:
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" {}Tento kód bude tiež perfektne fungovať, pretože parameter count môže bez problémov odkazovať na zdroje dát. Ale čo sa stane, ak počet serverov, ktoré potrebujete vytvoriť, závisí od výstupu nejakého zdroja? Na demonštráciu je najlepšie zobrať zdroj random_integer, ktorý, ako už názov napovedá, vracia náhodné celé číslo:
resource "random_integer" "num_instances" {
min = 1
max = 3
}Tento kód generuje náhodné číslo od 1 do 3. Pozrime sa, čo sa stane, ak sa pokúsime použiť výstup result tohto zdroja v parametri count zdroja aws_instance:
resource "aws_instance" "example_3" {
count = random_integer.num_instances.result
ami = "ami-0c55b159cbfafe1f0"
instance_type = "t2.micro"
}Ak spustíte tento kód terraform plan, dostanete nasledujúcu chybu:
Error: Invalid count argument
on main.tf line 30, in resource "aws_instance" "example_3":
30: count = random_integer.num_instances.result
Hodnota "count" závisí od atribútov zdroja, ktoré nie je možné určiť až do vykonania, takže Terraform nemôže predpovedať, koľko inštancií bude vytvorených. Na ich obídenie použite argument -target, aby ste najskôr aplikovali len zdroje, od ktorých count závisí.Terraform vyžaduje, aby sa count a for_each vypočítavali v etape plánovania, pred vytvorením alebo zmenou akýchkoľvek zdrojov. To znamená, že count a for_each môžu odkazovať na literály, premenné, zdroje údajov a dokonca aj zoznamy zdrojov (za predpokladu, že ich dĺžku je možné určiť počas plánovania), ale nie na vypočítané výstupné premenné zdroja.
count a for_each nemožno použiť v konfigurácii modulu
Niekedy môžete mať pokušenie pridať parameter count do konfigurácií modulu:
module "count_example" {
source = "..\/..\/..\/..\/modules\/services\/webserver-cluster"
count = 3
cluster_name = "terraform-up-and-running-example"
server_port = 8080
instance_type = "t2.micro"
}Tento kód sa pokúša použiť count v rámci modulu na vytvorenie troch kópií zdroja webserver-cluster. Alebo možno chcete urobiť pripojenie modulu voliteľným v závislosti od nejakej boolean podmienky, pričom jeho parametru count priradíte hodnotu 0. Takýto kód sa zdá byť celkom rozumný, avšak pri vykonaní terraform plan dostanete túto chybu:
Error: Reserved argument name in module block
on main.tf line 13, in module "count_example":
13: count = 3
Názov "count" je vyhradený na použitie v budúcej verzii Terraform.Bohužiaľ, k maniu Terraform 0.12.6 nepodporuje použitie count alebo for_each v zdroji module. Podľa poznámok k vydaniu Terraform 0.12 (http:\/\/bit.ly\/3257bv4) spoločnosť HashiCorp plánuje túto funkciu pridať v budúcnosti, takže v závislosti od toho, kedy túto knihu čítate, môže byť už dostupná. Ak chcete vedieť určite, .
Obmedzenia nasadení bezprestojového času
Použitie bloku create_before_destroy v kombinácii s ASG je skvelé riešenie na organizovanie nasadení s nulovým časom prestoja, ak neberieme do úvahy jeden aspekt: pravidlá automatického škálovania nie sú podporované. Alebo, ak sa vyjadriť presnejšie, každé nasadenie resetuje veľkosť ASG späť na min_size, čo môže byť problém, ak ste používali pravidlá automatického škálovania na zvýšenie počtu spustených serverov.
Napríklad modul webserver-cluster obsahuje niekoľko zdrojov aws_autoscaling_schedule, ktoré o 9:00 zvýšia počet serverov v klastrom z dvoch na desať. Ak sa vykoná nasadenie, povedzme, o 11:00, nová skupina ASG sa nenačíta s desiatimi, ale iba s dvomi servermi a zostane v tomto stave až do 9:00 nasledujúceho dňa.
Toto obmedzenie je možné obísť niekoľkými spôsobmi.
- Zmeniť parameter recurrence v aws_autoscaling_schedule z 0 9 * * * („spúšťať o 9:00“) na niečo ako 0-59 9-17 * * * („spúšťať každú minútu od 9:00 do 17:00“). Ak už ASG obsahuje desať serverov, opakované vykonanie tohto pravidla automatického škálovania nič nezmení, čo je to, čo potrebujeme. Ale ak bola skupina ASG nedávno nasadená, toto pravidlo zaručuje, že maximálne do minúty dosiahne počet jej serverov desať. Nie je to veľmi elegantný prístup a veľké skoky z desiatich na dva servery a späť môžu tiež spôsobiť problémy používateľom.
- Vytvoriť vlastný skript, ktorý využíva API AWS na určenie počtu aktívnych serverov v ASG, vyvolať ho pomocou externého zdroja dát (pozri časť „Externý zdroj dát“ na str. 249) a priradiť parametru desired_capacity skupiny ASG hodnotu, ktorú tento skript vráti. Takto každý nový inštancia ASG sa vždy spustí s rovnakou kapacitou, akú má váš kód Terraform, a sťažuje jeho údržbu.
Samozrejme, ideálne by bolo, keby Terraform mal zabudovanú podporu pre nasadenia s nulovým časom prestoja, ale k máju 2019 tím HashiCorp neplánoval pridávať túto funkcionalitu ().
Správny plán môže byť neúspešne realizovaný
Niekedy pri vykonávaní príkazu plan získate celkom korektný plán nasadenia, avšak príkaz apply vracia chybu. Skúste napríklad pridať zdroj aws_iam_user s rovnakým menom, aké ste použili pre IAM používateľa, ktorého ste vytvorili skôr v kapitole 2:
resource "aws_iam_user" "existing_user" {
# Zadajte sem meno už existujúceho IAM používateľa,
# aby ste si vyskúšali príkaz terraform import
name = "yevgeniy.brikman"
}Teraz, ak vykonáte príkaz plan, Terraform vygeneruje na prvý pohľad celkom rozumný plán nasadenia:
Terraform vykoná nasledujúce akcie:
# aws_iam_user.existing_user bude vytvorený
+ resource "aws_iam_user" "existing_user" {
+ arn = (známe po aplikovaní)
+ force_destroy = false
+ id = (známe po aplikovaní)
+ name = "yevgeniy.brikman"
+ path = "\/"
+ unique_id = (známe po aplikovaní)
}
Plán: 1 pridať, 0 zmeniť, 0 zničiť.Ak vykonáte príkaz apply, dostanete nasledujúcu chybu:
Chyba: Chyba pri vytváraní IAM používateľa yevgeniy.brikman: EntityAlreadyExists:
Používateľ s menom yevgeniy.brikman už existuje.
na main.tf riadok 10, v resource "aws_iam_user" "existing_user":
10: resource "aws_iam_user" "existing_user" {Problém je samozrejme v tom, že IAM používateľ s týmto menom už existuje. A to sa môže stať nielen s IAM používateľmi, ale aj takmer s akýmkoľvek zdrojom. Možno niekto vytvoril tento zdroj ručne alebo pomocou príkazového riadka, ale bez ohľadu na to, zhodovanie identifikátorov vedie k konfliktom. Táto chyba má mnoho variant, ktoré často prekvapujú nováčikov v Terraform.
Kľúčovým bodom je, že príkaz terraform plan zohľadňuje iba tie zdroje, ktoré sú uvedené v súbore stavu Terraform. Ak boli zdroje vytvorené iným spôsobom (napríklad ručne kliknutím na konzolu AWS), nebudú zahrnuté do súboru stavu a teda, Terraform ich nebude brať do úvahy pri vykonávaní príkazu plan. Nakoniec sa korektný na prvý pohľad plán ukáže ako neúspešný.
Z toho môžeme vyvodiť dve lekcie.
- Ak ste už začali pracovať s Terraform, nepoužívajte nič iné. Ak časť vašej infraštruktúry je spravovaná pomocou Terraform,ďalej ju nemožno meniť ručne. V opačnom prípade riskujete nielen podivné chyby v Terraform, ale aj popierate mnohé výhody IaC, pretože kód už nebude presným zobrazením vašej infraštruktúry.
- Ak máte už nejakú infraštruktúru, použite príkaz import. Ak začínate používať Terraform s existujúcou infraštruktúrou, môžete ju pridať do súboru stavu pomocou príkazu terraform import. Tak Terraform zistí, ktorou infraštruktúrou má spravovať. Príkaz import prijíma dva argumenty. Prvým je adresa zdroja vo vašich konfiguračných súboroch. Tu je rovnaká syntax ako v odkazoch na zdroje: _. (napríklad aws_iam_user.existing_user). Druhým argumentom je identifikátor zdroja, ktorý chcete importovať. Povedzme, že ako ID zdroja aws_iam_user vystupuje meno používateľa (napríklad yevgeniy.brikman), a ID zdroja aws_instance bude identifikátor servera EC2 (napríklad i-190e22e5). To, ako importovať zdroj, sa zvyčajne uvádza v dokumentácii na konci jeho stránky.
Nižšie je uvedený príkaz import, ktorý umožňuje synchronizovať zdroj aws_iam_user, ktorý ste pridali do svojej konfigurácie Terraform spolu s používateľom IAM v kapitole 2 (samozrejme, namiesto yevgeniy.brikman treba vložiť vaše meno):
$ terraform import aws_iam_user.existing_user yevgeniy.brikmanTerraform sa spojí s API AWS, aby našiel vášho používateľa IAM a vytvoril v súbore stavu súvislosť medzi ním a zdrojom aws_iam_user.existing_user vo vašej konfigurácii Terraform. Od tohto okamihu pri vykonávaní príkazu plan Terraform bude vedieť, že používateľ IAM už existuje a nebude sa snažiť ho vytvoriť znova.
Treba poznamenať, že ak máte už veľa zdrojov, ktoré chcete importovať do Terraform, ručné písanie kódu a import každého z nich po jednom môže byť únavná úloha. Preto sa oplatí zvážiť nástroj, ako je Terraforming (http://terraforming.dtan4.net/), ktorý môže automaticky importovať kód a stav z účtu AWS.
Refaktoring môže mať svoje nástrahy
Refaktoring je bežná prax v programovaní, keď meníte vnútornú štruktúru kódu, pričom vonkajšie správanie ostáva nezmenené. Je to potrebné na to, aby bol kód zrozumiteľnejší, prehľadnejší a jednoduchší na údržbu. Refaktoring je nevyhnutná technika, ktorú by ste mali pravidelne aplikovať. Ale pokiaľ ide o Terraform alebo akýkoľvek iný nástroj IaC, treba veľmi opatrne pristupovať k tomu, čo sa myslí pod „vonkajším správaním“ časti kódu, inak môžu vzniknúť neočakávané problémy.
Napríklad bežný typ refaktoringu pozostáva zo zmeny názvov premenných alebo funkcií na zrozumiteľnejšie. Mnohé IDE majú zabudovanú podporu refaktoringu a dokážu automaticky premenovať premenné a funkcie v rámci celého projektu. V jazykoch všeobecného použitia je to triviálna procedúra, nad ktorou sa netreba zamýšľať, avšak v Terrafome je potrebné byť mimoriadne opatrný, inak sa môžete stretnúť s problémami v prevádzke.
Napríklad modul webserver-cluster má vstupnú premennú cluster_name:
variable "cluster_name" { description = "Názov, ktorý sa použije pre všetky zdroje klastrov" type = string }Predstavte si, že ste začali používať tento modul na nasadenie mikroservisu s názvom foo. Neskôr ste sa rozhodli premenovať svoj servis na bar. Táto zmena sa môže zdať triviálna, ale v skutočnosti môže spôsobiť problémy v prevádzke.
Ide o to, že modul webserver-cluster používa premennú cluster_name v množstve zdrojov, vrátane parametra name dvoch zabezpečovacích skupín a 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] }Ak zmeníte parameter name v niektorom zdroji, Terraform odstráni starú verziu tohto zdroja a vytvorí novú. Avšak ak je takýmto zdrojom ALB, v období medzi jeho odstránením a načítaním novej verzie nebudete mať žiadny mechanizmus na presmerovanie prevádzky na váš webový server. Rovnako ak sa odstráni zabezpečovacia skupina, vaše servery začnú odmietať akúkoľvek sieťovú prevádzku, pokým nebude vytvorená nová skupina.
Ďalším typom refaktoringu, ktorý by vás mohol zaujímať, je zmena identifikátora Terrafome. Vezmime si za príklad zdroj aws_security_group v module webserver-cluster:
resource "aws_security_group" "instance" { # (...) }Identifikátor tohto zdroja sa nazýva instance. Predstavte si, že počas refaktoringu ste sa rozhodli zmeniť ho na zrozumiteľnejšie (podľa vás) meno cluster_instance:
resource "aws_security_group" "cluster_instance" { # (...) }Čo sa nakoniec stane? Správne: problém v prevádzke.
Terraform prepojí ID každého zdroja s identifikátorom cloudového poskytovateľa. Napríklad, iam_user sa pripojí k ID používateľa IAM v AWS a aws_instance – k ID servera AWS EC2. Ak zmeníte identifikátor zdroja (napríklad z instance na cluster_instance, ako v prípade aws_security_group), pre Terraform to bude vyzerať, ako by ste odstránili starý zdroj a pridali nový. Ak tieto zmeny aplikujete, Terraform odstráni starú bezpečnostnú skupinu a vytvorí inú, pričom vaše servery začnú odmietať akýkoľvek sieťový prenos.
Tu sú štyri základné lekcie, ktoré by ste si mali z tohto diskurzu odniesť.
- Vždy použite príkaz plan. Pomocou neho môžete odhaliť všetky tieto prekážky. Dôkladne si prejdite jeho výstup a venujte pozornosť situáciám, kedy Terraform plánuje odstrániť zdroje, ktoré pravdepodobne nie je potrebné odstraňovať.
- Vytvorte, skôr ako odstránite. Ak chcete nahradiť zdroj, dobre zvážte, či je potrebné vytvoriť náhradu pred odstránením originálu. Ak je odpoveď kladná, môže vám pomôcť create_before_destroy. Rovnakého výsledku môžete dosiahnuť manuálne, vykonaním dvoch krokov: najprv pridajte do konfigurácie nový zdroj a spustite príkaz apply, potom odstráňte zo konfigurácie starý zdroj a znova použite príkaz apply.
- Zmena identifikátorov si vyžaduje zmenu stavu. Ak chcete zmeniť identifikátor, ktorý je spojený so zdrojom (napríklad premenovať aws_security_group z instance na cluster_instance), a zabrániť tak odstráneniu zdroja a vytvoreniu jeho novej verzie, je potrebné zodpovedajúcim spôsobom aktualizovať súbor stavu Terraform. Nikdy to nerobte ručne – namiesto toho použite príkaz terraform state. Pri premenovávaní identifikátorov by ste mali vykonať príkaz terraform state mv, ktorý má nasledujúcu syntaktickú štruktúru:
terraform state mvORIGINAL_REFERENCE je výraz, ktorý odkazuje na zdroj vo svojej súčasnej podobe, a NEW_REFERENCE je miesto, kam chcete, aby bol presunutý. Napríklad, pri premenovávaní skupiny aws_security_group z instance na cluster_instance by ste mali vykonať nasledujúci príkaz:
$ terraform state mv aws_security_group.instance aws_security_group.cluster_instanceTakže informujete Terraform, že stav, ktorý sa predtým týkal aws_security_group.instance, teraz by mal byť spojený s aws_security_group.cluster_instance. Ak po premenovaní a spustení tohto príkazu terraform plan neukáže žiadne zmeny, znamená to, že ste všetko urobili správne.
- Niektoré parametre nie je možné meniť. Parametre mnohých zdrojov sú nemenné. Ak sa ich pokúsite zmeniť, Terraform odstráni starý zdroj a vytvorí namiesto neho nový. Na stránke každého zdroja sa zvyčajne uvádza, čo sa deje pri zmene určitého parametra, preto nezabudnite porovnávať s dokumentáciou. Vždy používajte príkaz plan a zvážte vhodnosť uplatnenia stratégie create_before_destroy.
Odložená konzistencia sa dohoduje… s odložením.
API niektorých cloudových poskytovateľov, ako je AWS, sú asynchrónne a majú odloženú konzistenciu. Asynchrónnosť znamená, že rozhranie môže okamžite vrátiť odpoveď bez čakania na dokončenie požadovanej akcie. Odložená konzistencia znamená, že na rozšírenie zmien po celom systéme môže byť potrebný čas; počas tohto procesu môžu byť vaše odpovede nekonzistentné a závisieť od toho, ktorá kópia zdroja údajov reaguje na vaše API volania.
Predstavte si napríklad, že vykonávate API volanie na AWS s požiadavkou na vytvorenie servera EC2. API vráti "úspešnú" odpoveď (201 Created) takmer okamžite, bez čakania na vytvorenie samotného servera. Ak sa okamžite pokúsite k nemu pripojiť, takmer určite sa to nepodarí, pretože AWS stále inicializuje zdroje alebo, alternatívne, server sa ešte nenaštartoval. Navyše, ak vykonáte ďalšie volanie na získanie informácií o tomto serveri, môže sa objaviť chyba (404 Not Found). Ide o to, že informácie o tomto serveri EC2 sa stále môžu šíriť po AWS a kým sa stanú dostupnými všade, musíte počkať niekoľko sekúnd.
Pri každom použití asynchrónneho API s odloženou konzistenciou musíte periodicky opakovať svoj požiadavok, kým akcia nebude dokončená a nebude sa šíriť po systéme. Bohužiaľ, AWS SDK neposkytuje žiadne dobré nástroje na to, a projekt Terraform predtým trpel mnohými chybami ako 6813 (https://github.com/hashicorp/terraform/issues/6813):
$ terraform apply aws_subnet.private-persistence.2: InvalidSubnetID.NotFound: Subnet ID 'subnet-xxxxxxx' neexistujeInými slovami, vytvárate zdroj (napríklad podsieť) a potom sa snažíte získať o ňom nejaké informácie (ako ID práve vytvorenej podsiete), ale Terraform ich nemôže nájsť. Väčšina takýchto chýb (vrátane 6813) už bola opravená, ale občas sa ešte objavujú, najmä keď sa do Terraform pridáva podpora nového typu zdrojov. Je to frustrujúce, ale vo väčšine prípadov to neznamená žiadnu škodu. Po opakovaní príkazu terraform apply by to malo fungovať, pretože v tom momente už budú informácie rozšírené po systéme.
Tento úryvok je prevzatý z knihy Jevgenija Brikmana .
Zdroj: habr.com
