Grackat Terraform

Grackat Terraform
Le të theksojmë disa gracka, duke përfshirë ato që lidhen me unazat, deklaratat dhe teknikat e vendosjes, si dhe çështje më të përgjithshme që prekin Terraform në përgjithësi:

  • parametrat count dhe for_seach kanë kufizime;
  • kufizoni zero dislokimet e kohës së ndërprerjes;
  • edhe një plan i mirë mund të dështojë;
  • rifaktorimi mund të ketë kurthet e veta;
  • koherenca e shtyrë është në përputhje... me shtyrjen.

Parametrat e numërimit dhe për_çdo kanë kufizime

Shembujt në këtë kapitull përdorin gjerësisht parametrin count dhe shprehjen for_seach në sythe dhe logjikën e kushtëzuar. Ata performojnë mirë, por kanë dy kufizime të rëndësishme për të cilat duhet të keni parasysh.

  • Numërimi dhe për_secili nuk mund të referojnë asnjë variabël të prodhimit të burimit.
  • count dhe for_each nuk mund të përdoren në konfigurimin e modulit.

count dhe for_seach nuk mund të referojnë asnjë variabël të prodhimit të burimit

Imagjinoni që ju duhet të vendosni disa serverë EC2 dhe për ndonjë arsye nuk dëshironi të përdorni ASG. Kodi juaj mund të jetë si ky:

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

Le t'i shikojmë ato një nga një.

Meqenëse parametri i numërimit është vendosur në një vlerë statike, ky kod do të funksionojë pa probleme: kur të ekzekutoni komandën aplikoni, ai do të krijojë tre serverë EC2. Por, çka nëse dëshironi të vendosni një server në secilën Zonë të Disponueshmërisë (AZ) brenda rajonit tuaj aktual AWS? Ju mund të vendosni kodin tuaj të ngarkojë një listë zonash nga burimi i të dhënave aws_availability_zones dhe më pas të kaloni nëpër secilën prej tyre dhe të krijoni një server EC2 në të duke përdorur parametrin e numërimit dhe aksesin e indeksit të grupit:

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

Ky kod gjithashtu do të funksionojë mirë, pasi parametri i numërimit mund të referojë burimet e të dhënave pa asnjë problem. Por çfarë ndodh nëse numri i serverëve që duhet të krijoni varet nga prodhimi i disa burimeve? Për ta demonstruar këtë, mënyra më e lehtë është përdorimi i burimit random_integer, i cili, siç sugjeron emri, kthen një numër të plotë të rastësishëm:

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

Ky kod gjeneron një numër të rastësishëm midis 1 dhe 3. Le të shohim se çfarë ndodh nëse përpiqemi të përdorim daljen e këtij burimi në parametrin e numërimit të burimit aws_instance:

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

Nëse ekzekutoni planin terraform në këtë kod, do të merrni gabimin e mëposhtëm:

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 kërkon që count dhe for_secil të llogaritet gjatë fazës së planifikimit, përpara se të krijohet ose modifikohet ndonjë burim. Kjo do të thotë që count dhe for_seach mund t'u referohen fjalëve të drejta, variablave, burimeve të të dhënave dhe madje edhe listave të burimeve (përderisa gjatësia e tyre mund të përcaktohet në kohën e planifikimit), por jo variablave të prodhimit të burimeve të llogaritura.

count dhe for_each nuk mund të përdoren në konfigurimin e modulit

Një ditë mund të tundoheni të shtoni një parametër numërimi në konfigurimin e modulit tuaj:

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

     count = 3

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

Ky kod përpiqet të përdorë numërimin brenda një moduli për të krijuar tre kopje të burimit të grupit të serverit në internet. Ose mund të dëshironi ta bëni opsional lidhjen e një moduli bazuar në disa kushte Boolean duke vendosur parametrin e tij të numërimit në 0. Ky mund të duket si kod i arsyeshëm, por do të merrni këtë gabim kur ekzekutoni planin terraform:

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.

Fatkeqësisht, që nga Terraform 0.12.6, përdorimi i count ose for_each në një burim moduli nuk mbështetet. Sipas shënimeve të lëshimit të Terraform 0.12 (http://bit.ly/3257bv4), HashiCorp planifikon ta shtojë këtë aftësi në të ardhmen, kështu që në varësi të kohës kur e lexoni këtë libër, ai mund të jetë tashmë i disponueshëm. Për ta zbuluar me siguri, lexoni ndryshimin e Terraform këtu.

Kufizimet e vendosjeve zero joproduktive

Përdorimi i bllokut create_before_destroy në kombinim me ASG është një zgjidhje e shkëlqyeshme për krijimin e vendosjeve pa ndërprerje, me përjashtim të një paralajmërimi: rregullat e shkallëzimit automatik nuk mbështeten. Ose për të qenë më të saktë, kjo rivendos madhësinë ASG në madhësinë min_në çdo vendosje, gjë që mund të jetë problem nëse po përdorni rregullat e shkallëzimit automatik për të rritur numrin e serverëve që funksionojnë.

Për shembull, moduli webserver-cluster përmban një palë burimesh aws_autoscaling_schedule, të cilat në orën 9 të mëngjesit rritin numrin e serverëve në grup nga dy në dhjetë. Nëse vendoseni, të themi, në orën 11 të mëngjesit, ASG-i i ri do të niset me vetëm dy serverë në vend të dhjetë dhe do të mbetet i tillë deri në orën 9 të mëngjesit të ditës tjetër.

Ky kufizim mund të anashkalohet në disa mënyra.

  • Ndryshoni parametrin e përsëritjes në aws_autoscaling_schedule nga 0 9 * * * ("vraponi në 9 të mëngjesit") në diçka si 0-59 9-17 * * * ("vraponi çdo minutë nga 9 e mëngjesit deri në 5 pasdite"). Nëse ASG ka tashmë dhjetë serverë, ekzekutimi përsëri i këtij rregulli të shkallëzimit automatik nuk do të ndryshojë asgjë, gjë që ne duam. Por nëse ASG është vendosur vetëm kohët e fundit, ky rregull do të sigurojë që në një maksimum prej një minutë numri i serverëve të tij të arrijë në dhjetë. Kjo nuk është një qasje krejtësisht elegante dhe kërcimet e mëdha nga dhjetë në dy serverë dhe mbrapa gjithashtu mund të shkaktojnë probleme për përdoruesit.
  • Krijoni një skript të personalizuar që përdor API-në AWS për të përcaktuar numrin e serverëve aktivë në ASG, thirreni duke përdorur një burim të jashtëm të dhënash (shih "Burimi i jashtëm i të dhënave" në faqen 249) dhe vendos parametrin e dëshiruar_kapacitetit të ASG në vlerën e kthyer nga skenarin. Në këtë mënyrë, çdo shembull i ri ASG do të funksionojë gjithmonë me të njëjtin kapacitet si kodi ekzistues Terraform dhe e bën më të vështirë mirëmbajtjen.

Natyrisht, Terraform në mënyrë ideale do të kishte mbështetje të integruar për vendosjet pa ndërprerje, por që nga maji 2019, ekipi HashiCorp nuk kishte plane për të shtuar këtë funksionalitet (detaje - këtu).

Plani i duhur mund të zbatohet pa sukses

Ndonjëherë komanda e planit prodhon një plan vendosjeje krejtësisht të saktë, por komanda e aplikimit kthen një gabim. Provoni, për shembull, të shtoni burimin aws_iam_user me të njëjtin emër që keni përdorur për përdoruesin IAM që keni krijuar më parë në Kapitullin 2:

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

Tani, nëse ekzekutoni komandën e planit, Terraform do të nxjerrë një plan vendosjeje në dukje të arsyeshme:

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.

Nëse ekzekutoni komandën aplikoni, do të merrni gabimin e mëposhtëm:

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

Problemi, natyrisht, është se një përdorues IAM me atë emër ekziston tashmë. Dhe kjo mund të ndodhë jo vetëm për përdoruesit e IAM, por për pothuajse çdo burim. Është e mundur që dikush ta krijoi këtë burim manualisht ose duke përdorur vijën e komandës, por sido që të jetë, përputhja e ID-ve çon në konflikte. Ka shumë variacione të këtij gabimi që shpesh i kapin në befasi të sapoardhurit në Terraform.

Pika kryesore është se komanda e planit terraform merr parasysh vetëm ato burime që janë të specifikuara në skedarin e gjendjes Terraform. Nëse burimet krijohen në ndonjë mënyrë tjetër (për shembull, manualisht duke klikuar në tastierën AWS), ato nuk do të përfundojnë në skedarin e gjendjes dhe për këtë arsye Terraform nuk do t'i marrë parasysh kur ekzekuton komandën e planit. Si rezultat, një plan që duket i saktë në shikim të parë do të rezultojë i pasuksesshëm.

Nga kjo mund të mësohen dy mësime.

  • Nëse tashmë keni filluar të punoni me Terraform, mos përdorni asgjë tjetër. Nëse një pjesë e infrastrukturës suaj menaxhohet duke përdorur Terraform, nuk mund ta modifikoni më manualisht. Përndryshe, jo vetëm që rrezikoni gabime të çuditshme Terraform, por gjithashtu mohoni shumë nga përfitimet e IaC pasi kodi nuk do të jetë më një paraqitje e saktë e infrastrukturës suaj.
  • Nëse tashmë keni një infrastrukturë, përdorni komandën e importit. Nëse po filloni të përdorni Terraform me infrastrukturën ekzistuese, mund ta shtoni atë në skedarin e gjendjes duke përdorur komandën e importit terraform. Në këtë mënyrë Terraform do të dijë se çfarë infrastrukturë duhet të menaxhojë. Komanda import merr dy argumente. E para është adresa e burimit në skedarët tuaj të konfigurimit. Sintaksa këtu është e njëjtë si për lidhjet e burimeve: _. (si aws_iam_user.existing_user). Argumenti i dytë është ID-ja e burimit që do të importohet. Le të themi se ID-ja e burimit aws_iam_user është emri i përdoruesit (për shembull, yevgeniy.brikman), dhe ID-ja e burimit aws_instance është ID e serverit EC2 (si i-190e22e5). Si të importoni një burim zakonisht tregohet në dokumentacionin në fund të faqes së tij.

    Më poshtë është një komandë importi që sinkronizon burimin aws_iam_user që keni shtuar në konfigurimin tuaj Terraform së bashku me përdoruesin e IAM në Kapitullin 2 (duke zëvendësuar emrin tuaj me yevgeniy.brikman, sigurisht):

    $ terraform import aws_iam_user.existing_user yevgeniy.brikman

    Terraform do të thërrasë API-në AWS për të gjetur përdoruesin tuaj IAM dhe për të krijuar një lidhje të skedarit shtetëror midis tij dhe burimit aws_iam_user.existing_user në konfigurimin tuaj të Terraform. Që tani e tutje, kur të ekzekutoni komandën e planit, Terraform do të dijë që përdoruesi IAM tashmë ekziston dhe nuk do të përpiqet ta krijojë më.

    Vlen të përmendet se nëse tashmë keni shumë burime që dëshironi të importoni në Terraform, shkrimi manual i kodit dhe importimi i secilit një nga një mund të jetë një sherr. Pra, ia vlen të shikoni një mjet si Terraforming (http://terraforming.dtan4.net/), i cili mund të importojë automatikisht kodin dhe gjendjen nga llogaria juaj AWS.

    Rifaktorimi mund të ketë kurthet e veta

    Rifaktorimi është një praktikë e zakonshme në programim ku ndryshoni strukturën e brendshme të kodit duke e lënë sjelljen e jashtme të pandryshuar. Kjo është për ta bërë kodin më të qartë, më të pastër dhe më të lehtë për t'u ruajtur. Rifaktorimi është një teknikë e domosdoshme që duhet përdorur rregullisht. Por kur bëhet fjalë për Terraform ose ndonjë mjet tjetër IaC, duhet të jeni jashtëzakonisht të kujdesshëm për atë që nënkuptoni me "sjelljen e jashtme" të një pjese të kodit, përndryshe do të shfaqen probleme të papritura.

    Për shembull, një lloj i zakonshëm i rifaktorimit është zëvendësimi i emrave të variablave ose funksioneve me ato më të kuptueshme. Shumë IDE kanë mbështetje të integruar për rifaktorimin dhe mund të riemërtojnë automatikisht variablat dhe funksionet gjatë gjithë projektit. Në gjuhët e programimit për qëllime të përgjithshme, kjo është një procedurë e parëndësishme për të cilën mund të mos mendoni, por në Terraform duhet të jeni jashtëzakonisht të kujdesshëm me këtë, përndryshe mund të keni ndërprerje.

    Për shembull, moduli webserver-cluster ka një variabël hyrëse cluster_name:

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

    Imagjinoni që keni filluar të përdorni këtë modul për të vendosur një mikroshërbim të quajtur foo. Më vonë, ju dëshironi të riemërtoni shërbimin tuaj në shirit. Ky ndryshim mund të duket i parëndësishëm, por në realitet mund të shkaktojë ndërprerje të shërbimit.

    Fakti është se moduli webserver-cluster përdor variablin cluster_name në një numër burimesh, duke përfshirë parametrin e emrit të dy grupeve të sigurisë dhe 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]
    }

    Nëse ndryshoni parametrin e emrit në një burim, Terraform do të fshijë versionin e vjetër të atij burimi dhe do të krijojë një të ri në vend të tij. Por nëse ai burim është një ALB, midis fshirjes së tij dhe shkarkimit të një versioni të ri, nuk do të keni një mekanizëm për të ridrejtuar trafikun në serverin tuaj të internetit. Po kështu, nëse një grup sigurie fshihet, serverët tuaj do të fillojnë të refuzojnë çdo trafik rrjeti derisa të krijohet një grup i ri.

    Një lloj tjetër rifaktorimi për të cilin mund të interesoheni është ndryshimi i ID Terraform. Le të marrim si shembull burimin aws_security_group në modulin webserver-cluster:

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

    Identifikuesi i këtij burimi quhet shembull. Imagjinoni që gjatë rifaktorimit keni vendosur ta ndryshoni atë në një emër më të kuptueshëm (sipas mendimit tuaj) cluster_instance:

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

    Çfarë do të ndodhë në fund? Kjo është e drejtë: një përçarje.

    Terraform lidh çdo ID të burimit me ID-në e ofruesit të resë kompjuterike. Për shembull, iam_user është i lidhur me ID-në e përdoruesit AWS IAM dhe aws_instance është i lidhur me ID-në e serverit AWS EC2. Nëse ndryshoni ID-në e burimit (të themi nga shembulli në cluster_instance, siç është rasti me aws_security_group), në Terraform do të duket sikur keni fshirë burimin e vjetër dhe keni shtuar një të ri. Nëse i zbatoni këto ndryshime, Terraform do të fshijë grupin e vjetër të sigurisë dhe do të krijojë një të ri, ndërsa serverët tuaj do të fillojnë të refuzojnë çdo trafik rrjeti.

    Këtu janë katër mësimet kryesore që duhet të hiqni nga ky diskutim.

    • Përdorni gjithmonë komandën plan. Mund të zbulojë të gjitha këto pengesa. Rishikoni me kujdes rezultatin e tij dhe kushtojini vëmendje situatave kur Terraform planifikon të fshijë burime që me shumë mundësi nuk duhet të fshihen.
    • Krijo përpara se të fshish. Nëse dëshironi të zëvendësoni një burim, mendoni me kujdes nëse keni nevojë të krijoni një zëvendësim përpara se të fshini origjinalin. Nëse përgjigja është po, krijimi_before_destroy mund të ndihmojë. I njëjti rezultat mund të arrihet manualisht duke kryer dy hapa: së pari shtoni një burim të ri në konfigurim dhe ekzekutoni komandën aplikoni, dhe më pas hiqni burimin e vjetër nga konfigurimi dhe përdorni përsëri komandën aplikoni.
    • Ndryshimi i identifikuesve kërkon ndryshimin e gjendjes. Nëse dëshironi të ndryshoni ID-në e lidhur me një burim (për shembull, riemërtoni aws_security_group nga shembulli në cluster_instance) pa e fshirë burimin dhe pa krijuar një version të ri të tij, duhet të përditësoni skedarin e gjendjes Terraform në përputhje me rrethanat. Asnjëherë mos e bëni këtë me dorë - përdorni komandën e gjendjes terraform në vend. Kur riemërtoni identifikuesit, duhet të ekzekutoni komandën terraform state mv, e cila ka sintaksën e mëposhtme:
      terraform state mv <ORIGINAL_REFERENCE> <NEW_REFERENCE>

      ORIGINAL_REFERENCE është një shprehje që i referohet burimit në formën e tij aktuale, dhe NEW_REFERENCE është vendi ku dëshironi ta zhvendosni. Për shembull, kur riemërtoni grupin aws_security_group nga shembull në cluster_instance, duhet të ekzekutoni komandën e mëposhtme:

      $ terraform state mv 
         aws_security_group.instance 
         aws_security_group.cluster_instance

      Kjo i tregon Terraform se gjendja që ishte lidhur më parë me aws_security_group.instance tani duhet të lidhet me aws_security_group.cluster_instance. Nëse pas riemërtimit dhe ekzekutimit të kësaj komande plani terraform nuk tregon ndonjë ndryshim, atëherë keni bërë gjithçka në mënyrë korrekte.

    • Disa cilësime nuk mund të ndryshohen. Parametrat e shumë burimeve janë të pandryshueshme. Nëse përpiqeni t'i ndryshoni ato, Terraform do të fshijë burimin e vjetër dhe do të krijojë një të ri në vend të tij. Çdo faqe burimi zakonisht do të tregojë se çfarë ndodh kur ndryshoni një cilësim të caktuar, prandaj sigurohuni që të kontrolloni dokumentacionin. Përdorni gjithmonë komandën plan dhe merrni parasysh përdorimin e strategjisë create_before_destroy.

    Konsistenca e shtyrë është në përputhje... me shtyrjen

    API-të e disa ofruesve të cloud, të tilla si AWS, janë asinkrone dhe kanë qëndrueshmëri të vonuar. Asinkronia do të thotë që ndërfaqja mund të kthejë menjëherë një përgjigje pa pritur që veprimi i kërkuar të përfundojë. Konsistenca e vonuar nënkupton që ndryshimet mund të kërkojnë kohë për t'u përhapur në të gjithë sistemin; ndërkohë që kjo po ndodh, përgjigjet tuaja mund të jenë jokonsistente dhe varen nga ajo se cila kopje e burimit të të dhënave po u përgjigjet thirrjeve tuaja API.

    Imagjinoni, për shembull, që të bëni një thirrje API në AWS duke i kërkuar që të krijojë një server EC2. API do të kthejë një përgjigje "të suksesshme" (201 e krijuar) pothuajse menjëherë, pa pritur që të krijohet vetë serveri. Nëse përpiqeni të lidheni me të menjëherë, pothuajse me siguri do të dështojë sepse në atë pikë AWS ende po inicializon burimet ose, përndryshe, serveri nuk është nisur ende. Për më tepër, nëse bëni një telefonatë tjetër për të marrë informacion rreth këtij serveri, mund të merrni një gabim (404 Nuk u gjet). Gjë është se informacioni në lidhje me këtë server EC2 mund të përhapet ende në të gjithë AWS përpara se të bëhet i disponueshëm kudo, do t'ju duhet të prisni disa sekonda.

    Sa herë që përdorni një API asinkron me konsistencë dembele, duhet të riprovoni periodikisht kërkesën tuaj derisa veprimi të përfundojë dhe të përhapet nëpër sistem. Fatkeqësisht, AWS SDK nuk ofron ndonjë mjet të mirë për këtë, dhe projekti Terraform vuante nga shumë gabime si 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

    Me fjalë të tjera, ju krijoni një burim (si një nënrrjet) dhe më pas përpiqeni të merrni disa informacione rreth tij (si ID-ja e nënrrjetit të krijuar rishtazi), dhe Terraform nuk mund ta gjejë atë. Shumica e këtyre gabimeve (përfshirë 6813) janë rregulluar, por ato ende shfaqen herë pas here, veçanërisht kur Terraform shton mbështetje për një lloj të ri burimi. Kjo është e bezdisshme, por në shumicën e rasteve nuk shkakton ndonjë dëm. Kur të ekzekutoni përsëri terraform application, gjithçka duhet të funksionojë, pasi në këtë kohë informacioni do të jetë përhapur tashmë në të gjithë sistemin.

    Ky fragment është paraqitur nga libri i Evgeniy Brikman "Terraform: infrastruktura në nivelin e kodit".

Burimi: www.habr.com

Shto një koment