Mitego ya Terraform

Mitego ya Terraform
Hebu tuangazie hitilafu chache, ikiwa ni pamoja na zile zinazohusiana na vitanzi, ikiwa taarifa na mbinu za kupeleka, pamoja na masuala ya jumla zaidi yanayoathiri Terraform kwa ujumla:

  • hesabu na kwa_kila vigezo vina mapungufu;
  • punguza uwekaji wa muda wa sifuri;
  • hata mpango mzuri unaweza kushindwa;
  • refactoring inaweza kuwa na mitego yake;
  • upatanisho ulioahirishwa unalingana... na uahirisho.

Hesabu na kwa_kila vigezo vina mapungufu

Mifano katika sura hii hutumia sana kigezo cha kuhesabu na usemi wa_kila katika vitanzi na mantiki ya masharti. Wanafanya vizuri, lakini wana mapungufu mawili muhimu ambayo unahitaji kufahamu.

  • Hesabu na kwa_kila moja haiwezi kurejelea anuwai za matokeo ya rasilimali.
  • count na kwa_kila moja haiwezi kutumika katika usanidi wa moduli.

count na for_each haiwezi kurejelea anuwai za rasilimali za pato

Fikiria unahitaji kupeleka seva kadhaa za EC2 na kwa sababu fulani hutaki kutumia ASG. Nambari yako inaweza kuwa kama hii:

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

Hebu tuwaangalie mmoja baada ya mwingine.

Kwa kuwa parameter ya kuhesabu imewekwa kwa thamani ya tuli, msimbo huu utafanya kazi bila matatizo: unapoendesha amri ya kuomba, itaunda seva tatu za EC2. Lakini vipi ikiwa ungetaka kupeleka seva moja katika kila Eneo la Upatikanaji (AZ) ndani ya eneo lako la sasa la AWS? Unaweza kufanya msimbo wako upakie orodha ya kanda kutoka chanzo cha data cha aws_availability_zones kisha upitishe kila moja na uunde seva ya EC2 ndani yake kwa kutumia kigezo cha kuhesabu na ufikiaji wa faharasa ya safu:

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

Nambari hii pia itafanya kazi vizuri, kwani kigezo cha kuhesabu kinaweza kurejelea vyanzo vya data bila shida yoyote. Lakini nini kinatokea ikiwa idadi ya seva unayohitaji kuunda inategemea matokeo ya rasilimali fulani? Ili kuonyesha hili, njia rahisi ni kutumia random_integer rasilimali, ambayo, kama jina linavyopendekeza, inarudisha nambari nasibu:

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

Nambari hii hutoa nambari nasibu kati ya 1 na 3. Hebu tuone kitakachotokea ikiwa tutajaribu kutumia matokeo ya rasilimali hii katika kigezo cha hesabu cha aws_instance rasilimali:

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

Ukiendesha mpango wa terraform kwenye nambari hii, utapata hitilafu ifuatayo:

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 inahitaji hesabu na kwa_kila kuhesabiwa wakati wa awamu ya kupanga, kabla ya rasilimali yoyote kuundwa au kurekebishwa. Hii ina maana kwamba count na for_each inaweza kurejelea halisi, vigeu, vyanzo vya data, na hata orodha za rasilimali (ilimradi urefu wao unaweza kuamuliwa wakati wa kuratibu), lakini si kukokotoa vigeu vya matokeo ya rasilimali.

count na kwa_kila moja haiwezi kutumika katika usanidi wa moduli

Siku moja unaweza kujaribiwa kuongeza kigezo cha hesabu kwenye usanidi wa moduli yako:

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

     count = 3

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

Nambari hii inajaribu kutumia kuhesabu ndani ya moduli kuunda nakala tatu za rasilimali ya nguzo ya wavuti. Au unaweza kutaka kufanya kuunganisha moduli kuwa ya hiari kulingana na hali fulani ya Boolean kwa kuweka kigezo chake cha kuhesabu hadi 0. Hii inaweza kuonekana kama msimbo unaofaa, lakini utapata hitilafu hii unapoendesha mpango wa 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.

Kwa bahati mbaya, kufikia Terraform 0.12.6, kutumia count au for_kila katika nyenzo ya moduli haitumiki. Kulingana na maelezo ya toleo ya Terraform 0.12 (http://bit.ly/3257bv4), HashiCorp inapanga kuongeza uwezo huu katika siku zijazo, kwa hivyo kulingana na wakati utakaposoma kitabu hiki, huenda tayari kikapatikana. Ili kujua kwa uhakika, soma mabadiliko ya Terraform hapa.

Vizuizi vya Usambazaji wa Muda Sifuri wa Kutokuwepo

Kutumia zuio la create_before_destroy pamoja na ASG ni suluhisho bora kwa kuunda utumaji wa muda usiopungua, isipokuwa tahadhari moja: sheria za kuongeza kasi otomatiki hazitumiki. Au ili kuwa sahihi zaidi, hii huweka upya ukubwa wa ASG hadi min_size kwenye kila uwekaji, ambayo inaweza kuwa tatizo ikiwa unatumia sheria za kuongeza kasi kiotomatiki kuongeza idadi ya seva zinazofanya kazi.

Kwa mfano, moduli ya nguzo ya webserver ina jozi ya rasilimali za aws_autoscaling_schedule, ambayo saa 9 asubuhi huongeza idadi ya seva kwenye nguzo kutoka mbili hadi kumi. Ukituma saa, sema, 11 a.m., ASG mpya itaanza na seva mbili badala ya kumi na kubaki hivyo hadi 9 a.m. siku inayofuata.

Kizuizi hiki kinaweza kuepukwa kwa njia kadhaa.

  • Badilisha kigezo cha kujirudia katika aws_autoscaling_schedule kutoka 0 9 * * * (β€œkimbia saa 9 asubuhi”) hadi kitu kama 0-59 9-17 * * * (β€œkimbia kila dakika kutoka 9 asubuhi hadi 5 jioni”). Ikiwa ASG tayari ina seva kumi, kuendesha sheria hii ya kuongeza otomatiki tena haitabadilisha chochote, ambayo ndio tunataka. Lakini ikiwa ASG imetumwa hivi karibuni tu, sheria hii itahakikisha kuwa katika kiwango cha juu cha dakika idadi ya seva zake itafikia kumi. Hii sio mbinu ya kifahari kabisa, na kuruka kubwa kutoka kwa seva kumi hadi mbili na nyuma pia kunaweza kusababisha matatizo kwa watumiaji.
  • Unda hati maalum inayotumia API ya AWS ili kubainisha idadi ya seva zinazotumika katika ASG, iite kwa kutumia chanzo cha nje cha data (ona "Chanzo cha Data ya Nje" kwenye ukurasa wa 249), na uweke kigezo cha uwezo_wa_wa ASG kwa thamani iliyorejeshwa na hati. Kwa njia hii, kila mfano mpya wa ASG utaendeshwa kwa uwezo sawa na msimbo uliopo wa Terraform na hufanya iwe vigumu zaidi kudumisha.

Bila shaka, Terraform ingekuwa na usaidizi wa ndani wa uwekaji wa muda usiopungua sifuri, lakini kufikia Mei 2019, timu ya HashiCorp haikuwa na mpango wa kuongeza utendakazi huu (maelezo - hapa).

Mpango sahihi unaweza kutekelezwa bila mafanikio

Wakati mwingine amri ya mpango hutoa mpango sahihi wa kupeleka, lakini amri ya kuomba inarudisha kosa. Jaribu, kwa mfano, kuongeza rasilimali ya aws_iam_user kwa jina lile lile ulilotumia kwa mtumiaji wa IAM uliyeunda awali katika Sura ya 2:

resource "aws_iam_user" "existing_user" {
   # ΠŸΠΎΠ΄ΡΡ‚Π°Π²ΡŒΡ‚Π΅ сюда имя ΡƒΠΆΠ΅ ΡΡƒΡ‰Π΅ΡΡ‚Π²ΡƒΡŽΡ‰Π΅Π³ΠΎ ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»Ρ IAM,
   # Ρ‡Ρ‚ΠΎΠ±Ρ‹ ΠΏΠΎΠΏΡ€Π°ΠΊΡ‚ΠΈΠΊΠΎΠ²Π°Ρ‚ΡŒΡΡ Π² использовании ΠΊΠΎΠΌΠ°Π½Π΄Ρ‹ terraform import
   name = "yevgeniy.brikman"
}

Sasa, ikiwa utaendesha amri ya mpango, Terraform itatoa mpango unaoonekana kuwa sawa wa kupeleka:

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.

Ukiendesha amri ya kuomba utapata makosa yafuatayo:

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

Tatizo, bila shaka, ni kwamba mtumiaji wa IAM aliye na jina hilo tayari yupo. Na hii inaweza kutokea sio tu kwa watumiaji wa IAM, lakini kwa karibu rasilimali yoyote. Kuna uwezekano kwamba mtu aliunda nyenzo hii kwa mikono au kwa kutumia safu ya amri, lakini kwa vyovyote vile, kulinganisha vitambulisho husababisha migogoro. Kuna tofauti nyingi za hitilafu hii ambayo mara nyingi huwapata wageni kwa Terraform kwa mshangao.

Jambo kuu ni kwamba amri ya mpango wa terraform inazingatia tu rasilimali hizo ambazo zimeainishwa kwenye faili ya hali ya Terraform. Ikiwa rasilimali zinaundwa kwa njia nyingine (kwa mfano, kwa mikono kwa kubofya kwenye console ya AWS), hazitaishia kwenye faili ya serikali na kwa hiyo Terraform haitazingatia wakati wa kutekeleza amri ya mpango. Matokeo yake, mpango unaoonekana kuwa sahihi kwa mtazamo wa kwanza utageuka kuwa haukufanikiwa.

Kuna mambo mawili ya kujifunza kutokana na hili.

  • Ikiwa tayari umeanza kufanya kazi na Terraform, usitumie kitu kingine chochote. Ikiwa sehemu ya miundombinu yako inadhibitiwa kwa kutumia Terraform, huwezi tena kuirekebisha wewe mwenyewe. Vinginevyo, hauhatarishi tu makosa ya ajabu ya Terraform, lakini pia unapuuza manufaa mengi ya IaC kwani msimbo hautakuwa tena uwakilishi sahihi wa miundombinu yako.
  • Ikiwa tayari una miundombinu fulani, tumia amri ya kuagiza. Ikiwa unaanza kutumia Terraform na miundombinu iliyopo, unaweza kuiongeza kwenye faili ya serikali kwa kutumia amri ya uingizaji wa terraform. Kwa njia hii Terraform itajua ni miundombinu gani inahitaji kusimamiwa. Amri ya kuingiza inachukua hoja mbili. Ya kwanza ni anwani ya rasilimali katika faili zako za usanidi. Syntax hapa ni sawa na ya viungo vya rasilimali: _. (kama aws_iam_user.exist_user). Hoja ya pili ni kitambulisho cha rasilimali itakayoingizwa. Hebu tuseme kitambulisho cha rasilimali aws_iam_user ni jina la mtumiaji (kwa mfano, yevgeniy.brikman), na kitambulisho cha rasilimali aws_instance ni kitambulisho cha seva ya EC2 (kama i-190e22e5). Jinsi ya kuagiza rasilimali kawaida huonyeshwa kwenye hati iliyo chini ya ukurasa wake.

    Ifuatayo ni amri ya kuingiza ambayo inasawazisha rasilimali ya aws_iam_user ambayo umeongeza kwenye usanidi wako wa Terraform pamoja na mtumiaji wa IAM katika Sura ya 2 (kubadilisha jina lako kwa yevgeniy.brikman, bila shaka):

    $ terraform import aws_iam_user.existing_user yevgeniy.brikman

    Terraform itaita API ya AWS ili kutafuta mtumiaji wako wa IAM na kuunda muungano wa faili wa serikali kati yake na rasilimali ya mtumiaji_iam_user.existing_user katika usanidi wako wa Terraform. Kuanzia sasa, unapoendesha amri ya mpango, Terraform itajua kwamba mtumiaji wa IAM tayari yupo na hatajaribu kuunda tena.

    Inafaa kumbuka kuwa ikiwa tayari una rasilimali nyingi ambazo ungependa kuingiza kwenye Terraform, kuandika kwa mikono msimbo na kuagiza kila moja kwa wakati kunaweza kuwa shida. Kwa hivyo inafaa kutafuta zana kama Terraforming (http://terraforming.dtan4.net/), ambayo inaweza kuingiza kiotomati msimbo na hali kutoka kwa akaunti yako ya AWS.

    Refactoring inaweza kuwa na mitego yake

    Kuunda upya ni mazoezi ya kawaida katika upangaji programu ambapo unabadilisha muundo wa ndani wa msimbo huku ukiacha tabia ya nje bila kubadilika. Hii ni kufanya msimbo kuwa wazi zaidi, nadhifu, na rahisi kutunza. Refactoring ni mbinu ya lazima ambayo inapaswa kutumika mara kwa mara. Lakini inapokuja kwa Terraform au zana nyingine yoyote ya IaC, unapaswa kuwa mwangalifu sana kuhusu kile unachomaanisha kwa "tabia ya nje" ya kipande cha msimbo, vinginevyo matatizo yasiyotarajiwa yatatokea.

    Kwa mfano, aina ya kawaida ya urekebishaji upya ni kubadilisha majina ya vigeu au chaguo za kukokotoa na zinazoeleweka zaidi. IDE nyingi zina usaidizi wa ndani wa kurekebisha tena na zinaweza kubadilisha kiotomatiki vigeu na vitendakazi katika mradi wote. Katika lugha za programu za madhumuni ya jumla, huu ni utaratibu mdogo ambao unaweza usifikirie, lakini katika Terraform unapaswa kuwa mwangalifu sana na hili, vinginevyo unaweza kukumbwa na hitilafu.

    Kwa mfano, moduli ya nguzo ya webserver ina jina la cluster_name la pembejeo:

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

    Fikiria kuwa ulianza kutumia moduli hii kupeleka huduma ndogo inayoitwa foo. Baadaye, ungependa kubadilisha jina la huduma yako hadi upau. Mabadiliko haya yanaweza kuonekana kuwa madogo, lakini kwa kweli yanaweza kusababisha usumbufu wa huduma.

    Ukweli ni kwamba moduli ya nguzo ya wavuti hutumia utaftaji wa cluster_name katika rasilimali kadhaa, pamoja na kigezo cha jina la vikundi viwili vya usalama na 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]
    }

    Ukibadilisha kigezo cha jina kwenye rasilimali, Terraform itafuta toleo la zamani la rasilimali hiyo na kuunda mpya mahali pake. Lakini ikiwa rasilimali hiyo ni ALB, kati ya kuifuta na kupakua toleo jipya, hutakuwa na utaratibu wa kuelekeza trafiki kwenye seva yako ya wavuti. Vivyo hivyo, ikiwa kikundi cha usalama kitafutwa, seva zako zitaanza kukataa trafiki yoyote ya mtandao hadi kikundi kipya kitakapoundwa.

    Aina nyingine ya urekebishaji ambayo unaweza kupendezwa nayo ni kubadilisha Kitambulisho cha Terraform. Wacha tuchukue rasilimali ya aws_security_group katika moduli ya nguzo ya wavuti kama mfano:

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

    Kitambulisho cha rasilimali hii inaitwa mfano. Fikiria kuwa wakati wa kurekebisha tena uliamua kuibadilisha kuwa inayoeleweka zaidi (kwa maoni yako) cluster_instance ya jina:

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

    Nini kitatokea mwishoni? Hiyo ni kweli: usumbufu.

    Terraform inahusisha kila kitambulisho cha nyenzo na kitambulisho cha mtoa huduma wa wingu. Kwa mfano, iam_user inahusishwa na Kitambulisho cha mtumiaji cha AWS IAM, na aws_instance inahusishwa na Kitambulisho cha seva cha AWS EC2. Ukibadilisha kitambulisho cha nyenzo (sema kutoka kwa mfano hadi cluster_instance, kama ilivyo kwa aws_security_group), hadi Terraform itaonekana kana kwamba umefuta rasilimali ya zamani na kuongeza mpya. Ukitumia mabadiliko haya, Terraform itafuta kikundi cha zamani cha usalama na kuunda kipya, huku seva zako zikianza kukataa trafiki yoyote ya mtandao.

    Hapa kuna masomo manne muhimu unapaswa kuchukua kutoka kwa mjadala huu.

    • Tumia amri ya mpango kila wakati. Inaweza kufichua nyufa hizi zote. Kagua matokeo yake kwa uangalifu na uzingatie hali ambapo Terraform inapanga kufuta rasilimali ambazo kuna uwezekano mkubwa hazipaswi kufutwa.
    • Unda kabla ya kufuta. Ikiwa unataka kubadilisha rasilimali, fikiria kwa uangalifu ikiwa unahitaji kuunda mbadala kabla ya kufuta asili. Ikiwa jibu ni ndiyo, create_before_destroy inaweza kusaidia. Matokeo sawa yanaweza kupatikana kwa mikono kwa kufanya hatua mbili: kwanza ongeza rasilimali mpya kwenye usanidi na uendesha amri ya kuomba, na kisha uondoe rasilimali ya zamani kutoka kwa usanidi na utumie tena amri ya kuomba.
    • Kubadilisha vitambulisho kunahitaji kubadilisha hali. Ikiwa ungependa kubadilisha kitambulisho kinachohusishwa na rasilimali (kwa mfano, badilisha jina la aws_security_group kutoka kwa mfano hadi cluster_instance) bila kufuta rasilimali na kuunda toleo lake jipya, lazima usasishe faili ya hali ya Terraform ipasavyo. Usiwahi kufanya hivi mwenyewe - tumia amri ya hali ya terraform badala yake. Wakati wa kubadilisha vitambulisho, unapaswa kuendesha terraform state mv amri, ambayo ina syntax ifuatayo:
      terraform state mv <ORIGINAL_REFERENCE> <NEW_REFERENCE>

      ORIGINAL_REFERENCE ni usemi unaorejelea rasilimali katika hali yake ya sasa, na NEW_REFERENCE ndipo unapotaka kuihamisha. Kwa mfano, unapobadilisha jina la kikundi cha aws_security_group kutoka kwa mfano hadi cluster_instance, unahitaji kutekeleza amri ifuatayo:

      $ terraform state mv 
         aws_security_group.instance 
         aws_security_group.cluster_instance

      Hii inaiambia Terraform kwamba hali ambayo hapo awali ilihusishwa na aws_security_group.instance inapaswa sasa kuhusishwa na aws_security_group.cluster_instance. Ikiwa baada ya kubadili jina na kuendesha mpango huu wa terraform ya amri hauonyeshi mabadiliko yoyote, basi ulifanya kila kitu kwa usahihi.

    • Baadhi ya mipangilio haiwezi kubadilishwa. Vigezo vya rasilimali nyingi hazibadiliki. Ukijaribu kuzibadilisha, Terraform itafuta rasilimali ya zamani na kuunda mpya mahali pake. Kila ukurasa wa rasilimali utaonyesha kile kinachotokea unapobadilisha mpangilio fulani, kwa hivyo hakikisha kuwa umeangalia hati. Tumia amri ya mpango kila wakati na uzingatie kutumia mkakati wa create_before_destroy.

    Uthabiti ulioahirishwa ni sawa... na ucheleweshaji

    Baadhi ya API za watoa huduma za wingu, kama vile AWS, hazilingani na zimechelewesha uthabiti. Asynchrony inamaanisha kuwa kiolesura kinaweza kurudisha jibu mara moja bila kungoja kitendo kilichoombwa kukamilika. Uthabiti uliocheleweshwa unamaanisha kuwa mabadiliko yanaweza kuchukua muda kueneza katika mfumo mzima; wakati haya yanafanyika, majibu yako yanaweza yasifanane na yanategemea ni nakala gani ya chanzo cha data inajibu simu zako za API.

    Fikiria, kwa mfano, kwamba unapiga simu ya API kwa AWS ukiiuliza iunde seva ya EC2. API itarejesha jibu la "mafanikio" (201 Imeundwa) karibu mara moja, bila kungoja seva yenyewe iundwe. Ukijaribu kuunganishwa nayo mara moja, hakika itashindwa kwa sababu wakati huo AWS bado inaanzisha rasilimali au, vinginevyo, seva bado haijaanza. Zaidi ya hayo, ukipiga simu nyingine ili kupata taarifa kuhusu seva hii, unaweza kupokea hitilafu (404 Haijapatikana). Jambo ni kwamba maelezo kuhusu seva hii ya EC2 bado yanaweza kuenezwa kote kwenye AWS kabla ya kupatikana kila mahali, utahitaji kusubiri sekunde chache.

    Wakati wowote unapotumia API ya asynchronous na uthabiti wa uvivu, lazima ujaribu tena ombi lako mara kwa mara hadi hatua ikamilike na kueneza kupitia mfumo. Kwa bahati mbaya, AWS SDK haitoi zana zozote nzuri kwa hili, na mradi wa Terraform ulikumbwa na hitilafu nyingi kama 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

    Kwa maneno mengine, unaunda rasilimali (kama subnet) na kisha kujaribu kupata taarifa fulani kuihusu (kama kitambulisho cha subnet mpya iliyoundwa), na Terraform haiwezi kuipata. Nyingi za hitilafu hizi (ikiwa ni pamoja na 6813) zimerekebishwa, lakini bado zinaongezeka mara kwa mara, hasa wakati Terraform inapoongeza usaidizi kwa aina mpya ya rasilimali. Hii inakera, lakini katika hali nyingi haisababishi madhara yoyote. Unapoendesha terraform kuomba tena, kila kitu kinapaswa kufanya kazi, kwani kwa wakati huu habari itakuwa tayari imeenea katika mfumo wote.

    Nukuu hii imewasilishwa kutoka kwa kitabu na Evgeniy Brikman "Terraform: miundombinu katika kiwango cha kanuni".

Chanzo: mapenzi.com

Kuongeza maoni