Terraform pasti

Terraform pasti
Izpostavimo nekaj pasti, vključno s tistimi, ki so povezane z zankami, stavki if in tehnikami uvajanja, pa tudi bolj splošne težave, ki vplivajo na Terraform na splošno:

  • Parametra count in for_each imata omejitve;
  • Omejitve uvajanja brez izpadov;
  • celo dober načrt lahko propade;
  • refaktoriranje ima lahko svoje pasti;
  • Odložena doslednost je skladna z ... odložitvijo.

Parametra count in for_each imata omejitve.

Primeri v tem poglavju v zankah in pogojni logiki pogosto uporabljajo parameter count in izraz for_each. Čeprav sta učinkovita, imata dve pomembni omejitvi, ki se ju je treba zavedati.

  • V count ali for_each se ni mogoče sklicevati na nobeno spremenljivko izhodnih podatkov vira.
  • Funkcij count in for_each ni mogoče uporabiti v konfiguraciji modula.

V count ali for_each se ni mogoče sklicevati na nobeno izhodno spremenljivko vira.

Predstavljajte si, da morate namestiti več strežnikov EC2 in iz nekega razloga ne želite uporabljati ASG. Vaša koda bi lahko izgledala takole:

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

Poglejmo jih enega za drugim.

Ker je parameter count nastavljen na statično vrednost, bo ta koda delovala brez težav: ko zaženete ukaz apply, bo ustvaril tri strežnike EC2. Kaj pa, če bi želeli namestiti en strežnik v vsako območje razpoložljivosti (AZ) znotraj trenutne regije AWS? Koda bi lahko naložila seznam con iz vira podatkov aws_availability_zones in nato pregledala vsako od njih, v njej ustvarila strežnik EC2 z uporabo parametra count in dostopala do polja 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" {}

Tudi ta koda bo delovala brez težav, saj se parameter count lahko brez težav sklicuje na vire podatkov. Kaj pa se zgodi, če je število strežnikov, ki jih morate ustvariti, odvisno od izhoda nekega vira? Za demonstracijo tega je najlažji način uporaba vira random_integer, ki, kot lahko uganete že iz njegovega imena, vrne naključno celo število:

resource "random_integer" "num_instances" {
  min = 1
  max = 3
}

Ta koda generira naključno število med 1 in 3. Poglejmo, kaj se zgodi, če poskusimo uporabiti rezultat 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 zaženete načrt terraform na tej kodi, 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

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 zahteva, da se count in for_each ovrednotita ob času načrtovanja, preden se ustvarijo ali spremenijo kakršni koli viri. To pomeni, da se count in for_each lahko sklicujeta na literale, spremenljivke, vire podatkov in celo sezname virov (pod pogojem, da je njihovo dolžino mogoče določiti ob času načrtovanja), ne pa na izračunane izhodne spremenljivke virov.

count in for_each ni mogoče uporabiti v konfiguraciji modula

Na neki točki boste morda v skušnjavi, da konfiguraciji modula dodate parameter `count`:

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 za ustvarjanje treh kopij vira spletnega strežnika-gruče. Ali pa bi morda želeli vključitev modula narediti neobvezno na podlagi nekega logičnega pogoja tako, da njegov parameter `count` nastavite na 0. Ta koda se morda zdi smiselna, vendar boste pri zagonu programa Terraform Plan dobili naslednjo napako:

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.

Žal od različice Terraform 0.12.6 uporaba count ali for_each v viru modula ni več podprta. Glede na opombe ob izdaji Terraform 0.12 (http://bit.ly/3257bv4) HashiCorp načrtuje, da bo to funkcijo dodal v prihodnosti, zato je morda že na voljo, odvisno od tega, kdaj to berete. Če želite zagotovo preveriti, Preberite si seznam sprememb Terraforma tukaj.

Omejitve uvajanja brez izpadov

Uporaba bloka create_before_destroy v povezavi z ASG je odlična rešitev za uvajanje brez izpadov, razen ene omejitve: ne podpira pravil samodejnega skaliranja. Natančneje, velikost ASG se pri vsakem uvajanju ponastavi nazaj na min_size, kar je lahko težava, če ste s pravili samodejnega skaliranja povečali število delujočih strežnikov.

Na primer, modul webserver-cluster vsebuje par virov aws_autoscaling_schedule, ki povečata število strežnikov v gruči z dveh na deset ob 9:00. Če namestitve izvedete recimo ob 11:00, se bo novi ASG zagnal z le dvema strežnikoma namesto z desetimi in bo v tem stanju ostal do 9:00 naslednjega dne.

To omejitev je mogoče zaobiti na več načinov.

  • Spremenite parameter ponavljanja v aws_autoscaling_schedule iz 0 9 * * * ("zaženi ob 9") v nekaj takega kot 0-59 9-17 * * * ("zaženi vsako minuto od 9 do 5"). Če ima ASG že deset strežnikov, ponovni zagon tega pravila samodejnega skaliranja ne bo spremenil ničesar, kar si želimo. Če pa je ASG na novo nameščen, to pravilo zagotavlja, da bo dosegel deset strežnikov v največ eni minuti. To ni ravno eleganten pristop, veliki skoki z desetih na dva strežnika in nazaj pa lahko povzročijo težave tudi uporabnikom.
  • Ustvarite skript po meri, ki uporablja AWS API za določanje števila aktivnih strežnikov v ASG, ga pokličite z uporabo zunanjega vira podatkov (glejte »Zunanji vir podatkov« na strani 249) in nastavite parameter desired_capacity ASG na vrednost, ki jo vrne ta skript. To zagotavlja, da se vsak nov primerek ASG vedno začne z enako zmogljivostjo kot stara koda Terraform, zaradi česar je vzdrževanje težje.

Seveda bi imel Terraform idealno vgrajeno podporo za uvajanje brez izpadov, vendar ekipa HashiCorp od maja 2019 ni imela načrtov za dodajanje te funkcionalnosti (podrobnosti so tukaj).

Pravilen načrt se lahko neuspešno izvede

Včasih zagon ukaza plan ustvari popolnoma veljaven načrt uvajanja, vendar ukaz apply vrne napako. Na primer, poskusite dodati vir aws_iam_user z istim imenom, kot ste ga uporabili za uporabnika IAM, ki ste ga ustvarili prej v 2. poglavju:

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

Če zdaj zaženete ukaz plan, bo Terraform izpisal načrt uvajanja, ki je na prvi pogled videti razumen:

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 zaženete ukaz apply, boste dobili 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. To se lahko zgodi ne le uporabnikom IAM, ampak skoraj vsakemu viru. Nekdo je morda vir ustvaril ročno ali prek ukazne vrstice, vendar v obeh primerih navzkrižja ID-jev vodijo do konfliktov. Ta napaka ima veliko različic in pogosto preseneti novince v Terraformu.

Ključna točka je, da ukaz terraform plan upošteva samo vire, določene v datoteki stanja Terraform. Če so viri ustvarjeni na kakršen koli drug način (na primer ročno, s klikom miške v konzoli AWS), ne bodo vključeni v datoteko stanja in jih Terraform zato ne bo upošteval pri izvajanju ukaza plan. Posledično bo na videz pravilen načrt propadel.

Iz tega se lahko naučimo dveh lekcij.

  • Če ste že začeli delati s Terraformom, ne uporabljajte ničesar drugega. Če del vaše infrastrukture upravljate s Terraformom, ga ne morete več ročno spreminjati. V nasprotnem primeru ne tvegate le nenavadnih napak Terraforma, temveč tudi izničite številne prednosti IaC, saj koda ne bo več natančno predstavljala vaše infrastrukture.
  • Če že imate nekaj infrastrukture, uporabite ukaz import. Če začenjate uporabljati Terraform z obstoječo infrastrukturo, jo lahko dodate v datoteko stanja z ukazom terraform import. To Terraformu pove, katero infrastrukturo naj upravlja. Ukaz import sprejme dva argumenta. Prvi je naslov vira v vaših konfiguracijskih datotekah. Uporablja isto sintakso kot reference virov: _. (kot aws_iam_user.existing_user). Drugi argument je ID vira za uvoz. Na primer, ID vira aws_iam_user je uporabniško ime (npr. yevgeniy.brikman), ID vira aws_instance pa je ID strežnika EC2 (kot i-190e22e5). Kako uvoziti vir, je običajno določeno v dokumentaciji na dnu njegove strani.

    Tukaj je ukaz za uvoz, ki sinhronizira vir aws_iam_user, ki ste ga dodali v konfiguracijo Terraforma, skupaj z uporabnikom IAM v 2. poglavju (seveda namesto yevgeniy.brikman uporabite svoje ime):

    $ terraform import aws_iam_user.existing_user yevgeniy.brikman

    Terraform bo dostopal do AWS API-ja, da bi našel vašega uporabnika IAM in ustvaril povezavo datoteke stanja med njim in virom aws_iam_user.existing_user v vaši konfiguraciji Terraforma. Od te točke naprej bo Terraform ob zagonu ukaza plan vedel, da uporabnik IAM že obstaja, in ga ne bo poskušal znova ustvariti.

    Omeniti velja, da če že imate veliko virov, ki jih želite uvoziti v Terraform, je lahko ročno pisanje kode in uvoz vsakega posebej okorno. Zato je vredno razmisliti o orodju, kot je Terraforming (http://terraforming.dtan4.net/), ki lahko samodejno uvozi kodo in stanje iz vašega računa AWS.

    Refaktoriranje ima lahko svoje pasti

    Refaktoriranje Refaktoriranje je pogosta programerska praksa, pri kateri spremenite notranjo strukturo kode, medtem ko zunanje vedenje pustite nespremenjeno. To se naredi zato, da je koda bolj razumljiva, urejena in vzdrževalna. Refaktoriranje je nepogrešljiva tehnika, ki jo je treba redno uporabljati. Ko pa gre za Terraform ali katero koli drugo orodje za IaC, morate biti izjemno previdni pri tem, kaj mislite z "zunanjim vedenjem" dela kode, sicer se bodo pojavile nepričakovane težave.

    Na primer, pogosto refaktoriranje je spreminjanje imen spremenljivk ali funkcij v bolj razumljiva. Številna integrirana razvojna okolja (IDE) imajo vgrajeno podporo za refaktoriranje in lahko samodejno preimenujejo spremenljivke in funkcije v celotnem projektu. V splošnih programskih jezikih je to trivialen postopek, ki ga lahko spregledamo, v Terraformu pa je potrebna izjemna previdnost, da se izognemo izpadom.

    Na primer, modul webserver-cluster ima vhodno spremenljivko cluster_name:

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

    Predstavljajte si, da ste začeli uporabljati ta modul za uvajanje mikrostoritve z imenom foo. Kasneje želite svojo storitev preimenovati v bar. Ta sprememba se morda zdi nepomembna, vendar lahko v resnici povzroči izpade.

    Bistvo 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 imena vira, bo Terraform izbrisal staro različico tega vira in na njenem mestu ustvaril novo. Če pa je vir ALB, ne boste imeli mehanizma za preusmeritev prometa na spletni strežnik med njegovim izbrisom in nalaganjem nove različice. Podobno bodo strežniki, če je varnostna skupina izbrisana, začeli zavračati ves omrežni promet, dokler ni ustvarjena nova skupina.

    Druga prenova, ki bi vas morda zanimala, je sprememba identifikatorja Terraform. Vzemimo za primer vir aws_security_group v modulu webserver-cluster:

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

    Identifikator za ta vir se imenuje instanca. Predstavljajte si, da se med refaktoriranjem odločite, da ga spremenite v bolj opisno (po vašem mnenju) ime cluster_instance:

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

    Kaj se bo zgodilo na koncu? Tako je: prekinitev storitve.

    Terraform vsak ID vira poveže z ID-jem ponudnika storitev v oblaku. Na primer, iam_user je povezan z ID-jem uporabnika AWS IAM, aws_instance pa z ID-jem strežnika AWS EC2. Če spremenite ID vira (npr. iz instance v cluster_instance, kot pri aws_security_group), bo Terraform to videl, kot da ste izbrisali stari vir in dodali novega. Če uporabite te spremembe, bo Terraform izbrisal staro varnostno skupino in ustvaril novo, medtem ko bodo vaši strežniki začeli zavračati ves omrežni promet.

    Tukaj so štiri ključne lekcije, ki bi jih morali potegniti iz te razprave.

    • Vedno uporabite ukaz plan. Z njim lahko prepoznate vse te težave. Pozorno preglejte njegov izpis in bodite pozorni na situacije, ko Terraform načrtuje brisanje virov, ki jih verjetno ne bi smeli brisati.
    • Ustvari, preden uničiš. Če želiš vir zamenjati, dobro premisli, ali moraš pred brisanjem izvirnika ustvariti nadomestnega. Če je tako, ti lahko pomaga ukaz create_before_destroy. Enak rezultat lahko dosežeš ročno v dveh korakih: najprej dodaš nov vir v konfiguracijo in zaženeš ukaz apply, nato pa odstraniš stari vir iz konfiguracije in znova zaženeš ukaz apply.
    • Spreminjanje identifikatorjev zahteva spremembo stanja. Če želite spremeniti identifikator, povezan z virom (na primer preimenovanje aws_security_group iz instance v cluster_instance), ne da bi izbrisali vir in ustvarili novo različico, morate ustrezno posodobiti datoteko stanja Terraform. Tega nikoli ne počnite ročno – namesto tega uporabite ukaz terraform state. Pri preimenovanju identifikatorjev zaženite ukaz terraform state mv, ki ima naslednjo sintakso:
      terraform state mv <ORIGINAL_REFERENCE> <NEW_REFERENCE>

      ORIGINAL_REFERENCE je izraz, ki se sklicuje na vir v njegovem trenutnem stanju, NEW_REFERENCE pa je lokacija, kamor ga želite premakniti. Če želite na primer preimenovati aws_security_group iz instance v cluster_instance, zaženite naslednji ukaz:

      $ terraform state mv 
         aws_security_group.instance 
         aws_security_group.cluster_instance

      To pove Terraformu, da mora biti stanje, ki je bilo prej povezano z aws_security_group.instance, zdaj povezano z aws_security_group.cluster_instance. Če Terraformov načrt po preimenovanju in zagonu tega ukaza ne pokaže sprememb, ste vse naredili pravilno.

    • Nekaterih parametrov ni mogoče spremeniti. Številni parametri virov so nespremenljivi. Če jih poskusite spremeniti, bo Terraform izbrisal stari vir in na njegovem mestu ustvaril novega. Vsaka stran virov običajno določa, kaj se zgodi, ko se parameter spremeni, zato se prepričajte, da ste prebrali dokumentacijo. Vedno uporabite ukaz plan in razmislite o uporabi strategije create_before_destroy.

    Odložena skladnost je skladna z ... zamudo

    API-ji nekaterih ponudnikov storitev v oblaku, kot je AWS, so asinhroni in imajo zapoznelo konsistentnost. Asinhronost pomeni, da lahko vmesnik vrne odgovor takoj, ne da bi čakal na dokončanje zahtevanega dejanja. Zapoznela konsistentnost pomeni, da lahko traja nekaj časa, da se spremembe razširijo po sistemu; medtem ko se to zgodi, so vaši odgovori lahko nedosledni in odvisni od tega, katera replika vira podatkov se odzove na vaše klice API-ja.

    Na primer, predstavljajte si, da pokličete API na AWS in ga prosite za ustvarjanje strežnika EC2. API bo skoraj takoj vrnil odgovor »uspešno« (201 Ustvarjeno), ne da bi čakal, da se strežnik sam ustvari. Če se boste poskušali povezati z njim takoj, bo skoraj zagotovo spodletelo, saj AWS še vedno inicializira vire ali pa se strežnik še ni zagnal. Poleg tega lahko pri ponovnem klicu za pridobitev informacij o tem strežniku prejmete napako (404 Ni najdeno). To je zato, ker se informacije o tem strežniku EC2 morda še vedno širijo po AWS, zato bo trajalo nekaj sekund, da bodo na voljo drugje.

    Kadar koli uporabljate asinhroni API z leno konsistentnostjo, morate periodično znova poskusiti izvesti zahtevo, dokler se dejanje ne zaključi in razširi po celotnem sistemu. Žal AWS SDK za to ne ponuja nobenih dobrih orodij, projekt Terraform pa je že prej trpel zaradi številnih napak, kot 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

    Z drugimi besedami, ustvarite vir (na primer podomrežje) in nato poskusite pridobiti nekaj informacij o njem (kot je ID novo ustvarjenega podomrežja), Terraform pa ga ne najde. Večina teh napak (vključno s 6813) je bila odpravljena, vendar se občasno še vedno pojavljajo, zlasti ko Terraform doda podporo za novo vrsto vira. To je nadležno, vendar je v večini primerov neškodljivo. Ponovni zagon ukaza terraform apply bi moral delovati, saj se bodo informacije do takrat razširile po celotnem sistemu.

    Ta odlomek je iz knjige Evgenyja Brickmana Terraform: Infrastruktura na ravni kode.

Vir: www.habr.com

Kupite zanesljivo gostovanje za strani z DDoS zaščito, VPS VDS strežniki 🔥 Kupite zanesljivo spletno gostovanje z zaščito DDoS, VPS VDS strežniki | ProHoster