Terraform enkonvenyans

Terraform enkonvenyans
Ann mete aksan sou kèk enkonvenyans, ki gen ladan sa ki gen rapò ak bouk, si deklarasyon ak teknik deplwaman, osi byen ke pwoblèm pi jeneral ki afekte Terraform an jeneral:

  • paramèt konte ak for_each gen limit;
  • limite zewo deplwaman D';
  • menm yon bon plan ka echwe;
  • refactoring ka gen enkonvenyans li yo;
  • koerans difere konsistan... ak ranvwa.

Paramèt konte ak for_each gen limit

Egzanp yo nan chapit sa a fè anpil itilizasyon paramèt konte ak ekspresyon for_each nan bouk ak lojik kondisyonèl. Yo fè byen, men yo gen de limit enpòtan ke ou bezwen konnen.

  • Count ak for_each pa ka fè referans a okenn varyab pwodiksyon resous.
  • count ak for_each pa ka itilize nan konfigirasyon modil.

count ak for_each pa ka fè referans a okenn varyab pwodiksyon resous

Imajine ou bezwen deplwaye plizyè serveurs EC2 epi pou kèk rezon ou pa vle sèvi ak ASG. Kòd ou a ta ka tankou sa a:

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

Ann gade yo youn pa youn.

Depi paramèt konte a mete sou yon valè estatik, kòd sa a pral travay san pwoblèm: lè ou kouri lòd aplike a, li pral kreye twa sèvè EC2. Men, e si ou te vle deplwaye yon sèvè nan chak Zòn Disponibilite (AZ) nan rejyon AWS ou ye kounye a? Ou ka fè kòd ou a chaje yon lis zòn ki soti nan sous done aws_availability_zones epi answit pase chak youn epi kreye yon sèvè EC2 ladan l lè l sèvi avèk paramèt konte a ak aksè nan endèks etalaj la:

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

Kòd sa a pral travay byen tou, depi paramèt konte a ka fè referans a sous done san okenn pwoblèm. Men, sa k ap pase si kantite serveurs ou bezwen kreye depann sou pwodiksyon kèk resous? Pou demontre sa a, fason ki pi fasil la se sèvi ak resous random_integer, ki, jan non an sijere, retounen yon nonb antye relatif:

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

Kòd sa a jenere yon nimewo o aza ant 1 ak 3. Ann wè sa k ap pase si nou eseye sèvi ak pwodiksyon resous sa a nan paramèt konte resous aws_instance:

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

Si ou kouri plan terraform sou kòd sa a, ou pral jwenn erè sa a:

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 mande pou yo kalkile count and for_each pandan faz planifikasyon, anvan nenpòt resous yo kreye oswa modifye. Sa vle di ke count and for_each ka refere a literal, varyab, sous done, e menm lis resous (osi lontan ke longè yo ka detèmine nan moman orè), men se pa nan varyab pwodiksyon resous kalkile.

count ak for_each pa ka itilize nan konfigirasyon modil

Yon jou ou ta ka tante ajoute yon paramèt konte nan konfigirasyon modil ou a:

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

     count = 3

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

Kòd sa a eseye sèvi ak konte andedan yon modil pou kreye twa kopi resous sèvè entènèt-cluster la. Oswa ou ta ka vle fè konekte yon modil opsyonèl ki baze sou kèk kondisyon Boolean lè w mete paramèt konte li a 0. Sa a ta ka sanble kòd rezonab, men w ap jwenn erè sa a lè w ap kouri plan 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.

Malerezman, apati Terraform 0.12.6, yo pa sipòte itilize count oswa for_each nan yon resous modil. Dapre nòt lage Terraform 0.12 yo (http://bit.ly/3257bv4), HashiCorp planifye pou ajoute kapasite sa a nan lavni, kidonk depann sou lè ou li liv sa a, li ka deja disponib. Pou konnen pou asire w, li Terraform changelog la isit la.

Limitasyon Zewo D' Deplwaman

Sèvi ak blòk create_before_destroy an konbinezon ak ASG se yon bon solisyon pou kreye deplwaman zewo-downtime, eksepte pou yon opozisyon: règ otoscaling yo pa sipòte. Oswa yo dwe pi egzak, sa a retabli gwosè ASG tounen nan min_size sou chak deplwaman, ki ta ka yon pwoblèm si ou te itilize règ otoscaling pou ogmante kantite sèvè k ap kouri.

Pa egzanp, modil webserver-cluster la gen yon pè resous aws_autoscaling_schedule, ki a 9 am ogmante kantite sèvè nan gwoup la soti de a dis. Si ou deplwaye a, di, 11 a.m., nouvo ASG la pral demare ak jis de sèvè olye ke dis epi rete konsa jiska 9 a.m. jou kap vini an.

Limit sa a ka kontourne nan plizyè fason.

  • Chanje paramèt repetisyon nan aws_autoscaling_schedule soti nan 0 9 * * * ("kouri a 9 am") pou yon bagay tankou 0-59 9-17 * * * ("kouri chak minit soti nan 9 am a 5 pm"). Si ASG deja gen dis serveurs, kouri règ sa a otoscaling ankò pa pral chanje anyen, ki se sa nou vle. Men, si ASG la sèlman dènyèman te deplwaye, règ sa a pral asire ke nan yon maksimòm de yon minit kantite a nan serveurs li yo pral rive nan dis. Sa a se pa yon apwòch totalman elegant, ak gwo so soti nan dis a de serveurs ak tounen lakay ou ka lakòz tou pwoblèm pou itilizatè yo.
  • Kreye yon script koutim ki sèvi ak AWS API pou detèmine kantite sèvè aktif nan ASG la, rele li lè l sèvi avèk yon sous done ekstèn (gade "Sous Done Ekstèn" nan paj 249), epi mete paramèt want_capacity ASG a sou valè a retounen. script la. Nan fason sa a, chak nouvo egzanp ASG ap toujou kouri nan menm kapasite ak kòd Terraform ki deja egziste a epi fè li pi difisil pou kenbe.

Natirèlman, Terraform ta depreferans gen sipò entegre pou deplwaman zewo-detan, men apati Me 2019, ekip HashiCorp pa te gen okenn plan pou ajoute fonksyonalite sa a (detay - isit la).

Plan ki kòrèk la ka aplike san siksè

Pafwa kòmandman plan an pwodui yon plan deplwaman parfe kòrèk, men kòmandman aplike a retounen yon erè. Eseye, pou egzanp, ajoute resous aws_iam_user ak menm non ou te itilize pou itilizatè IAM ou te kreye pi bonè nan Chapit 2:

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

Koulye a, si ou kouri lòd plan an, Terraform pral bay yon plan deplwaman w pèdi rezonab:

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.

Si ou kouri lòd aplike a, ou pral jwenn erè sa a:

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

Pwoblèm nan, nan kou, se ke yon itilizatè IAM ak non sa a deja egziste. Epi sa ka rive pa sèlman itilizatè IAM, men prèske nenpòt resous. Li posib ke yon moun te kreye resous sa a manyèlman oswa lè l sèvi avèk liy lòd la, men nenpòt fason, matche ID mennen nan konfli. Gen anpil varyasyon nan erè sa a ki souvan trape fèk vini nan Terraform pa sipriz.

Pwen kle a se ke lòd plan Terraform sèlman pran an kont resous sa yo ki espesifye nan dosye eta Terraform la. Si yo kreye resous nan yon lòt fason (pa egzanp, manyèlman lè w klike sou konsole AWS la), yo p ap fini nan fichye eta a, kidonk Terraform pa pral pran yo an kont lè w ap egzekite kòmandman plan an. Kòm yon rezilta, yon plan ki sanble kòrèk nan premye gade pral tounen san siksè.

Gen de leson yo dwe aprann nan sa a.

  • Si ou te deja kòmanse travay ak Terraform, pa sèvi ak anyen ankò. Si yon pati nan enfrastrikti ou jere ak Terraform, ou pa kapab modifye li manyèlman ankò. Sinon, ou pa sèlman riske erè Terraform etranj, men ou tou refize anpil nan benefis IaC depi kòd la p ap yon reprezantasyon egzat nan enfrastrikti ou ankò.
  • Si ou deja gen kèk enfrastrikti, sèvi ak lòd enpòte a. Si w ap kòmanse sèvi ak Terraform ak enfrastrikti ki egziste deja, ou ka ajoute li nan fichye eta a lè l sèvi avèk lòd enpòte terraform la. Nan fason sa a Terraform pral konnen ki enfrastrikti ki bezwen jere. Kòmandman enpòte a pran de agiman. Premye a se adrès resous ki nan dosye konfigirasyon ou yo. Sentaks la isit la se menm jan ak pou lyen resous: _. (tankou aws_iam_user.existing_user). Dezyèm agiman an se ID resous yo dwe enpòte. Ann di ID resous aws_iam_user se non itilizatè a (pa egzanp, yevgeniy.brikman), ak ID resous aws_instance se ID sèvè EC2 (tankou i-190e22e5). Ki jan yo enpòte yon resous anjeneral endike nan dokiman ki anba a nan paj li yo.

    Anba a se yon kòmandman enpòte ki senkronize resous aws_iam_user ou ajoute nan konfigirasyon Terraform ou a ansanm ak itilizatè IAM nan Chapit 2 (ranplase non ou pou yevgeniy.brikman, nan kou):

    $ terraform import aws_iam_user.existing_user yevgeniy.brikman

    Terraform pral rele API AWS la pou jwenn itilizatè IAM ou a epi kreye yon asosyasyon fichye eta ant li ak resous aws_iam_user.existing_user nan konfigirasyon Terraform ou a. Depi koulye a, lè ou kouri kòmand plan an, Terraform pral konnen ke itilizatè IAM a deja egziste epi li pa pral eseye kreye li ankò.

    Li se vo anyen ke si ou deja gen anpil resous ke ou vle enpòte nan Terraform, manyèlman ekri kòd la ak enpòte chak youn youn nan yon moman ka yon konplikasyon. Se konsa, li vo gade nan yon zouti tankou Terraforming (http://terraforming.dtan4.net/), ki ka otomatikman enpòte kòd ak eta soti nan kont AWS ou.

    Refactoring ka gen enkonvenyans li yo

    Refactoring se yon pratik komen nan pwogramasyon kote ou chanje estrikti entèn kòd la pandan w ap kite konpòtman ekstèn lan san okenn chanjman. Sa a se fè kòd la pi klè, pwòp, ak pi fasil pou kenbe. Refactoring se yon teknik endispansab ki ta dwe itilize regilyèman. Men, lè li rive Terraform oswa nenpòt lòt zouti IaC, ou dwe fè anpil atansyon sou sa ou vle di pa "konpòtman ekstèn" nan yon moso nan kòd, otreman pwoblèm inatandi pral leve.

    Pou egzanp, yon kalite refactoring komen se ranplase non yo nan varyab oswa fonksyon ak moun ki pi konprann. Anpil IDE gen sipò entegre pou refactoring epi yo ka otomatikman chanje non varyab ak fonksyon atravè pwojè a. Nan langaj pwogram jeneral, sa a se yon pwosedi trivial ke ou ta ka pa panse sou, men nan Terraform ou dwe fè anpil atansyon ak sa a, otreman ou ka fè eksperyans pann.

    Pa egzanp, modil webserver-cluster la gen yon varyab non cluster_name:

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

    Imajine ke ou te kòmanse itilize modil sa a pou deplwaye yon mikwosèvis ki rele foo. Apre sa, ou vle chanje non sèvis ou a ba. Chanjman sa a ka sanble trivial, men an reyalite li ka lakòz dezòd sèvis.

    Reyalite a se ke modil webserver-cluster la itilize varyab cluster_name nan yon kantite resous, ki gen ladan paramèt non de gwoup sekirite ak ALB la:

    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]
    }

    Si ou chanje paramèt non an sou yon resous, Terraform pral efase ansyen vèsyon an nan resous sa a epi kreye yon nouvo nan plas li. Men, si resous sa a se yon ALB, ant efase li ak telechaje yon nouvo vèsyon, ou p ap gen yon mekanis pou redireksyon trafik nan sèvè entènèt ou an. Menm jan an tou, si yo efase yon gwoup sekirite, serveurs ou yo ap kòmanse rejte nenpòt trafik rezo jiskaske yo kreye yon nouvo gwoup.

    Yon lòt kalite refactoring ou ta ka enterese nan chanje ID Terraform la. Ann pran resous aws_security_group nan modil webserver-cluster la kòm yon egzanp:

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

    Idantifyan resous sa a rele egzanp. Imajine ke pandan refactoring ou deside chanje li nan yon non cluster_instance ki pi konprann (nan opinyon ou):

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

    Kisa ki pral rive nan fen a? Sa a dwat: yon dezòd.

    Terraform asosye chak ID resous ak ID founisè nwaj la. Pa egzanp, iam_user asosye ak ID itilizatè AWS IAM, epi aws_instance asosye ak ID sèvè AWS EC2. Si ou chanje ID resous la (di soti nan egzanp nan cluster_instance, tankou se ka a ak aws_security_group), nan Terraform li pral parèt kòm si ou efase ansyen resous la epi ajoute yon nouvo. Si ou aplike chanjman sa yo, Terraform pral efase ansyen gwoup sekirite a epi kreye yon nouvo, pandan y ap serveurs ou yo kòmanse rejte nenpòt trafik rezo.

    Men kat leson kle ou ta dwe retire nan diskisyon sa a.

    • Toujou itilize kòmandman plan an. Li ka revele tout dechire sa yo. Revize pwodiksyon li ak anpil atansyon epi peye atansyon sou sitiyasyon kote Terraform planifye pou efase resous ki gen plis chans pa ta dwe efase.
    • Kreye anvan ou efase. Si ou vle ranplase yon resous, reflechi byen sou si ou bezwen kreye yon ranplasman anvan efase orijinal la. Si repons lan se wi, create_before_destroy ka ede. Menm rezilta a ka reyalize manyèlman lè w fè de etap: premye ajoute yon nouvo resous nan konfigirasyon an epi kouri lòd aplike a, ak Lè sa a, retire ansyen resous la nan konfigirasyon an epi sèvi ak kòmandman an aplike ankò.
    • Chanje idantifyan mande pou chanje eta. Si ou vle chanje ID ki asosye ak yon resous (pa egzanp, chanje non aws_security_group soti nan egzanp nan cluster_instance) san yo pa efase resous la epi kreye yon nouvo vèsyon li, ou dwe mete ajou dosye eta Terraform kòmsadwa. Pa janm fè sa manyèlman - sèvi ak kòmandman eta terraform pito. Lè w chanje non idantifyan yo, ou ta dwe kouri lòd terraform state mv, ki gen sentaks sa a:
      terraform state mv <ORIGINAL_REFERENCE> <NEW_REFERENCE>

      ORIGINAL_REFERENCE se yon ekspresyon ki refere a resous la nan fòm li ye kounye a, epi NEW_REFERENCE se kote ou vle deplase li. Pou egzanp, lè w ap chanje non gwoup aws_security_group soti nan egzanp nan cluster_instance, ou bezwen kouri lòd sa a:

      $ terraform state mv 
         aws_security_group.instance 
         aws_security_group.cluster_instance

      Sa a di Terraform ke eta a ki te deja asosye ak aws_security_group.instance ta dwe kounye a asosye ak aws_security_group.cluster_instance. Si apre yo fin chanje non ak kouri plan terraform lòd sa a pa montre okenn chanjman, Lè sa a, ou te fè tout bagay kòrèkteman.

    • Gen kèk paramèt pa ka chanje. Paramèt anpil resous yo pa chanje. Si ou eseye chanje yo, Terraform pral efase ansyen resous la epi kreye yon nouvo nan plas li. Chak paj resous pral anjeneral endike sa k ap pase lè ou chanje yon anviwònman an patikilye, kidonk asire w ke ou tcheke dokiman an. Toujou itilize kòmandman plan an epi konsidere itilize estrateji create_before_destroy.

    Konsistans difere konsistan... ak ranvwa

    Gen kèk API founisè nwaj yo, tankou AWS, asynchrone epi yo gen reta konsistan. Asynchrony vle di ke koòdone a ka imedyatman retounen yon repons san yo pa tann pou aksyon yo mande a fini. Konsistans reta vle di ke chanjman yo ka pran tan pou pwopaje nan tout sistèm nan; pandan sa ap pase, repons ou yo ka enkonsistan ak depann sou ki kopi sous done ki reponn a apèl API ou yo.

    Imajine, pou egzanp, ke ou fè yon apèl API bay AWS mande li pou kreye yon sèvè EC2. API a pral retounen yon repons "siksè" (201 Kreye) prèske imedyatman, san yo pa tann pou sèvè a li menm yo dwe kreye. Si ou eseye konekte ak li touswit, li pral prèske sètènman echwe paske nan pwen sa a AWS toujou inisyalize resous oswa, altènativman, sèvè a poko demare. Anplis, si w fè yon lòt apèl pou w jwenn enfòmasyon sou sèvè sa a, ou ka resevwa yon erè (404 Not Found). Bagay la se ke enfòmasyon sou sèvè EC2 sa a ka toujou pwopaje nan tout AWS anvan li vin disponib toupatou, ou pral oblije rete tann kèk segonn.

    Chak fwa ou itilize yon API asynchrone ak konsistans parese, ou dwe detanzantan reesye demann ou a jiskaske aksyon an fini ak pwopaje nan sistèm nan. Malerezman, AWS SDK pa bay okenn bon zouti pou sa, ak pwojè Terraform te konn soufri anpil pinèz tankou 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

    Nan lòt mo, ou kreye yon resous (tankou yon subnet) ak Lè sa a, eseye jwenn kèk enfòmasyon sou li (tankou ID a nan subnet ki fèk kreye), epi Terraform pa ka jwenn li. Pifò nan pinèz sa yo (ki gen ladan 6813) yo te fikse, men yo toujou ap parèt detanzantan, espesyalman lè Terraform ajoute sipò pou yon nouvo kalite resous. Sa a se anmèdan, men nan pifò ka yo pa lakòz okenn mal. Lè ou kouri terraform aplike ankò, tout bagay ta dwe travay, depi lè sa a enfòmasyon yo pral deja gaye nan tout sistèm nan.

    Ekstrè sa a prezante nan liv la pa Evgeniy Brikman "Terraform: enfrastrikti nan nivo kòd la".

Sous: www.habr.com

Add nouvo kòmantè