Teraformné nástrahy

Teraformné nástrahy
Zdôraznime niekoľko úskalí, vrátane tých, ktoré súvisia so slučkami, príkazmi if a technikami nasadenia, ako aj všeobecnejšie problémy, ktoré ovplyvňujú Terraform vo všeobecnosti:

  • parametre count a for_each majú obmedzenia;
  • obmedziť nasadenie nulových prestojov;
  • aj dobrý plán môže zlyhať;
  • refaktoring môže mať svoje úskalia;
  • odložená koherencia je v súlade... s odložením.

Parametre count a for_each majú obmedzenia

Príklady v tejto kapitole vo veľkej miere využívajú parameter count a výraz for_each v slučkách a podmienenej logike. Fungujú dobre, ale majú dve dôležité obmedzenia, ktoré si musíte uvedomiť.

  • Count a for_each nemôžu odkazovať na žiadne výstupné premenné zdroja.
  • count a for_each nemožno použiť v konfigurácii modulu.

count a for_each nemôže odkazovať na žiadne výstupné premenné zdroja

Predstavte si, že potrebujete nasadiť niekoľko serverov EC2 a z nejakého dôvodu nechcete používať ASG. Váš kód môže byť takýto:

resource "aws_instance" "example_1" {
   count             = 3
   ami                = "ami-0c55b159cbfafe1f0"
   instance_type = "t2.micro"
}

Pozrime sa na ne jeden po druhom.

Keďže parameter count je nastavený na statickú hodnotu, tento kód bude fungovať bez problémov: keď spustíte príkaz apply, vytvorí tri servery EC2. Čo keby ste však chceli nasadiť jeden server v každej zóne dostupnosti (AZ) v rámci vašej aktuálnej oblasti AWS? Môžete nechať svoj kód načítať zoznam zón zo zdroja údajov aws_availability_zones a potom prejsť cez každú z nich a vytvoriť v nej server EC2 pomocou parametra count a prístupu k indexu poľa:

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ž fungovať dobre, pretože parameter count môže bez problémov odkazovať na zdroje údajov. Čo sa však stane, ak počet serverov, ktoré musíte vytvoriť, závisí od výstupu nejakého zdroja? Aby sme to demonštrovali, najjednoduchším spôsobom je použiť 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 medzi 1 a 3. Pozrime sa, čo sa stane, ak sa pokúsime použiť výstup 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 plán terraform na tomto kóde, zobrazí sa nasledujúca chyba:

Error: Invalid count argument

   on main.tf line 30, in resource "aws_instance" "example_3":
   30: count = random_integer.num_instances.result

The "count" value depends on resource attributes that cannot be determined until apply, so Terraform cannot predict how many instances will be created. To work around this, use the -target argument to first apply only the resources that the count depends on.

Terraform vyžaduje, aby sa počet a for_each vypočítali počas fázy plánovania, predtým ako sa vytvoria alebo upravia akékoľvek zdroje. To znamená, že count a for_each môžu odkazovať na literály, premenné, zdroje údajov a dokonca aj zoznamy zdrojov (pokiaľ ich dĺžku možno určiť v čase plánovania), ale nie na vypočítané výstupné premenné zdrojov.

count a for_each nemožno použiť v konfigurácii modulu

Jedného dňa môžete byť v pokušení pridať parameter počtu do konfigurácie 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ť počet v module na vytvorenie troch kópií zdroja webserver-cluster. Alebo možno budete chcieť urobiť pripojenie modulu voliteľným v závislosti od nejakej booleovskej podmienky nastavením jeho parametra počtu na 0. Môže to vyzerať ako rozumný kód, ale pri vykonávaní plánu terraform sa zobrazí táto chyba:

Error: Reserved argument name in module block

   on main.tf line 13, in module "count_example":
   13: count = 3

The name "count" is reserved for use in a future version of Terraform.

Bohužiaľ, od Terraform 0.12.6 nie je podporované použitie count alebo for_each v zdroji modulu. Podľa poznámok k vydaniu Terraform 0.12 (http://bit.ly/3257bv4) plánuje HashiCorp túto schopnosť pridať v budúcnosti, takže v závislosti od toho, kedy si prečítate túto knihu, môže byť už dostupná. Aby ste to s istotou zistili, prečítajte si zoznam zmien Terraform tu.

Obmedzenia nulových výpadkov nasadenia

Použitie bloku create_before_destroy v kombinácii s ASG je skvelým riešením na vytváranie nasadení s nulovými prestojmi, až na jednu výhradu: pravidlá automatického škálovania nie sú podporované. Alebo aby som bol presnejší, toto obnoví veľkosť ASG späť na min_size pri každom nasadení, čo by mohol byť problém, ak by ste na zvýšenie počtu spustených serverov používali pravidlá automatického škálovania.

Napríklad modul webserver-cluster obsahuje pár zdrojov aws_autoscaling_schedule, ktorý o 9:11 zvyšuje počet serverov v klastri z dvoch na desať. Ak nasadíte povedzme o 9:XNUMX, nový ASG sa spustí iba s dvoma servermi namiesto desiatich a zostane tak až do XNUMX:XNUMX nasledujúceho dňa.

Toto obmedzenie sa dá obísť niekoľkými spôsobmi.

  • Zmeňte parameter opakovania v aws_autoscaling_schedule z 0 9 * * * (“bežať o 9:0”) na niečo ako 59-9 17-9 * * * (“bežať každú minútu od 5:XNUMX do XNUMX:XNUMX”). Ak už má ASG desať serverov, opätovným spustením tohto pravidla automatického škálovania sa nič nezmení, čo chceme. Ale ak bol ASG nasadený len nedávno, toto pravidlo zabezpečí, že maximálne za minútu dosiahne počet jeho serverov desať. Toto nie je úplne elegantný prístup a veľké skoky z desiatich na dva servery a späť môžu tiež spôsobiť používateľom problémy.
  • Vytvorte vlastný skript, ktorý používa AWS API na určenie počtu aktívnych serverov v ASG, zavolajte ho pomocou externého zdroja údajov (pozrite „Externý zdroj údajov“ na strane 249) a nastavte parameter požadovanej_kapacity ASG na hodnotu vrátenú scenár. Týmto spôsobom bude každá nová inštancia ASG vždy bežať s rovnakou kapacitou ako existujúci kód Terraform a bude náročnejšia na údržbu.

Samozrejme, Terraform by v ideálnom prípade mal vstavanú podporu pre nasadenia s nulovými prestojmi, ale od mája 2019 tím HashiCorp neplánoval pridať túto funkciu (podrobnosti - tu).

Správny plán môže byť neúspešne implementovaný

Niekedy príkaz plan vytvorí úplne správny plán nasadenia, ale príkaz Apply vráti chybu. Skúste napríklad pridať zdroj aws_iam_user s rovnakým názvom, aký ste použili pre používateľa IAM, ktorého ste vytvorili skôr v kapitole 2:

resource "aws_iam_user" "existing_user" {
   # Подставьте сюда имя уже существующего пользователя IAM,
   # чтобы попрактиковаться в использовании команды terraform import
   name = "yevgeniy.brikman"
}

Ak teraz spustíte príkaz plánu, Terraform vygeneruje zdanlivo rozumný plán nasadenia:

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.

Ak spustíte príkaz použiť, zobrazí sa nasledujúca chyba:

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" {

Problém je samozrejme v tom, že používateľ IAM s týmto menom už existuje. A to sa môže stať nielen používateľom IAM, ale takmer akémukoľvek zdroju. Je možné, že niekto vytvoril tento zdroj manuálne alebo pomocou príkazového riadka, ale v každom prípade zhoda ID vedie ku konfliktom. Existuje mnoho variácií tejto chyby, ktoré často zaskočia nováčikov v Terraforme.

Kľúčovým bodom je, že príkaz plán terraform berie do úvahy len tie zdroje, ktoré sú špecifikované v súbore stavu Terraform. Ak sú zdroje vytvorené nejakým iným spôsobom (napríklad manuálne kliknutím v konzole AWS), neskončia v súbore stavu a preto ich Terraform nebude brať do úvahy pri vykonávaní príkazu plánu. Výsledkom je, že plán, ktorý sa na prvý pohľad zdá byť správny, sa ukáže ako neúspešný.

Z toho plynú dve ponaučenia.

  • Ak ste už s Terraformom začali pracovať, nič iné nepoužívajte. Ak je časť vašej infraštruktúry spravovaná pomocou Terraform, už ju nemôžete upravovať manuálne. V opačnom prípade riskujete nielen podivné chyby Terraform, ale tiež negujete mnohé z výhod IaC, pretože kód už nebude presnou reprezentáciou vašej infraštruktúry.
  • Ak už máte 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. Terraform tak bude vedieť, akú infraštruktúru treba spravovať. Príkaz import má dva argumenty. Prvým je adresa zdroja vo vašich konfiguračných súboroch. Syntax je tu rovnaká ako pre odkazy na zdroje: _. (ako aws_iam_user.existing_user). Druhým argumentom je ID zdroja, ktorý sa má importovať. Povedzme, že ID prostriedku aws_iam_user je meno používateľa (napríklad yevgeniy.brikman) a ID prostriedku aws_instance je ID servera EC2 (napríklad i-190e22e5). Ako importovať zdroj je zvyčajne uvedené v dokumentácii v spodnej časti stránky.

    Nižšie je uvedený príkaz importu, ktorý synchronizuje zdroj aws_iam_user, ktorý ste pridali do svojej konfigurácie Terraform spolu s používateľom IAM v kapitole 2 (samozrejme nahradením vášho mena za yevgeniy.brikman):

    $ terraform import aws_iam_user.existing_user yevgeniy.brikman

    Terraform zavolá AWS API, aby našiel vášho používateľa IAM a vytvoril spojenie so stavovým súborom medzi ním a prostriedkom aws_iam_user.existing_user vo vašej konfigurácii Terraform. Odteraz, keď spustíte príkaz plánu, Terraform bude vedieť, že používateľ IAM už existuje a nebude sa ho pokúšať znova vytvoriť.

    Stojí za zmienku, že ak už máte veľa zdrojov, ktoré chcete importovať do Terraformu, manuálne písanie kódu a import každého po jednom môže byť problém. Takže stojí za to pozrieť sa na nástroj ako Terraforming (http://terraforming.dtan4.net/), ktorý dokáže automaticky importovať kód a stav z vášho účtu AWS.

    Refaktoring môže mať svoje úskalia

    Refaktorovanie je bežná prax v programovaní, kde meníte vnútornú štruktúru kódu, zatiaľ čo vonkajšie správanie zostáva nezmenené. Je to preto, aby bol kód prehľadnejší, prehľadnejší a ľahšie sa udržiava. Refaktoring je nevyhnutná technika, ktorá by sa mala používať pravidelne. Ale pokiaľ ide o Terraform alebo akýkoľvek iný nástroj IaC, musíte byť mimoriadne opatrní, čo myslíte „vonkajším správaním“ časti kódu, inak sa vyskytnú neočakávané problémy.

    Bežným typom refaktoringu je napríklad nahradenie názvov premenných alebo funkcií zrozumiteľnejšími. Mnohé IDE majú vstavanú podporu pre refactoring a môžu automaticky premenovať premenné a funkcie v rámci projektu. Vo všeobecných programovacích jazykoch ide o triviálny postup, nad ktorým by ste sa možno ani nezamýšľali, no v Terraforme musíte byť na to maximálne opatrní, inak môžete zaznamenať výpadky.

    Napríklad modul webserver-cluster má vstupnú premennú cluster_name:

    variable "cluster_name" {
       description = "The name to use for all the cluster resources"
       type          = string
    }

    Predstavte si, že ste tento modul začali používať na nasadenie mikroslužby s názvom foo. Neskôr budete chcieť svoju službu premenovať na bar. Táto zmena sa môže zdať triviálna, no v skutočnosti môže spôsobiť narušenie služieb.

    Faktom je, že modul webserver-cluster používa premennú cluster_name v mnohých zdrojoch vrátane parametra názvu dvoch bezpečnostný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 názvu na zdroji, Terraform vymaže starú verziu tohto zdroja a vytvorí na jej mieste novú. Ak je však týmto zdrojom ALB, medzi jeho odstránením a stiahnutím novej verzie nebudete mať mechanizmus na presmerovanie prevádzky na váš webový server. Podobne, ak sa odstráni bezpečnostná skupina, vaše servery začnú odmietať akúkoľvek sieťovú prevádzku, kým sa nevytvorí nová skupina.

    Ďalším typom refaktoringu, ktorý by vás mohol zaujímať, je zmena Terraform ID. Vezmime si ako príklad zdroj aws_security_group v module webserver-cluster:

    resource "aws_security_group" "instance" {
      # (...)
    }

    Identifikátor tohto zdroja sa nazýva inštancia. Predstavte si, že ste sa pri refaktoringu rozhodli zmeniť ho na zrozumiteľnejší (podľa vás) názov cluster_instance:

    resource "aws_security_group" "cluster_instance" {
       # (...)
    }

    Čo sa nakoniec stane? Správne: narušenie.

    Terraform spája každé ID prostriedku s ID poskytovateľa cloudu. Napríklad iam_user je priradený k ID užívateľa AWS IAM a aws_instance je priradený k ID servera AWS EC2. Ak zmeníte ID prostriedku (povedzme z inštancie na cluster_instance, ako je to v prípade aws_security_group), na Terraform to bude vyzerať, ako keby ste odstránili starý prostriedok a pridali nový. Ak použijete tieto zmeny, Terraform vymaže starú bezpečnostnú skupinu a vytvorí novú, zatiaľ čo vaše servery začnú odmietať akúkoľvek sieťovú prevádzku.

    Tu sú štyri kľúčové ponaučenia, ktoré by ste si z tejto diskusie mali odniesť.

    • Vždy používajte príkaz plán. Dokáže odhaliť všetky tieto háčiky. Pozorne si prezrite jeho výstup a venujte pozornosť situáciám, kedy Terraform plánuje vymazať zdroje, ktoré by s najväčšou pravdepodobnosťou nemali byť vymazané.
    • Pred odstránením vytvorte. Ak chcete nahradiť zdroj, pred odstránením originálu si dobre premyslite, či je potrebné vytvoriť náhradu. Ak je odpoveď áno, create_before_destroy vám môže pomôcť. Rovnaký výsledok je možné dosiahnuť manuálne vykonaním dvoch krokov: najprv pridajte nový prostriedok do konfigurácie a spustite príkaz Apply a potom odstráňte starý prostriedok z konfigurácie a znova použite príkaz Apply.
    • Zmena identifikátorov vyžaduje zmenu stavu. Ak chcete zmeniť ID priradené k prostriedku (napríklad premenovať aws_security_group z inštancie na cluster_instance) bez vymazania prostriedku a vytvorenia jeho novej verzie, musíte 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 spustiť príkaz terraform state mv, ktorý má nasledujúcu syntax:
      terraform state mv <ORIGINAL_REFERENCE> <NEW_REFERENCE>

      ORIGINAL_REFERENCE je výraz, ktorý odkazuje na zdroj v jeho aktuálnej forme, a NEW_REFERENCE je miesto, kam ho chcete presunúť. Napríklad pri premenovávaní skupiny aws_security_group z inštancie na cluster_instance musíte spustiť nasledujúci príkaz:

      $ terraform state mv 
         aws_security_group.instance 
         aws_security_group.cluster_instance

      Toto informuje Terraform, že stav, ktorý bol predtým priradený k aws_security_group.instance, by mal byť teraz priradený k aws_security_group.cluster_instance. Ak po premenovaní a spustení tohto príkazového plánu terraform neukazuje žiadne zmeny, urobili ste všetko správne.

    • Niektoré nastavenia nie je možné zmeniť. Parametre mnohých zdrojov sú nemenné. Ak sa ich pokúsite zmeniť, Terraform vymaže starý zdroj a vytvorí na jeho mieste nový. Na každej stránke zdrojov sa zvyčajne uvádza, čo sa stane, keď zmeníte konkrétne nastavenie, preto si nezabudnite pozrieť dokumentáciu. Vždy používajte príkaz plan a zvážte použitie stratégie create_before_destroy.

    Odložená konzistencia je konzistentná... s odkladom

    Niektoré rozhrania API poskytovateľov cloudu, ako napríklad AWS, sú asynchrónne a majú oneskorenú konzistenciu. Asynchrónnosť znamená, že rozhranie môže okamžite vrátiť odpoveď bez čakania na dokončenie požadovanej akcie. Oneskorená konzistencia znamená, že rozšírenie zmien v systéme môže chvíľu trvať; kým sa to deje, vaše odpovede môžu byť nekonzistentné a môžu závisieť od toho, ktorá replika zdroja údajov reaguje na vaše volania API.

    Predstavte si napríklad, že zavoláte API do AWS a požiadate ho o vytvorenie servera EC2. API vráti „úspešnú“ odpoveď (201 Created) takmer okamžite, bez čakania na vytvorenie samotného servera. Ak sa k nemu pokúsite pripojiť okamžite, takmer určite zlyhá, pretože v tom okamihu AWS stále inicializuje prostriedky alebo, prípadne, server sa ešte nezaviedol. Okrem toho, ak uskutočníte ďalší hovor, aby ste získali informácie o tomto serveri, môže sa zobraziť chyba (404 Nenájdené). Ide o to, že informácie o tomto serveri EC2 sa môžu stále šíriť v rámci AWS, kým budú dostupné všade, budete musieť počkať niekoľko sekúnd.

    Vždy, keď použijete asynchrónne API s pomalou konzistenciou, musíte svoju požiadavku pravidelne opakovať, kým sa akcia nedokončí a nebude sa šíriť systémom. Bohužiaľ, AWS SDK na to neposkytuje žiadne dobré nástroje a projekt Terraform trpel mnohými chybami, ako 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 exist

    Inými slovami, vytvoríte zdroj (napríklad podsieť) a potom sa o ňom pokúsite získať nejaké informácie (napríklad ID novovytvorenej podsiete) a Terraform ho nemôže nájsť. Väčšina týchto chýb (vrátane 6813) bola opravená, ale stále sa z času na čas objavujú, najmä keď Terraform pridáva podporu pre nový typ zdroja. Je to nepríjemné, ale vo väčšine prípadov nespôsobuje žiadnu škodu. Keď znova spustíte aplikáciu terraform, všetko by malo fungovať, pretože v tomto čase už budú informácie rozšírené po celom systéme.

    Tento úryvok je uvedený z knihy Jevgenija Brikmana "Terraform: infraštruktúra na úrovni kódu".

Zdroj: hab.com

Pridať komentár