Terraform Fallen

Terraform Fallen
Loosst eis e puer Falen ervirhiewen, dorënner déi mat Loops, wann Aussoen an Deployment Techniken, souwéi méi allgemeng Themen déi Terraform am Allgemengen beaflossen:

  • d'Zuel a fir_each Parameteren hunn Aschränkungen;
  • Limitéiert null Ausbrochzäiten;
  • souguer e gudde Plang kann versoen;
  • Refactoring kann seng Falen hunn;
  • ausgestallte Kohärenz ass konsequent ... mat Auslaaf.

D'Zuel a fir_each Parameteren hunn Aschränkungen

D'Beispiller an dësem Kapitel maachen extensiv Notzung vum Grofparameter an dem for_each Ausdrock a Schleifen a bedingungsloser Logik. Si Leeschtunge gutt, mä si hunn zwou wichteg Aschränkungen, datt Dir bewosst gin muss.

  • Count a for_each kënnen keng Ressourceausgangsvariablen referenzéieren.
  • count a for_each kann net an der Modulkonfiguratioun benotzt ginn.

count a for_each kann keng Ressourceausgangsvariablen referenzéieren

Stellt Iech vir, Dir musst e puer EC2 Serveren ofsetzen an aus iergendengem Grond wëllt Dir ASG net benotzen. Äre Code kéint esou sinn:

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

Loosst eis se een nom aneren kucken.

Zënter dem Zähleparameter op e statesche Wäert gesat ass, funktionnéiert dëse Code ouni Probleemer: wann Dir de Kommando applizéiert, erstellt dräi EC2 Serveren. Awer wat wann Dir e Server an all Disponibilitéitszone (AZ) an Ärer aktueller AWS Regioun wëllt ofsetzen? Dir kënnt Äre Code eng Lëscht vun Zonen aus der aws_availability_zones Datequell lueden an dann duerch all eenzel schloen an en EC2 Server dran erstellen andeems Dir de Grofparameter an den Array Index Zougang benotzt:

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

Dëse Code funktionnéiert och gutt, well de Grofparameter kann Datenquellen referenzéieren ouni Probleemer. Awer wat geschitt wann d'Zuel vun de Serveren déi Dir braucht fir ze kreéieren hänkt vun der Ausgab vun enger Ressource of? Fir dëst ze demonstréieren, ass de einfachste Wee fir d'random_integer Ressource ze benotzen, déi, wéi den Numm et scho seet, en zoufälleg ganzt Zuel zréckginn:

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

Dëse Code generéiert eng zoufälleg Zuel tëscht 1 an 3. Loosst eis kucken wat geschitt wa mir probéieren den Ausgang vun dëser Ressource am Zuelparameter vun der aws_instance Ressource ze benotzen:

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

Wann Dir Terraform Plang op dësem Code leeft, kritt Dir de folgende Feeler:

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 erfuerdert datt d'Zuel a fir_each während der Planungsphase berechent ginn, ier all Ressourcen erstallt oder geännert ginn. Dëst bedeit datt d'Zuel a fir_each op literaler, Variabelen, Datequellen a souguer Ressourcelëschte bezéie kënnen (soulaang hir Längt bei der Zäitplangzäit bestëmmt ka ginn), awer net op berechnen Ressourceausgangsvariablen.

count a for_each kann net an der Modulkonfiguratioun benotzt ginn

Irgendwann kënnt Dir versicht ginn e Grofparameter an Är Modulkonfiguratioun ze addéieren:

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

     count = 3

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

Dëse Code probéiert de Grof an engem Modul ze benotzen fir dräi Exemplare vun der Webserver-Cluster Ressource ze kreéieren. Oder Dir wëllt vläicht d'Verbindung vun engem Modul fakultativ maachen ofhängeg vun e puer booleschen Konditiounen andeems Dir säi Grofparameter op 0 setzt. Dëst kéint wéi raisonnabel Code ausgesinn, awer Dir kritt dëse Feeler wann Dir Terraform Plang ausféiert:

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.

Leider, wéi vun Terraform 0.12.6, benotzt Count oder for_each an engem Modul Ressource gëtt net ënnerstëtzt. Geméiss den Terraform 0.12 Verëffentlechungsnotizen (http://bit.ly/3257bv4), plangt HashiCorp dës Fäegkeet an der Zukunft ze addéieren, also ofhängeg wéi Dir dëst Buch liest, kann et scho verfügbar sinn. Fir sécher erauszefannen, liest den Terraform Changelog hei.

Aschränkungen vun Zero Downtime Deployments

D'Benotzung vum create_before_destroy Block a Kombinatioun mat ASG ass eng super Léisung fir Null-Downtime Deployementer, ausser fir een Opgepasst: Autoskaléierungsregelen ginn net ënnerstëtzt. Oder fir méi präzis ze sinn, setzt dëst d'ASG Gréisst zréck op min_size op all Deployment, wat e Problem kéint sinn wann Dir Autoskaléierungsregele benotzt fir d'Zuel vun de Serveren ze erhéijen déi lafen.

Zum Beispill enthält de Webserver-Cluster Modul e Paar aws_autoscaling_schedule Ressourcen, déi um 9 Auer d'Zuel vun de Serveren am Cluster vun zwee op zéng erhéicht. Wann Dir z.B. um 11 Auer deployéiert, wäert den neien ASG mat nëmmen zwee Serveren anstatt zéng booten a bleift esou bis 9 Auer den nächsten Dag.

Dës Begrenzung kann op verschidde Manéieren ëmgoen.

  • Ännert de Widderhuelungsparameter am aws_autoscaling_schedule vun 0 9 * * * ("Lafen um 9 Auer") op eppes wéi 0-59 9-17 * * * ("all Minutt vun 9 Auer bis 5 Auer lafen"). Wann ASG schonn zéng Serveren huet, wäert dës Autoskaléierungsregel nach eng Kéier näischt änneren, wat mir wëllen. Awer wann den ASG eréischt viru kuerzem agesat gouf, wäert dës Regel suergen datt an engem Maximum vun enger Minutt d'Zuel vu senge Serveren zéng erreecht. Dëst ass net eng ganz elegant Approche, a grouss Spréng vun zéng op zwee Serveren an zréck kënnen och Probleemer fir Benotzer verursaachen.
  • Erstellt e personaliséierte Skript deen d'AWS API benotzt fir d'Zuel vun den aktive Serveren am ASG ze bestëmmen, rufft se mat enger externer Datequell (kuckt "Extern Datenquell" op Säit 249), a setzt den ASG's wish_capacity Parameter op de Wäert zréck vum d'Schrëft. Op dës Manéier wäert all nei ASG Instanz ëmmer mat der selwechter Kapazitéit lafen wéi den existente Terraform Code a mécht et méi schwéier ze erhalen.

Natierlech hätt Terraform idealerweis agebauter Ënnerstëtzung fir Null-Downtime Deployementer, awer vum Mee 2019 hat d'HashiCorp Team keng Pläng fir dës Funktionalitéit ze addéieren (Detailer - hei).

De richtege Plang kann net erfollegräich ëmgesat ginn

Heiansdo produzéiert de Plangbefehl e perfekt korrekten Deploymentplang, awer de Kommando gëlt e Feeler zréck. Probéiert zum Beispill d'aws_iam_user Ressource mat dem selwechten Numm ze addéieren, deen Dir fir den IAM Benotzer benotzt hutt, deen Dir virdru am Kapitel 2 erstallt hutt:

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

Elo, wann Dir de Plang Kommando leeft, wäert Terraform e scheinbar raisonnabel Deploymentplang erausginn:

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.

Wann Dir de Kommando applizéiert hutt, kritt Dir de folgende Feeler:

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

De Problem ass natierlech datt en IAM Benotzer mat deem Numm schonn existéiert. An dëst kann net nëmme mat IAM Benotzer geschéien, mee bal all Ressource. Et ass méiglech datt iergendeen dës Ressource manuell erstallt huet oder d'Kommandozeil benotzt, awer entweder Manéier, passend IDen féiert zu Konflikter. Et gi vill Variatiounen vun dësem Feeler, déi dacks Newcomer zu Terraform iwwerraschen.

De Schlësselpunkt ass datt de Terraform Plang Kommando nëmmen déi Ressourcen berücksichtegt déi an der Terraform Staatsdatei spezifizéiert sinn. Wann Ressourcen op eng aner Manéier erstallt ginn (zum Beispill manuell andeems Dir op der AWS Konsole klickt), wäerte se net an der Staatsdatei ophalen an dofir wäert Terraform se net berücksichtegen wann Dir de Plangbefehl ausféiert. Als Resultat wäert e Plang, deen op den éischte Bléck richteg schéngt, net erfollegräich sinn.

Et ginn zwou Lektioune vun dësem ze léieren.

  • Wann Dir schonn ugefaang mat Terraform ze schaffen, benotzt näischt anescht. Wann en Deel vun Ärer Infrastruktur mat Terraform geréiert gëtt, kënnt Dir se net méi manuell änneren. Soss riskéiert Dir net nëmme komesch Terraform Feeler, awer Dir negéiert och vill vun de Virdeeler vum IaC well de Code net méi eng korrekt Duerstellung vun Ärer Infrastruktur wäert sinn.
  • Wann Dir schonn eng Infrastruktur hutt, benotzt den Import Kommando. Wann Dir ufänkt Terraform mat existéierenden Infrastrukturen ze benotzen, kënnt Dir et an d'Staatsdatei mat der terraform Import Kommando addéieren. Op dës Manéier wäert Terraform wëssen wéi eng Infrastruktur geréiert muss ginn. Den Import Kommando hëlt zwee Argumenter. Déi éischt ass d'Ressource Adress an Äre Konfiguratiounsdateien. D'Syntax hei ass d'selwecht wéi fir Ressourcelinks: _. (wéi aws_iam_user.existing_user). Dat zweet Argument ass d'ID vun der Ressource déi importéiert gëtt. Loosst eis soen datt d'Ressource ID aws_iam_user de Benotzernumm ass (zum Beispill yevgeniy.brikman), an d'Ressource ID aws_instance ass d'EC2 Server ID (wéi i-190e22e5). Wéi een eng Ressource importéiert gëtt normalerweis an der Dokumentatioun um Enn vun hirer Säit uginn.

    Drënner ass en Import Kommando, deen d'aws_iam_user Ressource synchroniséiert, déi Dir an Är Terraform Konfiguratioun zesumme mam IAM Benotzer am Kapitel 2 bäigefüügt hutt (awer Ären Numm fir yevgeniy.brikman natierlech ersat):

    $ terraform import aws_iam_user.existing_user yevgeniy.brikman

    Terraform rufft d'AWS API fir Ären IAM Benotzer ze fannen an eng Staatsdateiassociatioun tëscht him an der aws_iam_user.existing_user Ressource an Ärer Terraform Konfiguratioun ze kreéieren. Vun elo un, wann Dir de Plang Kommando leeft, wäert Terraform wëssen datt den IAM Benotzer scho existéiert a probéiert et net erëm ze kreéieren.

    Et ass derwäert ze notéieren datt wann Dir scho vill Ressourcen hutt déi Dir an Terraform importéiere wëllt, manuell de Code schreiwen an all eenzel gläichzäiteg importéieren kann e Problem sinn. Also ass et derwäert en Tool wéi Terraforming (http://terraforming.dtan4.net/) ze kucken, deen automatesch Code a Staat vun Ärem AWS Kont importéiere kann.

    Refactoring kann seng Falen hunn

    Refactoring ass eng gemeinsam Praxis am Programméiere wou Dir d'intern Struktur vum Code ännert, während Dir dat externt Verhalen onverännert léisst. Dëst ass fir de Code méi kloer, propper a méi einfach ze pflegen. Refactoring ass eng onverzichtbar Technik déi regelméisseg benotzt soll ginn. Awer wann et ëm Terraform oder all aner IaC-Tool kënnt, musst Dir extrem virsiichteg sinn wat Dir mam "externt Verhalen" vun engem Stéck Code mengt, soss entstinn onerwaart Probleemer.

    Zum Beispill, eng gemeinsam Aart vu Refactoring ersetzt d'Nimm vu Variabelen oder Funktiounen duerch méi verständlech. Vill IDEs hunn agebauter Ënnerstëtzung fir Refactoring a kënnen automatesch Variabelen a Funktiounen am ganze Projet ëmbenennen. An allgemeng Zwecker Programméierungssproochen ass dëst eng trivial Prozedur iwwer déi Dir vläicht net denkt, awer an Terraform musst Dir extrem virsiichteg sinn, soss kënnt Dir Ausbréch erliewen.

    Zum Beispill huet de Webserver-Cluster Modul eng Input Variabel Cluster_Name:

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

    Stellt Iech vir datt Dir ugefaang hutt dëse Modul ze benotzen fir e Mikroservice mam Numm foo z'installéieren. Méi spéit wëllt Dir Äre Service op Bar ëmbenennen. Dës Ännerung kann trivial schéngen, awer a Wierklechkeet kann et Servicestéierunge verursaachen.

    D'Tatsaach ass datt de Webserver-Cluster Modul d'Cluster_name Variabel an enger Rei vu Ressourcen benotzt, dorënner den Nummparameter vun zwou Sécherheetsgruppen an der 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]
    }

    Wann Dir den Nummparameter op enger Ressource ännert, läscht Terraform déi al Versioun vun dëser Ressource an erstellt eng nei op senger Plaz. Awer wann dës Ressource eng ALB ass, tëscht dem Läschen an dem Download vun enger neier Versioun, hutt Dir kee Mechanismus fir de Traffic op Äre Webserver ze redirectéieren. Och wann eng Sécherheetsgrupp geläscht gëtt, fänken Är Serveren un all Netzwierkverkéier ze refuséieren bis en neie Grupp erstallt gëtt.

    Eng aner Aart vu Refactoring, an där Dir interesséiert sidd, ass d'Terraform ID z'änneren. Loosst eis d'aws_security_group Ressource am Webserver-Cluster Modul als Beispill huelen:

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

    Den Identifizéierer vun dëser Ressource gëtt Instanz genannt. Stellt Iech vir datt Dir während der Refactoring décidéiert hutt et op e méi verständlechen (an Ärer Meenung no) Numm cluster_instance z'änneren:

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

    Wat wäert um Enn geschéien? Dat ass richteg: eng Stéierung.

    Terraform assoziéiert all Ressource ID mat der Cloud Provider ID. Zum Beispill ass iam_user mat der AWS IAM Benotzer ID assoziéiert, an aws_instance ass mat der AWS EC2 Server ID assoziéiert. Wann Dir d'Ressource ID ännert (soen vun Instanz zu cluster_instance, wéi de Fall mat aws_security_group), zu Terraform schéngt et wéi wann Dir déi al Ressource geläscht hutt an eng nei bäigefüügt. Wann Dir dës Ännerungen applizéiert, wäert Terraform déi al Sécherheetsgrupp läschen an en neien erstellen, während Är Serveren ufänken all Netzwierkverkéier ze refuséieren.

    Hei sinn véier Schlëssel Lektioune Dir sollt aus dëser Diskussioun ewech huelen.

    • Benotzt ëmmer de Plang Kommando. Et kann all dës Snags opdecken. Iwwerpréift seng Ausgab suergfälteg an oppassen op Situatiounen wou Terraform plangt Ressourcen ze läschen déi héchstwahrscheinlech net geläscht solle ginn.
    • Erstellt ier Dir läscht. Wann Dir eng Ressource ersetzen wëllt, denkt suergfälteg un, ob Dir en Ersatz musst erstellen ier Dir d'Original läscht. Wann d'Äntwert jo ass, kann create_before_destroy hëllefen. Datselwecht Resultat kann manuell erreecht ginn andeems Dir zwee Schrëtt gemaach hutt: als éischt eng nei Ressource an d'Konfiguratioun bäizefügen an d'Uwendungskommando auszeféieren, an dann déi al Ressource aus der Konfiguratioun erofhuelen an d'Befehl applizéieren erëm.
    • Änneren vun Identifizéierer erfuerdert Ännerung vum Staat. Wann Dir d'ID mat enger Ressource verbonne wëllt änneren (zum Beispill, ëmbenennen aws_security_group vun Instanz op cluster_instance) ouni d'Ressource ze läschen an eng nei Versioun dovun ze kreéieren, musst Dir d'Terraform Staatsdatei entspriechend aktualiséieren. Maacht dat ni manuell - benotzt amplaz de terraform Staat Kommando. Wann Dir Identifizéierer ëmbenennt, sollt Dir den Terraform State mv Kommando lafen, deen déi folgend Syntax huet:
      terraform state mv <ORIGINAL_REFERENCE> <NEW_REFERENCE>

      ORIGINAL_REFERENCE ass en Ausdrock deen op d'Ressource a senger aktueller Form bezitt, an NEW_REFERENCE ass wou Dir se réckelen wëllt. Zum Beispill, wann Dir d'aws_security_group Grupp vun Instanz op Cluster_instance ëmbenannt, musst Dir de folgende Kommando ausféieren:

      $ terraform state mv 
         aws_security_group.instance 
         aws_security_group.cluster_instance

      Dëst erzielt Terraform datt de Staat, dee virdru mat aws_security_group.instance verbonne war, elo mat aws_security_group.cluster_instance assoziéiert ginn soll. Wann nom Ëmbenennung a Laf vun dësem Kommando Terraform Plang keng Ännerungen weist, dann hutt Dir alles richteg gemaach.

    • E puer Astellunge kënnen net geännert ginn. D'Parameteren vu ville Ressourcen sinn onverännerbar. Wann Dir probéiert se z'änneren, läscht Terraform déi al Ressource an erstellt eng nei op senger Plaz. All Ressource Säit gëtt normalerweis uginn wat geschitt wann Dir eng bestëmmte Kader änneren, also sécherstellen der Dokumentatioun ze kontrolléieren. Benotzt ëmmer de Plang Kommando a betruecht d'Benotzung vun der create_before_destroy Strategie.

    Deferred Konsequenz ass konsequent ... mat Deferral

    E puer Cloud Provider APIen, sou wéi AWS, sinn asynchron an hunn eng verspéit Konsistenz. Asynchrony heescht datt d'Interface direkt eng Äntwert kann zréckginn ouni op déi ugefrote Handlung ze waarden. Verspéidt Konsequenz bedeit datt Ännerungen Zäit daueren kënnen fir am ganze System ze propagéieren; wärend dëst geschitt, Är Äntwerte kënnen inkonsistent sinn an ofhängeg vun wéi enger Datequelle Replik op Är API-Uriff reagéiert.

    Stellt Iech vir, zum Beispill, datt Dir en API Uruff un AWS mécht a frot et en EC2 Server ze kreéieren. D'API wäert eng "erfollegräich" Äntwert (201 Erstellt) bal direkt zréckginn, ouni ze waarden op de Server selwer erstallt ze ginn. Wann Dir probéiert et direkt ze verbannen, wäert et bal sécher falen, well zu deem Zäitpunkt ass AWS nach ëmmer Ressourcen initialiséiert oder, alternativ, de Server ass nach net gestart. Ausserdeem, wann Dir en aneren Uruff maacht fir Informatiounen iwwer dëse Server ze kréien, kënnt Dir e Feeler kréien (404 Not Found). D'Saach ass datt d'Informatioun iwwer dësen EC2 Server nach ëmmer duerch AWS propagéiert ka ginn ier se iwwerall verfügbar ass, Dir musst e puer Sekonnen waarden.

    Wann Dir eng asynchron API mat fauler Konsequenz benotzt, musst Dir Är Ufro periodesch nei probéieren bis d'Aktioun fäerdeg ass a propagéiert duerch de System. Leider bitt d'AWS SDK keng gutt Tools fir dëst, an den Terraform Projet huet fréier vu villen Käfere gelidden wéi 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

    An anere Wierder, Dir erstellt eng Ressource (wéi e Subnet) a probéiert dann e puer Informatioun doriwwer ze kréien (wéi d'ID vum nei erstallten Subnet), an Terraform kann et net fannen. Déi meescht vun dëse Käfere (inklusiv 6813) goufen fixéiert, awer si kommen ëmmer nach vun Zäit zu Zäit op, besonnesch wann Terraform Ënnerstëtzung fir en neie Ressourcentyp bäidréit. Dëst ass lästeg, awer an de meeschte Fäll verursaacht kee Schued. Wann Dir nach eng Kéier terraform leeft, sollt alles funktionnéieren, well bis dës Zäit d'Informatioun schonn am ganze System verbreet ass.

    Dësen Extrait gëtt aus dem Buch vum Evgeniy Brikman presentéiert "Terraform: Infrastruktur um Code Niveau".

Source: will.com

Kaaft zouverlässeg Hosting fir Site mat DDoS Schutz, VPS VDS Server 🔥 Kaaft zouverléissegt Websäithosting mat DDoS-Schutz, VPS VDS Server | ProHoster