Pikat e ndjeshme të Terraform

Pikat e ndjeshme të Terraform
Do t'i përmendim disa pengesa, duke përfshirë ato që lidhen me ciklet, shprehjet if dhe metodologjitë e shpërndarjes, si dhe me probleme më të përgjithshme që lidhen me Terraform-in në tërësi:

  • parametrat count dhe for_each kanë kufizime;
  • kufizimet e shpërndarjeve me kohë të zero;
  • edhe një plan i mirë mund të dështojë;
  • refaktorizimi mund të ketë magjitë e veta;
  • konvergjenca e vonuar është në harmoni... me vonesën.

Parametrat count dhe for_each kanë kufizime

Në shembujt e këtij kapitulli, parameteri count dhe shprehja for_each përdoren aktivisht në cikle dhe logjikën kushtore. Ata japin rezultate të mira, por kanë dy kufizime të rëndësishme, për të cilat duhet të jeni të vetëdijshëm.

  • Në count dhe for_each nuk mund të referoheni në cilatdo variabla dalëse të burimit.
  • count dhe for_each nuk mund të përdoren në konfigurimin e modulit.

Në count dhe for_each nuk mund të referoheni në cilatdo variabla dalëse të burimit.

Imagjinoni që duhet të shpërndani disa serverë EC2 dhe për ndonjë arsye nuk dëshironi të përdorni ASG. Kodi juaj mund të jetë i tillë:

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

Le t’i shqyrtojmë një nga një.

Duke qenë se parametri count ka një vlerë statike, ky kod do të funksionojë pa probleme: kur të ekzekutoni komandën apply, ai do të krijojë tre serverë EC2. Por, nëse dëshironi të shpërndani një server në secilën zonë të disponueshmërisë (Availability Zone ose AZ) në kuadër të rajonit aktual AWS? Ju mund të bëni që kodi juaj të ngarkohet lista e zonave nga burimi i të dhënave aws_availability_zones dhe pastaj të "kaloni" përmes secilës prej tyre dhe të krijoni aty një server EC2, duke përdorur parametrin count dhe qasjen në array përmes indekseve:

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 do të funksionojë gjithashtu mrekullisht, pasi parametri count mund të referohet pa probleme në burimet e të dhënave. Por çfarë do të ndodhë nëse numri i serverëve që duhet të krijoni varet nga dalja e ndonjë burimi? Për ta demonstruar këtë, është më lehtë të merrni burimin random_integer, i cili, siç mund të kuptohet nga emri, kthen një numër të rastësishëm të plotë:

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

Ky këtë kod gjeneron një numër të rastësishëm nga 1 në 3. Le të shohim se çfarë do të ndodhë nëse përpiqemi të përdorim rezultatin e këtij burimi në parametrin count 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 terraform plan për 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

Vlera "count" varet nga atributet e burimit që nuk mund të përcaktohen deri në aplikim, kështu që Terraform nuk mund të parashikojë se sa instanca do të krijohen. Për t'u përballur me këtë, përdorni argumentin -target për të aplikuar fillimisht vetëm burimet që varet count.

Terraform kërkon që count dhe for_each të llogariten në fazën e planifikimit, para se të krijohen ose ndryshohen ndonjë burim. Kjo do të thotë se count dhe for_each mund të referohen në literale, variabla, burime të dhënash dhe madje lista burimesh (nën kushtin që gjatësia e tyre të mund të përcaktohet gjatë planifikimit), por jo në variabla të daljes së llogaritur të burimeve.

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

Një herë do t'ju vije dëshira për të shtuar parametrin count në konfigurimet e modulit:

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ë count brenda modulit për të krijuar tre kopje të burimit webserver-cluster. Ose, ndoshta, dëshironi ta bëni lidhjen me modulin opsionale në varësi të ndonjë kushti Boolean, duke i dhënë parametrin count vlerën 0. Ky kod do të dukej mjaft i arsyeshëm, megjithatë, pasi të ekzekutoni terraform plan do të merrni këtë gabim:

Error: Reserved argument name in module block

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

Emri "count" është i rezervuar për përdorim në një version të ardhshëm të Terraform.

Fatkeqësisht, në momentin e daljes së Terraform 0.12.6, përdorimi i count ose for_each në burimin e modulit nuk mbështetet. Sipas shënimeve të lëshimit të Terraform 0.12 (http:\/\/bit.ly\/3257bv4) HashiCorp planifikon të shtojë këtë mundësi në të ardhmen, kështu që, në varësi të se kur e lexoni këtë libër, ajo mund të jetë tashmë në dispozicion. Për të përditësuar, lexoni regjistrin e ndryshimeve të Terraform këtu.

Kufizimet e shpërndarjeve pa kohë të papagueshme

Përdorimi i bllokut create_before_destroy në kombinim me ASG është një zgjidhje e shkëlqyer për organizimin e shpërndarjeve me kohë të papërgatitur të papërgjigjshme, përveç një nuancë: rregullat e automatizimit të shkallëzimit në këtë rast nuk mbështeten. Ose, për të qenë më të saktë, kjo e rikthen madhësinë e ASG-së përsëri në min_size me çdo shpërndarje, e cila mund të bëhet një problem nëse keni përdorur rregulla automatizimi për të rritur numrin e serverëve të aktivizuar.

Për shembull, moduli webserver-cluster përmban disa burime aws_autoscaling_schedule, të cilat në orën 9 e rrisin numrin e serverëve në klaster nga dy në dhjetë. Nëse realizoni një shpërndarje, le të themi në orën 11, grupi i ri ASG nuk do të ngarkohet me dhjetë, por vetëm me dy serverë dhe do të mbetet në këtë gjendje deri në orën 9 të mëngjesit të ditës tjetër.

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

  • Ndryshoni parametrin recurrence në aws_autoscaling_schedule nga 0 9 * * * ("ekzekuto në orën 9") në diçka si 0-59 9-17 * * * ("ekzekuto çdo minutë nga ora 9 deri në orën 5 pasdite"). Nëse në ASG ka tashmë dhjetë serverë, përsëritja e këtij rregulli të automatizimit të shkallëzimit nuk do të ndryshojë asgjë, që na nevojitet. Por nëse grupi ASG është shpërndarë shumë kohë rishtas, ky rregull garanton që jo më vonë se pas një minute numri i serverëve të saj të arrijë dhjetë. Kjo nuk është një qasje shumë elegante, dhe skacima të mëdha nga dhjetë në dy serverë dhe prapa gjithashtu mund të shkaktojnë probleme për përdoruesit.
  • Krijoni një skript të personalizuar, i cili përdor API-në AWS për të përcaktuar numrin e serverëve aktivë në ASG, thërrisni atë me një burim të jashtëm të dhënash (shihni seksionin "Burimi i jashtëm i të dhënave" në fq. 249) dhe caktoni parametrin desired_capacity të grupit ASG me vlerën që kthehet nga ky skript. Kështu, çdo instancë e re ASG do të fillojë gjithmonë me kapacitetin e njëjtë si ai që kishim për programin tonë Terraform dhe e bën atë më të komplikuar për t'u mirëmbajtur.

Sigurisht, në mënyrë ideale, Terraform duhet të ketë mbështetje të integruar për shpërndarjet me kohë të papërgjigjshme, por në maj 2019 ekipi i HashiCorp nuk kishte në plan të shtonte këtë funksionalitet (detajet - këtu).

Planifikimi i saktë mund të implementohet me pasiguri

Ndodh që, kur ekzekutoni komandën plan, të merrni një plan të saktë të implantimit, megjithatë komanda apply 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, të krijuar më parë në kapitullin 2:

resource "aws_iam_user" "existing_user" {
   # Vendosni këtu emrin e përdoruesit IAM që ekziston,
   # për të praktikuar përdorimin e komandës terraform import
   name = "yevgeniy.brikman"
}

Tani, nëse ekzekutoni komandën plan, Terraform do të nxjerrë një plan implantimi që duket mjaft i arsyeshëm:

Terraform do të kryejë veprimet e mëposhtme:

   # aws_iam_user.existing_user do të krijohet
   + resource "aws_iam_user" "existing_user" {
         + arn                  = (i njohur pas aplikimit)
         + force_destroy   = false
         + id                    = (i njohur pas aplikimit)
         + name               = "yevgeniy.brikman"
         + path                 = "\/"
         + unique_id         = (i njohur pas aplikimit)
      }

Plani: 1 për të shtuar, 0 për të ndryshuar, 0 për të shkatërruar.

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

Gabim: Gabim në krijimin e përdoruesit IAM yevgeniy.brikman: EntityAlreadyExists:
Përdoruesi me emrin yevgeniy.brikman ekziston tashmë.

   në main.tf rreshti 10, në burimin "aws_iam_user" "existing_user":
   10: resource "aws_iam_user" "existing_user" {

Problemi, sigurisht, është se përdoruesi IAM me këtë emër tashmë ekziston. Kjo mund të ndodhë jo vetëm me përdoruesit IAM, por edhe me praktikisht çdo burim tjetër. Ndoshta dikush e krijoi këtë burim manualisht ose përmes komandave, por, cfarëdo që të jetë, përputhja e identifikuesve çon në konflikte. Ky gabim ka shumë variante, të cilat shpesh i kapin në befasi fillestarët në Terraform.

Pika kryesore është se komanda terraform plan merr në konsideratë vetëm burimet që janë të listuara në skedarin e gjendjes së Terraform-it. Nëse burimet janë krijuar në një mënyrë tjetër (për shembull, manualisht, duke klikuar në konsolën AWS), ato nuk do të hyjnë në skedarin e gjendjes dhe, për pasojë, Terraform nuk do t'i marrë parasysh ato kur ekzekuton komandën plan. Si rezultat, një plan që duket i saktë në pamje të parë do të rezultojë në dështim.

Nga kjo, mund të nxirren dy mësime.

  • Nëse keni filluar të punoni me Terraform, mos përdorni asgjë tjetër. Nëse një pjesë e infrastrukturës tuaj menaxhohet me anë të Terraform-it, nuk mund të ndryshoni atë manualisht. Përndryshe, ju rrezikoni të merrni gabime të çuditshme në Terraform, por gjithashtu me këtë anuloni shumë nga përfitimet e IaC, pasi kodi nuk do të jetë më një pasqyrë e saktë e infrastrukturës tuaj.
  • Nëse tashmë keni një infrastrukturë, përdorni komandën import. Nëse po filloni të përdorni Terraform me një infrastrukturë ekzistuese, mund ta shtoni atë në skedarin e gjendjes me komandën terraform import. Kështu, Terraform do të dijë se me cilën infrastrukturë duhet të menaxhojë. Komanda import merr dy argumente. I pari është adresa e burimit në skedarët tuaj të konfigurimit. Këtu e njëjta sintaksë përdoret si në lidhjet e burimeve: _. (p.sh. aws_iam_user.existing_user). Argumenti i dytë është identifikuesi i burimit që duhet të importohet. Le të themi, si ID burimi aws_iam_user shërben emri i përdoruesit (p.sh. yevgeniy.brikman), ndërsa ID burimi aws_instance do të jetë identifikuesi i serverit EC2 (p.sh. i-190e22e5). Si të importohet një burim zakonisht shpjegohet në dokumentacionin në fund të faqes së tij.

    Më poshtë është komandë importi që lejon sinkronizimin e burimit aws_iam_user, të cilin e keni shtuar në konfigurimin tuaj Terraform së bashku me përdoruesin IAM në kapitullin 2 (sigurisht, në vend të yevgeniy.brikman duhet të vendosni emrin tuaj):

    $ terraform import aws_iam_user.existing_user yevgeniy.brikman

    Terraform do të kontaktojë me API-në e AWS për të gjetur përdoruesin tuaj IAM dhe për të krijuar në skedarin e gjendjes një lidhje midis tij dhe burimit aws_iam_user.existing_user në konfigurimin tuaj Terraform. Që nga ky moment, kur ekzekutohet komanda plan, Terraform do të dijë se përdoruesi IAM tashmë ekziston dhe nuk do të përpiqet ta krijojë atë përsëri.

    Duhet theksuar se, nëse keni shumë burime që dëshironi të importoni në Terraform, shkruajtja manuale e kodit dhe importimi i secilit prej tyre ndaras mund të jetë një punë e lodhshme. Prandaj, është e rekomandueshme të merrni parasysh një mjet si Terraforming (http://terraforming.dtan4.net/), i cili mund të importojë automatikisht kodin dhe gjendjen nga llogaria AWS.

    Rifaktorizimi mund të ketë disa kurthe

    Rifaktorizimi ë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ë e nevojshme për ta bërë kodin më të kuptueshëm, të pastër dhe më të lehtë për t'u mirëmbajtur. Rifaktorizimi është një metodikë e pazëvendësueshme që duhet të aplikohet 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ënkuptohet me "sjelljen e jashtme" të një pjese të kodit, përndryshe mund të shfaqen probleme të paparashikuara.

    P.sh e jushëm e refaktorimit është ndryshimi i emrave të variablave ose funksioneve me emra më kuptimplotë. Shumë IDE kanë mbështetje të integruar për refaktorizimin dhe mund të ndryshojnë automatikisht emrat e variablave dhe funksioneve në tërë projektin. Në gjuhët e programimit të zakonshme, kjo është një procedurë triviale për të cilën mund të mos mendoni, megjithatë, në Terraform duhet të jeni shumë të kujdesshëm me të; ndryshe, mund të hasni në ndërprerje të funksionimit.

    P.sh, moduli webserver-cluster ka një variabel hyrës cluster_name:

    variable "cluster_name" {
       description = "Emri për të cilin do të përdoren të gjitha burimet e klasterit"
       type          = string
    }

    Imagjinoni se keni filluar ta përdorni këtë modul për të vendosur një mikroshërbim me emrin foo. Më vonë, dëshironi ta ndryshoni emrin e shërbimit në bar. Ky ndryshim mund të duket trivial, por në realitet ai mund të shkaktojë ndërprerje të funksionimit.

    Çështja është se moduli webserver-cluster përdor variablën cluster_name në një numër të caktuar burimesh, duke përfshirë parametrin name 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 name në ndonjë burim, Terraform do të fshijë versionin e vjetër të atij burimi dhe do të krijojë një të ri. Por nëse një burim i tillë është ALB, në periudhën midis fshirjes së tij dhe ngarkimit të versionit të ri nuk do të keni një mekanizëm për të drejtuar trafikun në serverin tuaj. Po ashtu, nëse një grup sigurie fshihet, serverët tuaj do të fillojnë të refuzojnë çdo trafik rrjetor derisa të krijohet një grup i ri.

    Një tjetër lloj refaktorizimi që mund t'ju interesojë është ndryshimi i identifikuesit të Terraform. Merrni si shembull burimin aws_security_group në modulit webserver-cluster:

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

    Ky identifikues i burimit quhet instance. Imagjinoni se gjatë refaktorizimit vendosni ta ndryshoni në një emër më kuptimplotë (sipasje mendjes tuaj) cluster_instance:

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

    Çfarë do të ndodhë në fund? E saktë: ndërprerje e funksionimit.

    Terraform lidh ID-në e çdo burimi me identifikuesin e ofruesit të cloud. Për shembull, iam_user lidhet me identifikuesin e përdoruesit IAM në AWS, ndërsa aws_instance lidhet me ID-në e serverit AWS EC2. Nëse e ndryshoni identifikuesin e burimit (për shembull, nga instance në cluster_instance, si në rastin e aws_security_group), për Terraform do të duket ashtu si të kishit fshirë burimin e vjetër dhe të kishit shtuar një të ri. Nëse aplikoni këto ndryshime, Terraform do të fshijë grupin e vjetër të sigurisë dhe do të krijojë një tjetër, ndërkohë serverët tuaj do të fillojnë të refuzojnë çdo trafiku rrjetor.

    Ja katër mësime kryesore që duhet të nxirrni nga kjo diskutim.

    • Gjithmonë përdorni komandën plan. Ajo mund të zbulojë të gjitha këto nyje. Rishikoni me kujdes daljen e saj dhe kushtojini vëmendje situatave kur Terraform planifikon të fshijë burime që ndoshta nuk duhet të fshihen.
    • Krijoni para se të fshini. Nëse dëshironi të zëvendësoni një burim, mendoni mirë nëse duhet të krijoni zëvendësimin para se të fshini origjinalin. Nëse përgjigjja është pozitive, create_before_destroy mund t'ju ndihmojë. E njëjta rezultat mund të arrihet manualisht duke kryer dy hapa: së pari, shtoni burimin e ri në konfigurim dhe përgjigjuni me komandën apply, pastaj fshini burimin e vjetër nga konfigurimi dhe përdorni komandën apply përsëri.
    • Ndryshimi i identifikuesve kërkon ndryshimin e gjendjes. nëse dëshironi të ndryshoni identifikuesin që lidhet me burimin (për shembull, të rinomin aws_security_group nga instance në cluster_instance), duke shmangur fshirjen e burimit dhe krijimin e një versioni të ri, është e nevojshme të përditësoni skedarin e gjendjes së Terraform në përputhje. Kurrë mos e bëni këtë manualisht - përdorni në vend të kësaj komandën terraform state. Kur rinovoni identifikuesit, duhet të ekzekutoni komandën terraform state mv, e cila ka sintaksën e mëposhtme:
      terraform state mv

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

      $ terraform state mv 
         aws_security_group.instance 
         aws_security_group.cluster_instance

      Kështu do t'i tregoni Terraform-it se gjendja që më parë i përkiste aws_security_group.instance tani duhet të lidhet me aws_security_group.cluster_instance. Nëse pas rinovimit dhe ekzekutimit të kësaj komande terraform plan nuk tregon asnjë ndryshim, atëherë keni bërë gjithçka siç duhet.

    • Disa parametër nuk mund të ndryshohen. Parametër të shumë burimeve janë të pandryshueshëm. Nëse përpiqeni t'i ndryshoni ato, Terraform do të fshijë burimin e vjetër dhe do të krijojë një të ri për të. Në faqen e çdo burimi zakonisht gjendet informacioni se çfarë ndodh kur ndryshoni një parametër të caktuar, prandaj mos harroni të verifikoni dokumentacionin. Përdorni gjithmonë komandën plan dhe shqyrtoni arsyeshmërinë e aplikimit të strategjisë create_before_destroy.

    Konsistenca e shtyrë pajtohet... me vonesën

    API-të e disa ofruesve të shërbimeve në cloud, si AWS, janë asinkrone dhe kanë konsistencë të shtyrë. Asinkronia do të thotë se ndërfaqja mund të kthejë menjëherë një përgjigje, pa pritur përfundimin e veprimit të kërkuar. Konsistenca e shtyrë do të thotë se mund të nevojitet kohë për të përhapur ndryshimet në të gjithë sistemin; deri atëherë, përgjigjet tuaja mund të jenë të pasakta dhe të varen nga cila klon burimi të dhënash po përgjigjet në thirrjet tuaja API.

    Imagjinoni, për shembull, se ju bëni një thirrje API në AWS për të kërkuar krijimin e një serveri EC2. API-ja do të kthejë një përgjigje "të suksesshme" (201 Created) pothuajse menjëherë, pa pritur krijimin e vetë serverit. Nëse përpiqeni të lidheni menjëherë me të, është praktikisht e sigurt që nuk do të funksionojë, sepse në atë moment AWS ende po inicializon burimet ose, si alternativë, serveri nuk ka ngarkuar ende. Për më tepër, nëse bëni një thirrje tjetër për të marrë informacion mbi këtë server, mund të merrni një gabim (404 Not Found). Çështja është se informacioni për këtë server EC2 mund të jetë ende në procesin e shpërndarjes nëpër AWS, për të qenë i доступshëm kudo, do të duhet të prisni disa sekonda.

    Me çdo përdorim të API-së asinkrone me konsistencë të shtyrë, ju duhet të përsërisni herë pas here kërkesën tuaj derisa veprimi të përfundojë dhe të shpërndahet në sistem. Fatkeqësisht, AWS SDK nuk ofron asnjë mjet të mirë për këtë, dhe projekti Terraform ka vuajtur më parë nga shumë defekte si gabimi 6813 (https://github.com/hashicorp/terraform/issues/6813):

    $ terraform apply
    aws_subnet.private-persistence.2: InvalidSubnetID.NotFound:
    ID e subnet-it 'subnet-xxxxxxx' nuk ekziston

    Me fjalë të tjera, po krijoni një burim (p.sh. një subnet) dhe më pas përpiqeni të merrni disa të dhëna rreth tij (si ID e subnet-it të sapo krijuar), por Terraform nuk mund t'i gjejë ato. Shumica e këtyre gabimeve (përfshirë 6813) janë gjithashtu rregulluar, por herë pas here ato ende shfaqen, veçanërisht kur Terraform shton mbështetje për një tip të ri burimi. Kjo është e bezdisshme, por në shumicën e rasteve nuk shkakton ndonjë dëm. Duke e ekzekutuar përsëri terraform apply, gjithçka duhet të funksionojë, pasi deri në atë kohë informacioni tashmë do të jetë shpërndarë në sistem.

    Ky fragment është marrë nga libri i Evgeny Brickman «Terraform: infrastruktura si kod».

Burimi: habr.com

Blini hosting të besueshëm për faqe interneti me mbrojtje nga DDoS, serverë VPS VDS 🔥 Blini hosting të besueshëm për faqe interneti me mbrojtje nga DDoS, serverë VPS VDS | ProHoster