Terraform slaggate

Terraform slaggate
Kom ons lig 'n paar slaggate uit, insluitend dié wat verband hou met lusse, if-stellings en ontplooiingstegnieke, sowel as meer algemene kwessies wat Terraform in die algemeen raak:

  • die telling en vir_elke parameters het beperkings;
  • beperk nul stilstand-ontplooiings;
  • selfs 'n goeie plan kan misluk;
  • herfaktorering kan sy slaggate hê;
  • uitgestelde samehang is in ooreenstemming... met uitstel.

Die telling en vir_elke parameters het beperkings

Die voorbeelde in hierdie hoofstuk maak uitgebreide gebruik van die telparameter en die vir_elke uitdrukking in lusse en voorwaardelike logika. Hulle presteer goed, maar hulle het twee belangrike beperkings waarvan jy bewus moet wees.

  • Tel en vir_elk kan nie na enige hulpbronafvoerveranderlikes verwys nie.
  • count en for_each kan nie in modulekonfigurasie gebruik word nie.

count en for_each kan nie na enige hulpbronafvoerveranderlikes verwys nie

Stel jou voor dat jy verskeie EC2-bedieners moet ontplooi en om een ​​of ander rede wil jy nie ASG gebruik nie. Jou kode kan soos volg wees:

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

Kom ons kyk een vir een na hulle.

Aangesien die telparameter op 'n statiese waarde gestel is, sal hierdie kode sonder probleme werk: wanneer jy die toepassing-opdrag uitvoer, sal dit drie EC2-bedieners skep. Maar wat as jy een bediener in elke Beskikbaarheidsone (AZ) binne jou huidige AWS-streek wil ontplooi? U kan u kode 'n lys sones van die aws_availability_zones-databron laat laai en dan deur elkeen loop en 'n EC2-bediener daarin skep deur die telparameter en skikkingsindekstoegang te gebruik:

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

Hierdie kode sal ook goed werk, aangesien die telparameter sonder enige probleme na databronne kan verwys. Maar wat gebeur as die aantal bedieners wat u moet skep, afhang van die uitset van een of ander hulpbron? Om dit te demonstreer, is die maklikste manier om die random_integer hulpbron te gebruik, wat, soos die naam aandui, 'n ewekansige heelgetal gee:

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

Hierdie kode genereer 'n ewekansige getal tussen 1 en 3. Kom ons kyk wat gebeur as ons probeer om die uitset van hierdie hulpbron in die telparameter van die aws_instance hulpbron te gebruik:

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

As jy terraform plan op hierdie kode gebruik, sal jy die volgende fout kry:

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 vereis dat telling en vir_elk tydens die beplanningsfase bereken word, voordat enige hulpbronne geskep of gewysig word. Dit beteken dat tel en vir_elke kan verwys na letterlike, veranderlikes, databronne en selfs hulpbronlyste (solank hul lengte bepaal kan word tydens skeduleringstyd), maar nie na berekende hulpbronuitsetveranderlikes nie.

count en for_each kan nie in modulekonfigurasie gebruik word nie

Eendag sal jy dalk in die versoeking kom om 'n telparameter by jou modulekonfigurasie by te voeg:

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

     count = 3

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

Hierdie kode probeer om telling binne 'n module te gebruik om drie kopieë van die webbediener-kluster-hulpbron te skep. Of jy wil dalk die koppeling van 'n module opsioneel maak op grond van een of ander Boole-toestand deur sy telparameter op 0 te stel. Dit kan dalk na redelike kode lyk, maar jy sal hierdie fout kry wanneer jy terraform plan uitvoer:

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.

Ongelukkig, vanaf Terraform 0.12.6, word die gebruik van tel of vir_elk in 'n modulehulpbron nie ondersteun nie. Volgens die Terraform 0.12-vrystellingsnotas (http://bit.ly/3257bv4), beplan HashiCorp om hierdie vermoë in die toekoms by te voeg, so afhangend van wanneer jy hierdie boek lees, is dit dalk reeds beskikbaar. Om vir seker uit te vind, lees die Terraform changelog hier.

Beperkings van nul stilstand-ontplooiings

Die gebruik van die create_before_destroy-blok in kombinasie met ASG is 'n uitstekende oplossing vir die skep van nul-stilstand-ontplooiings, behalwe vir een waarskuwing: outoskaalreëls word nie ondersteun nie. Of om meer presies te wees, dit stel die ASG-grootte terug na min_size op elke ontplooiing, wat 'n probleem kan wees as jy outoskaalreëls gebruik om die aantal bedieners wat loop, te verhoog.

Byvoorbeeld, die webbediener-kluster-module bevat 'n paar aws_autoscaling_schedule-hulpbronne, wat om 9:11 die aantal bedieners in die groep van twee tot tien verhoog. As jy byvoorbeeld om 9:XNUMX ontplooi, sal die nuwe ASG begin met net twee bedieners eerder as tien en so bly tot XNUMX:XNUMX die volgende dag.

Hierdie beperking kan op verskeie maniere omseil word.

  • Verander die herhalingsparameter in aws_autoscaling_schedule van 0 9 * * * ("hardloop om 9 vm") na iets soos 0-59 9-17 * * * ("hardloop elke minuut van 9 vm tot 5 nm"). As ASG reeds tien bedieners het, sal hierdie outoskaalreël weer niks verander nie, en dit is wat ons wil hê. Maar as die ASG eers onlangs ontplooi is, sal hierdie reël verseker dat die aantal bedieners binne 'n maksimum van 'n minuut tien sal bereik. Dit is nie 'n heeltemal elegante benadering nie, en groot spronge van tien na twee bedieners en terug kan ook probleme vir gebruikers veroorsaak.
  • Skep 'n gepasmaakte skrip wat die AWS API gebruik om die aantal aktiewe bedieners in die ASG te bepaal, noem dit deur 'n eksterne databron te gebruik (sien "Eksterne databron" op bladsy 249), en stel die ASG se gewenste_kapasiteit-parameter op die waarde wat teruggegee word deur die draaiboek. Op hierdie manier sal elke nuwe ASG-instansie altyd op dieselfde kapasiteit as die bestaande Terraform-kode loop en maak dit moeiliker om te onderhou.

Natuurlik sou Terraform ideaal gesproke ingeboude ondersteuning hê vir nul-stilstand-ontplooiings, maar vanaf Mei 2019 het die HashiCorp-span geen planne gehad om hierdie funksionaliteit by te voeg nie (besonderhede - hier).

Die korrekte plan kan onsuksesvol geïmplementeer word

Soms produseer die plan-opdrag 'n perfek korrekte ontplooiingsplan, maar die toepassing-opdrag gee 'n fout terug. Probeer byvoorbeeld om die aws_iam_user-hulpbron by te voeg met dieselfde naam wat jy gebruik het vir die IAM-gebruiker wat jy vroeër in Hoofstuk 2 geskep het:

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

Nou, as u die plan-opdrag uitvoer, sal Terraform 'n skynbaar redelike ontplooiingsplan uitvoer:

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.

As jy die toepassing opdrag uitvoer, sal jy die volgende fout kry:

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

Die probleem is natuurlik dat 'n IAM-gebruiker met daardie naam reeds bestaan. En dit kan nie net met IAM-gebruikers gebeur nie, maar met byna enige hulpbron. Dit is moontlik dat iemand hierdie hulpbron met die hand geskep het of die opdragreël gebruik, maar hoe dit ook al sy, bypassende ID's lei tot konflikte. Daar is baie variasies van hierdie fout wat nuwelinge by Terraform dikwels verras.

Die sleutelpunt is dat die terraform-plan-opdrag slegs daardie hulpbronne in ag neem wat in die Terraform-staatlêer gespesifiseer word. As hulpbronne op 'n ander manier geskep word (byvoorbeeld, met die hand deur in die AWS-konsole te klik), sal dit nie in die staatlêer beland nie en daarom sal Terraform dit nie in ag neem wanneer die plan-opdrag uitgevoer word nie. As gevolg hiervan sal 'n plan wat met die eerste oogopslag korrek lyk, onsuksesvol blyk te wees.

Daar is twee lesse hieruit te leer.

  • As jy reeds met Terraform begin werk het, moet jy niks anders gebruik nie. As 'n deel van jou infrastruktuur met Terraform bestuur word, kan jy dit nie meer met die hand wysig nie. Andersins waag jy nie net vreemde Terraform-foute nie, maar jy ontken ook baie van die voordele van IaC, aangesien die kode nie meer 'n akkurate voorstelling van jou infrastruktuur sal wees nie.
  • As jy reeds infrastruktuur het, gebruik die invoeropdrag. As jy Terraform met bestaande infrastruktuur begin gebruik, kan jy dit by die staatlêer voeg deur die terraform-invoeropdrag te gebruik. Op hierdie manier sal Terraform weet watter infrastruktuur bestuur moet word. Die invoeropdrag neem twee argumente. Die eerste is die hulpbronadres in u konfigurasielêers. Die sintaksis hier is dieselfde as vir hulpbronskakels: _. (soos aws_iam_user.existing_user). Die tweede argument is die ID van die hulpbron wat ingevoer moet word. Kom ons sê die hulpbron-ID aws_iam_user is die gebruikernaam (byvoorbeeld, yevgeniy.brikman), en die hulpbron-ID aws_instance is die EC2-bediener-ID (soos i-190e22e5). Hoe om 'n hulpbron in te voer, word gewoonlik in die dokumentasie onderaan sy bladsy aangedui.

    Hieronder is 'n invoeropdrag wat die aws_iam_user-hulpbron sinchroniseer wat jy by jou Terraform-konfigurasie gevoeg het saam met die IAM-gebruiker in Hoofstuk 2 (natuurlik deur jou naam vir yevgeniy.brikman te vervang):

    $ terraform import aws_iam_user.existing_user yevgeniy.brikman

    Terraform sal die AWS API roep om jou IAM-gebruiker te vind en 'n staatlêerassosiasie tussen dit en die aws_iam_user.existing_user-hulpbron in jou Terraform-konfigurasie te skep. Van nou af, wanneer jy die plan-opdrag uitvoer, sal Terraform weet dat die IAM-gebruiker reeds bestaan ​​en sal dit nie weer probeer skep nie.

    Dit is opmerklik dat as u reeds baie hulpbronne het wat u in Terraform wil invoer, dit moeilik kan wees om die kode handmatig te skryf en elkeen op 'n slag in te voer. Dit is dus die moeite werd om na 'n instrument soos Terraforming (http://terraforming.dtan4.net/) te kyk, wat kode en staat outomaties vanaf jou AWS-rekening kan invoer.

    Refaktorering kan sy slaggate hê

    Refaktorering is 'n algemene praktyk in programmering waar jy die interne struktuur van die kode verander terwyl jy die eksterne gedrag onveranderd laat. Dit is om die kode duideliker, netjieser en makliker te maak om te onderhou. Refaktorering is 'n onontbeerlike tegniek wat gereeld gebruik moet word. Maar wanneer dit by Terraform of enige ander IaC-instrument kom, moet jy uiters versigtig wees oor wat jy bedoel met die "eksterne gedrag" van 'n stukkie kode, anders sal onverwagte probleme opduik.

    Byvoorbeeld, 'n algemene tipe herfaktorering is om die name van veranderlikes of funksies te vervang met meer verstaanbare. Baie IDE's het ingeboude ondersteuning vir herfaktorering en kan veranderlikes en funksies outomaties regdeur die projek hernoem. In algemene programmeertale is dit 'n onbenullige prosedure waaraan jy dalk nie dink nie, maar in Terraform moet jy uiters versigtig wees hiermee, anders kan jy onderbrekings ervaar.

    Byvoorbeeld, die webbediener-kluster-module het 'n invoerveranderlike cluster_name:

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

    Stel jou voor dat jy hierdie module begin gebruik het om 'n mikrodiens genaamd foo te ontplooi. Later wil jy jou diens na bar hernoem. Hierdie verandering mag dalk triviaal lyk, maar in werklikheid kan dit diensonderbrekings veroorsaak.

    Die feit is dat die webbediener-kluster-module die cluster_name-veranderlike in 'n aantal hulpbronne gebruik, insluitend die naamparameter van twee sekuriteitsgroepe en die 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]
    }

    As jy die naamparameter op 'n hulpbron verander, sal Terraform die ou weergawe van daardie hulpbron uitvee en 'n nuwe een in die plek daarvan skep. Maar as daardie hulpbron 'n ALB is, tussen die verwydering daarvan en die aflaai van 'n nuwe weergawe, sal jy nie 'n meganisme hê om verkeer na jou webbediener te herlei nie. Net so, as 'n sekuriteitsgroep uitgevee word, sal jou bedieners begin om enige netwerkverkeer te verwerp totdat 'n nuwe groep geskep word.

    Nog 'n tipe herfaktorering waarin jy dalk belangstel, is om die Terraform ID te verander. Kom ons neem die aws_security_group-hulpbron in die webbediener-kluster-module as 'n voorbeeld:

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

    Die identifiseerder van hierdie hulpbron word instansie genoem. Stel jou voor dat jy tydens herfaktorering besluit het om dit te verander na 'n meer verstaanbare (na jou mening) naam cluster_instance:

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

    Wat sal op die ou end gebeur? Dis reg: 'n ontwrigting.

    Terraform assosieer elke hulpbron-ID met die wolkverskaffer-ID. Byvoorbeeld, iam_user word geassosieer met die AWS IAM-gebruiker-ID, en aws_instance word geassosieer met die AWS EC2-bediener-ID. As jy die hulpbron-ID (sê van instansie na cluster_instance, soos die geval is met aws_security_group) verander na Terraform sal dit voorkom asof jy die ou hulpbron uitgevee het en 'n nuwe een bygevoeg het. As jy hierdie veranderinge toepas, sal Terraform die ou sekuriteitsgroep uitvee en 'n nuwe een skep, terwyl jou bedieners enige netwerkverkeer begin verwerp.

    Hier is vier sleutellesse wat jy uit hierdie bespreking moet neem.

    • Gebruik altyd die plan-opdrag. Dit kan al hierdie haakplekke openbaar. Hersien sy uitset noukeurig en let op situasies waar Terraform beplan om hulpbronne uit te vee wat heel waarskynlik nie uitgevee moet word nie.
    • Skep voor jy uitvee. As jy 'n hulpbron wil vervang, dink mooi oor of jy 'n plaasvervanger moet skep voordat jy die oorspronklike uitvee. As die antwoord ja is, kan create_before_destroy help. Dieselfde resultaat kan met die hand bereik word deur twee stappe uit te voer: voeg eers 'n nuwe hulpbron by die konfigurasie en voer die toepassing-opdrag uit, en verwyder dan die ou hulpbron uit die opstelling en gebruik die toepassing-opdrag weer.
    • Die verandering van identifiseerders vereis die verandering van toestand. As jy die ID wat met 'n hulpbron geassosieer word, wil verander (byvoorbeeld, hernoem aws_security_group van instansie na cluster_instance) sonder om die hulpbron uit te vee en 'n nuwe weergawe daarvan te skep, moet jy die Terraform-statuslêer dienooreenkomstig bywerk. Moet dit nooit met die hand doen nie - gebruik eerder die terraform state-opdrag. Wanneer u identifiseerders hernoem, moet u die terraform state mv-opdrag uitvoer, wat die volgende sintaksis het:
      terraform state mv <ORIGINAL_REFERENCE> <NEW_REFERENCE>

      ORIGINAL_REFERENCE is 'n uitdrukking wat verwys na die hulpbron in sy huidige vorm, en NEW_REFERENCE is waarheen jy dit wil skuif. Byvoorbeeld, wanneer u die aws_security_group-groep hernoem van instansie na cluster_instance, moet u die volgende opdrag uitvoer:

      $ terraform state mv 
         aws_security_group.instance 
         aws_security_group.cluster_instance

      Dit sê vir Terraform dat die toestand wat voorheen met aws_security_group.instance geassosieer is, nou met aws_security_group.cluster_instance geassosieer moet word. As na die hernoeming en uitvoer van hierdie opdrag terraform plan geen veranderinge toon nie, dan het jy alles korrek gedoen.

    • Sommige instellings kan nie verander word nie. Die parameters van baie hulpbronne is onveranderlik. As jy probeer om hulle te verander, sal Terraform die ou hulpbron uitvee en 'n nuwe een in die plek daarvan skep. Elke hulpbronbladsy sal gewoonlik aandui wat gebeur wanneer jy 'n spesifieke instelling verander, so maak seker dat jy die dokumentasie nagaan. Gebruik altyd die plan-opdrag en oorweeg dit om die create_before_destroy-strategie te gebruik.

    Uitgestelde konsekwentheid is in ooreenstemming ... met uitstel

    Sommige wolkverskaffers se API's, soos AWS, is asynchronies en het vertraagde konsekwentheid. Asinchronie beteken dat die koppelvlak onmiddellik 'n antwoord kan terugstuur sonder om te wag vir die gevraagde handeling om te voltooi. Vertraagde konsekwentheid beteken dat veranderings tyd kan neem om regdeur die stelsel te versprei; terwyl dit gebeur, kan jou antwoorde inkonsekwent wees en afhang van watter databron replika op jou API-oproepe reageer.

    Stel jou byvoorbeeld voor dat jy 'n API-oproep na AWS maak en dit vra om 'n EC2-bediener te skep. Die API sal amper onmiddellik 'n "suksesvolle" antwoord (201 Geskep) gee, sonder om te wag dat die bediener self geskep word. As jy probeer om dadelik daaraan te koppel, sal dit byna seker misluk, want op daardie stadium is AWS nog besig om hulpbronne te inisialiseer of, alternatiewelik, die bediener het nog nie gelaai nie. Verder, as jy nog 'n oproep maak om inligting oor hierdie bediener te kry, kan jy 'n fout (404 Nie gevind nie) ontvang. Die ding is dat die inligting oor hierdie EC2-bediener dalk nog deur AWS versprei kan word voordat dit oral beskikbaar word, jy sal 'n paar sekondes moet wag.

    Wanneer jy ook al 'n asynchrone API met lui konsekwentheid gebruik, moet jy jou versoek periodiek herprobeer totdat die aksie voltooi is en deur die stelsel versprei. Ongelukkig bied die AWS SDK geen goeie gereedskap hiervoor nie, en die Terraform-projek het vroeër aan baie foute soos 6813 gely (https://github.com/hashicorp/terraform/issues/6813):

    $ terraform apply
    aws_subnet.private-persistence.2: InvalidSubnetID.NotFound:
    The subnet ID 'subnet-xxxxxxx' does not exist

    Met ander woorde, jy skep 'n hulpbron (soos 'n subnet) en probeer dan 'n bietjie inligting daaroor kry (soos die ID van die nuutgeskepte subnet), en Terraform kan dit nie vind nie. Die meeste van hierdie foute (insluitend 6813) is reggestel, maar hulle duik steeds van tyd tot tyd op, veral wanneer Terraform ondersteuning vir 'n nuwe hulpbrontipe byvoeg. Dit is irriterend, maar veroorsaak in die meeste gevalle geen skade nie. Wanneer jy weer terraform laat loop, moet alles werk, want teen hierdie tyd sal die inligting reeds deur die stelsel versprei het.

    Hierdie uittreksel word aangebied uit die boek deur Evgeniy Brikman "Terraform: infrastruktuur op kodevlak".

Bron: will.com

Voeg 'n opmerking