Terraformiset sudenkuopat

Terraformiset sudenkuopat
Korostetaan muutamia sudenkuoppia, mukaan lukien silmukoihin, if-lauseisiin ja käyttöönottotekniikoihin liittyvät sudenkuopat, sekä yleisempiä Terraformiin vaikuttavia ongelmia:

  • count- ja for_each-parametreilla on rajoituksia;
  • rajoittaa nollakäyttöönottoa seisokkeja;
  • jopa hyvä suunnitelma voi epäonnistua;
  • Refaktorointi voi sisältää sudenkuopat;
  • viivästetty johdonmukaisuus on yhdenmukainen... lykkäyksen kanssa.

Count- ja for_each-parametreilla on rajoituksia

Tämän luvun esimerkeissä käytetään laajasti count-parametria ja for_each-lauseketta silmukoissa ja ehdollisessa logiikassa. Ne toimivat hyvin, mutta niillä on kaksi tärkeää rajoitusta, jotka sinun on oltava tietoisia.

  • Count ja for_each eivät voi viitata mihinkään resurssien tulosmuuttujiin.
  • count ja for_each ei voida käyttää moduulin kokoonpanossa.

count ja for_each eivät voi viitata mihinkään resurssin lähtömuuttujiin

Kuvittele, että sinun on otettava käyttöön useita EC2-palvelimia, etkä jostain syystä halua käyttää ASG:tä. Koodisi voisi olla tällainen:

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

Katsotaanpa niitä yksitellen.

Koska count-parametri on asetettu staattiseen arvoon, tämä koodi toimii ilman ongelmia: kun suoritat App-komennon, se luo kolme EC2-palvelinta. Mutta entä jos haluat ottaa käyttöön yhden palvelimen jokaiselle käytettävyysvyöhykkeelle (AZ) nykyisellä AWS-alueellasi? Voit asettaa koodisi lataamaan luettelon vyöhykkeistä aws_availability_zones-tietolähteestä ja kiertämään sitten jokaisen vyöhykkeen läpi ja luomaan siihen EC2-palvelimen käyttämällä count-parametria ja taulukkoindeksin käyttöä:

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

Tämä koodi toimii myös hyvin, koska count-parametri voi viitata tietolähteisiin ilman ongelmia. Mutta mitä tapahtuu, jos luotavien palvelimien määrä riippuu jonkin resurssin tuotosta? Tämän osoittamiseksi helpoin tapa on käyttää random_integer-resurssia, joka, kuten nimestä voi päätellä, palauttaa satunnaisen kokonaisluvun:

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

Tämä koodi luo satunnaisluvun väliltä 1 ja 3. Katsotaanpa, mitä tapahtuu, jos yritämme käyttää tämän resurssin tulosta aws_instance-resurssin count-parametrissa:

resource "aws_instance" "example_3" {
   count             = random_integer.num_instances.result
   ami                = "ami-0c55b159cbfafe1f0"
   instance_type = "t2.micro"
}

Jos suoritat terraform-suunnitelman tällä koodilla, saat seuraavan virheilmoituksen:

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 edellyttää, että count ja for_each lasketaan suunnitteluvaiheessa, ennen kuin resursseja luodaan tai muokataan. Tämä tarkoittaa, että count ja for_each voivat viitata literaaleihin, muuttujiin, tietolähteisiin ja jopa resurssiluetteloihin (kunhan niiden pituus voidaan määrittää ajoitushetkellä), mutta ei laskettuihin resurssien tulosmuuttujiin.

count ja for_each ei voida käyttää moduulin kokoonpanossa

Jonakin päivänä saatat tuntea houkutusta lisätä laskentaparametrin moduulikokoonpanoon:

module "count_example" {
     source = "../../../../modules/services/webserver-cluster"

     count = 3

     cluster_name = "terraform-up-and-running-example"
     server_port = 8080
     instance_type = "t2.micro"
}

Tämä koodi yrittää käyttää moduulin sisäistä laskentaa luodakseen kolme kopiota verkkopalvelinklusterin resurssista. Tai voit halutessasi tehdä moduulin kytkemisen valinnaiseksi Boolen ehdoista riippuen asettamalla sen count-parametrin arvoksi 0. Tämä saattaa näyttää järkevältä koodilta, mutta saat tämän virheilmoituksen, kun suoritat terraform-suunnitelmaa:

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.

Valitettavasti Terraform 0.12.6:sta lähtien count- tai for_each -käyttöä ei tueta moduuliresurssissa. Terraform 0.12 -julkaisutietojen (http://bit.ly/3257bv4) mukaan HashiCorp aikoo lisätä tämän ominaisuuden tulevaisuudessa, joten riippuen siitä, milloin luet tätä kirjaa, se saattaa olla jo saatavilla. Saadaksesi varmuuden, lue Terraformin muutosloki täältä.

Nollakatkon käyttöönoton rajoitukset

Create_before_destroy-lohkon käyttäminen yhdessä ASG:n kanssa on loistava ratkaisu nolla-seisokkikäyttöisten käyttöönottojen luomiseen, lukuun ottamatta yhtä varoitusta: automaattisen skaalauksen sääntöjä ei tueta. Tai tarkemmin sanottuna, tämä palauttaa ASG-koon takaisin minimikokoon jokaisessa käyttöönotossa, mikä voi olla ongelma, jos käytät automaattista skaalausta lisäämään käynnissä olevien palvelimien määrää.

Esimerkiksi webserver-cluster-moduuli sisältää parin aws_autoscaling_schedule-resursseja, jotka klo 9 lisäävät klusterin palvelimien lukumäärän kahdesta kymmeneen. Jos otat käyttöön esimerkiksi klo 11, uusi ASG käynnistyy vain kahdella palvelimella kymmenen sijasta ja pysyy sellaisena seuraavan päivän klo 9 asti.

Tämä rajoitus voidaan kiertää useilla tavoilla.

  • Muuta aws_autoscaling_schedule:n toistuvuusparametri arvosta 0 9 * * * ("ajo klo 9") arvoon 0-59 9-17 * * * ("ajoa joka minuutti klo 9 - 5"). Jos ASG:llä on jo kymmenen palvelinta, tämän automaattisen skaalauksen säännön uudelleen suorittaminen ei muuta mitään, minkä haluamme. Mutta jos ASG on otettu käyttöön vasta äskettäin, tämä sääntö varmistaa, että sen palvelimien määrä nousee enintään minuutissa kymmeneen. Tämä ei ole täysin tyylikäs lähestymistapa, ja suuret hyppyt kymmenestä kahteen palvelimeen ja takaisin voivat myös aiheuttaa ongelmia käyttäjille.
  • Luo mukautettu komentosarja, joka määrittää ASG:n aktiivisten palvelimien määrän AWS API:n avulla, kutsu sitä käyttämällä ulkoista tietolähdettä (katso "Ulkoinen tietolähde" ​​sivulla 249) ja aseta ASG:n haluttu_kapasiteettiparametri arvoksi, jonka palauttaa käsikirjoitus. Tällä tavalla jokainen uusi ASG-ilmentymä toimii aina samalla kapasiteetilla kuin olemassa oleva Terraform-koodi ja vaikeuttaa sen ylläpitoa.

Tietenkin Terraformilla olisi ihanteellisesti sisäänrakennettu tuki nolla-seisokkikäyttöön, mutta toukokuussa 2019 HashiCorp-tiimillä ei ollut suunnitelmia lisätä tätä toimintoa (tiedot - täältä).

Oikea suunnitelma saattaa epäonnistua

Joskus plan-komento tuottaa täysin oikean käyttöönottosuunnitelman, mutta apply-komento palauttaa virheen. Kokeile esimerkiksi lisätä aws_iam_user-resurssi samalla nimellä, jolla käytit aiemmin luvussa 2 luomaasi IAM-käyttäjää:

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

Jos nyt suoritat plan-komennon, Terraform tulostaa näennäisen järkevän käyttöönottosuunnitelman:

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.

Jos suoritat App-komennon, saat seuraavan virheilmoituksen:

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

Ongelmana on tietysti se, että samanniminen IAM-käyttäjä on jo olemassa. Ja tämä ei voi tapahtua vain IAM-käyttäjille, vaan melkein kaikille resursseille. On mahdollista, että joku on luonut tämän resurssin manuaalisesti tai komentorivin avulla, mutta kummassakin tapauksessa tunnusten täsmääminen johtaa ristiriitaan. Tästä virheestä on monia muunnelmia, jotka usein yllättävät Terraformin uudet tulokkaat.

Tärkeintä on, että terraform plan -komento ottaa huomioon vain ne resurssit, jotka on määritetty Terraform-tilatiedostossa. Jos resurssit luodaan jollain muulla tavalla (esim. manuaalisesti AWS-konsolissa napsauttamalla), ne eivät päädy tilatiedostoon, joten Terraform ei ota niitä huomioon Plan-komentoa suorittaessaan. Tämän seurauksena ensi silmäyksellä oikealta näyttävä suunnitelma osoittautuu epäonnistuneeksi.

Tästä on opittava kaksi opetusta.

  • Jos olet jo aloittanut työskentelyn Terraformin kanssa, älä käytä mitään muuta. Jos osaa infrastruktuuristasi hallitaan Terraformilla, et voi enää muokata sitä manuaalisesti. Muuten riskinäsi ei ole vain outoja Terraform-virheitä, vaan myös monet IaC:n edut mitätöidään, koska koodi ei enää ole tarkka esitys infrastruktuuristasi.
  • Jos sinulla on jo infrastruktuuri, käytä tuontikomentoa. Jos olet aloittamassa Terraformin käyttöä olemassa olevan infrastruktuurin kanssa, voit lisätä sen tilatiedostoon terraformin tuontikomennolla. Näin Terraform tietää, mitä infrastruktuuria on hallittava. Import-komento ottaa kaksi argumenttia. Ensimmäinen on resurssiosoite asetustiedostoissasi. Syntaksi tässä on sama kuin resurssilinkeissä: _. (kuten aws_iam_user.existing_user). Toinen argumentti on tuotavan resurssin tunnus. Oletetaan, että resurssitunnus aws_iam_user on käyttäjänimi (esimerkiksi yevgeniy.brikman) ja resurssitunnus aws_instance on EC2-palvelimen tunnus (kuten i-190e22e5). Resurssin tuonti on yleensä mainittu sen sivun alalaidassa olevassa dokumentaatiossa.

    Alla on tuontikomento, joka synkronoi aws_iam_user-resurssin, jonka lisäsit Terraform-kokoonpanoon IAM-käyttäjän kanssa luvussa 2 (korvaa tietysti nimesi yevgeniy.brikmanilla):

    $ terraform import aws_iam_user.existing_user yevgeniy.brikman

    Terraform kutsuu AWS-sovellusliittymää löytääkseen IAM-käyttäjäsi ja luodakseen tilatiedostoyhteyden sen ja Terraform-kokoonpanosi aws_iam_user.existing_user-resurssin välille. Tästä eteenpäin, kun suoritat plan-komennon, Terraform tietää, että IAM-käyttäjä on jo olemassa, eikä yritä luoda sitä uudelleen.

    On syytä huomata, että jos sinulla on jo paljon resursseja, jotka haluat tuoda Terraformiin, koodin manuaalinen kirjoittaminen ja kunkin tuominen yksitellen voi olla vaivalloista. Joten kannattaa etsiä työkalua, kuten Terraforming (http://terraforming.dtan4.net/), joka voi tuoda koodin ja tilan automaattisesti AWS-tililtäsi.

    Refaktoroinnilla voi olla ansoja

    Refaktorointi on yleinen ohjelmoinnin käytäntö, jossa koodin sisäistä rakennetta muutetaan samalla kun ulkoinen käyttäytyminen jätetään ennalleen. Tämän tarkoituksena on tehdä koodista selkeämpi, siistimpi ja helpompi ylläpitää. Refactoring on välttämätön tekniikka, jota tulee käyttää säännöllisesti. Mutta mitä tulee Terraformiin tai mihin tahansa muuhun IaC-työkaluun, sinun on oltava erittäin varovainen sen suhteen, mitä tarkoitat koodinpalan "ulkoisella käytöksellä", muuten ilmaantuu odottamattomia ongelmia.

    Esimerkiksi yleinen refaktorointityyppi on muuttujien tai funktioiden nimien korvaaminen ymmärrettävämmillä. Monissa IDE:issä on sisäänrakennettu tuki uudelleenjärjestelylle, ja ne voivat automaattisesti nimetä muuttujat ja toiminnot uudelleen koko projektin ajan. Yleiskäyttöisissä ohjelmointikielissä tämä on triviaali toimenpide, jota et ehkä ajattele, mutta Terraformissa sinun on oltava erittäin varovainen tämän kanssa, muuten saatat kohdata katkoksia.

    Esimerkiksi webserver-cluster-moduulilla on syöttömuuttuja klusterin_nimi:

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

    Kuvittele, että aloitit tämän moduulin käytön foo-nimisen mikropalvelun käyttöönotossa. Myöhemmin haluat nimetä palvelusi uudelleen baariksi. Tämä muutos saattaa vaikuttaa triviaalilta, mutta todellisuudessa se voi aiheuttaa palveluhäiriöitä.

    Tosiasia on, että webserver-cluster-moduuli käyttää muuttujaa cluster_name useissa resursseissa, mukaan lukien kahden suojausryhmän nimiparametri ja 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]
    }

    Jos muutat resurssin nimiparametria, Terraform poistaa resurssin vanhan version ja luo uuden tilalle. Mutta jos kyseinen resurssi on ALB, sen poistamisen ja uuden version lataamisen välillä sinulla ei ole mekanismia ohjata liikennettä verkkopalvelimellesi. Samoin jos suojausryhmä poistetaan, palvelimesi alkavat hylätä verkkoliikennettä, kunnes uusi ryhmä on luotu.

    Toinen uudelleenmuodostustyyppi, josta saatat olla kiinnostunut, on Terraform-tunnuksen muuttaminen. Otetaan esimerkkinä aws_security_group-resurssi webserver-cluster-moduulissa:

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

    Tämän resurssin tunnistetta kutsutaan instanssiksi. Kuvittele, että refaktoroinnin aikana päätit muuttaa sen ymmärrettävämpään (mielestäsi) nimeen cluster_instance:

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

    Mitä lopulta tapahtuu? Aivan oikein: häiriö.

    Terraform liittää jokaisen resurssitunnuksen pilvipalveluntarjoajan tunnukseen. Esimerkiksi iam_user liittyy AWS IAM -käyttäjätunnukseen ja aws_instance on liitetty AWS EC2 -palvelimen tunnukseen. Jos muutat resurssin tunnuksen (esimerkiksi ilmentymästä cluster_instance-tyyppiin, kuten aws_security_group), Terraformiksi, se näyttää siltä kuin olisit poistanut vanhan resurssin ja lisännyt uuden. Jos otat nämä muutokset käyttöön, Terraform poistaa vanhan suojausryhmän ja luo uuden, kun taas palvelimesi alkavat hylätä verkkoliikennettä.

    Tässä on neljä keskeistä opetusta, jotka sinun pitäisi ottaa pois tästä keskustelusta.

    • Käytä aina plan-komentoa. Se voi paljastaa kaikki nämä vinot. Tarkista sen tuotos huolellisesti ja kiinnitä huomiota tilanteisiin, joissa Terraform aikoo poistaa resursseja, joita ei todennäköisesti pitäisi poistaa.
    • Luo ennen poistamista. Jos haluat korvata resurssin, harkitse huolellisesti, tarvitseeko sinun luoda korvaava, ennen kuin poistat alkuperäisen. Jos vastaus on kyllä, create_before_destroy voi auttaa. Sama tulos voidaan saavuttaa manuaalisesti suorittamalla kaksi vaihetta: lisää ensin uusi resurssi kokoonpanoon ja suorita App-komento ja poista sitten vanha resurssi määrityksestä ja käytä App-komentoa uudelleen.
    • Tunnisteiden muuttaminen vaatii tilan muuttamisen. Jos haluat muuttaa resurssiin liittyvää tunnusta (esimerkiksi nimetä aws_security_group uudelleen ilmentymästä klusteriinstanssiksi) poistamatta resurssia ja luomatta siitä uutta versiota, sinun on päivitettävä Terraform-tilatiedosto vastaavasti. Älä koskaan tee tätä manuaalisesti - käytä sen sijaan terraform state -komentoa. Kun nimeät tunnisteita uudelleen, sinun tulee suorittaa terraform state mv -komento, jolla on seuraava syntaksi:
      terraform state mv <ORIGINAL_REFERENCE> <NEW_REFERENCE>

      ORIGINAL_REFERENCE on lauseke, joka viittaa resurssiin sen nykyisessä muodossa, ja NEW_REFERENCE on paikka, johon haluat siirtää sen. Kun esimerkiksi nimeät aws_security_group-ryhmän uudelleen ilmentymästä klusteriinstanssiksi, sinun on suoritettava seuraava komento:

      $ terraform state mv 
         aws_security_group.instance 
         aws_security_group.cluster_instance

      Tämä kertoo Terraformille, että tila, joka oli aiemmin liitetty tiedostoon aws_security_group.instance, tulisi nyt liittää osoitteeseen aws_security_group.cluster_instance. Jos tämän komennon uudelleennimeämisen ja suorittamisen jälkeen terraformisuunnitelma ei näytä muutoksia, teit kaiken oikein.

    • Joitakin asetuksia ei voi muuttaa. Monien resurssien parametrit ovat muuttumattomia. Jos yrität muuttaa niitä, Terraform poistaa vanhan resurssin ja luo uuden tilalle. Jokainen resurssisivu kertoo yleensä, mitä tapahtuu, kun muutat tiettyä asetusta, joten muista tarkistaa dokumentaatio. Käytä aina plan-komentoa ja harkitse create_before_destroy-strategian käyttöä.

    Lykätty johdonmukaisuus on yhdenmukainen... lykkäyksen kanssa

    Joidenkin pilvipalveluntarjoajien sovellusliittymät, kuten AWS, ovat asynkronisia ja niillä on viivästynyt johdonmukaisuus. Asynkronisuus tarkoittaa, että käyttöliittymä voi välittömästi palauttaa vastauksen odottamatta pyydetyn toiminnon valmistumista. Viivästynyt johdonmukaisuus tarkoittaa, että muutosten leviäminen koko järjestelmässä saattaa kestää jonkin aikaa. kun tämä tapahtuu, vastauksesi voivat olla epäjohdonmukaisia ​​ja riippuvia siitä, minkä tietolähteen replika vastaa API-kutsuihisi.

    Kuvittele esimerkiksi, että soitat API-kutsun AWS:lle ja pyydät sitä luomaan EC2-palvelimen. API palauttaa "onnistuneen" vastauksen (201 Luotu) lähes välittömästi, odottamatta itse palvelimen luomista. Jos yrität muodostaa yhteyden siihen heti, se epäonnistuu melkein varmasti, koska siinä vaiheessa AWS vielä alustaa resursseja tai vaihtoehtoisesti palvelin ei ole vielä käynnistynyt. Lisäksi, jos soitat toisen puhelun saadaksesi tietoja tästä palvelimesta, saatat saada virheilmoituksen (404 Ei löydy). Asia on siinä, että tätä EC2-palvelinta koskevat tiedot saattavat edelleen levitä AWS:ssä ennen kuin ne tulevat saataville kaikkialla, sinun on odotettava muutama sekunti.

    Aina kun käytät asynkronista sovellusliittymää, jonka johdonmukaisuus on laiska, sinun on ajoittain yritettävä pyyntöäsi uudelleen, kunnes toiminto on valmis ja se etenee järjestelmän läpi. Valitettavasti AWS SDK ei tarjoa tähän hyviä työkaluja, ja Terraform-projekti kärsi aiemmin monista virheistä, kuten 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

    Toisin sanoen luot resurssin (kuten aliverkon) ja yrität sitten saada siitä tietoja (kuten juuri luodun aliverkon tunnuksen), jolloin Terraform ei löydä sitä. Suurin osa näistä virheistä (mukaan lukien 6813) on korjattu, mutta niitä tulee silti aika ajoin, varsinkin kun Terraform lisää tuen uudelle resurssityypille. Tämä on ärsyttävää, mutta useimmissa tapauksissa ei aiheuta haittaa. Kun suoritat terraform application -sovelluksen uudelleen, kaiken pitäisi toimia, koska tähän mennessä tieto on jo levinnyt koko järjestelmään.

    Tämä ote on esitetty Evgeniy Brikmanin kirjasta "Terraform: infrastruktuuri kooditasolla".

Lähde: will.com

Lisää kommentti