Vi kommer att lyfta fram nÄgra fallgropar, inklusive de som rör cykler, if-uttalanden och distributionsmetoder, samt mer allmÀnna problem som rör Terraform i stort:
- parametrarna count och for_each har begrÀnsningar;
- begrÀnsningar för distributioner med noll avbrottstid;
- Àven en bra plan kan misslyckas;
- refactorering kan ha sina fÀllor;
- fördröjd konsistens stÀmmer överens... med fördröjning.
Parametrarna count och for_each har begrÀnsningar
I exemplen i detta kapitel tillÀmpas parametern count och uttrycket for_each aktivt i cykler och logik. De fungerar vÀl, men det finns tvÄ viktiga begrÀnsningar som man mÄste vara medveten om.
- I count och for_each kan man inte referera till nÄgra utdata frÄn resursvariabler.
- count och for_each kan inte anvÀndas i modulkonfigurationer.
I count och for_each kan man inte referera till nÄgra utdata frÄn resursvariabler
FörestÀll dig att du behöver distribuera flera EC2-servrar och av nÄgon anledning inte vill anvÀnda ASG. Din kod kan se ut sÄ hÀr:
resource "aws_instance" "example_1" {
count = 3
ami = "ami-0c55b159cbfafe1f0"
instance_type = "t2.micro"
}
LÄt oss titta pÄ dem ett och ett.
Eftersom parametern count har tilldelats ett statiskt vÀrde kommer den hÀr koden att fungera utan problem: nÀr du kör kommandot apply kommer det att skapa tre EC2-servrar. Men om du vill distribuera en server i varje tillgÀnglighetszon (Availability Zone eller AZ) inom den aktuella AWS-regionen? Du kan göra sÄ att din kod laddar listan över zoner frÄn datakÀllan aws_availability_zones och sedan "cykliskt" gÄr igenom var och en av dem och skapar en EC2-server i varje zon, genom att anvÀnda parametern count och Ätkomst till arrayen via index:
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" {}Den hÀr koden kommer ocksÄ att fungera perfekt, eftersom parametern count problemfritt kan referera till datakÀllor. Men vad hÀnder om antalet servrar du behöver skapa beror pÄ utdata frÄn en viss resurs? För att demonstrera detta Àr det enklast att ta resursen random_integer, som, som namnet antyder, returnerar ett slumpmÀssigt heltal:
resource "random_integer" "num_instances" {
min = 1
max = 3
}Den hÀr koden genererar ett slumpmÀssigt tal mellan 1 och 3. LÄt oss se vad som hÀnder om vi försöker anvÀnda utdata frÄn result frÄn den hÀr resursen i parametern count för resursen aws_instance:
resource "aws_instance" "example_3" {
count = random_integer.num_instances.result
ami = "ami-0c55b159cbfafe1f0"
instance_type = "t2.micro"
}Om du kör terraform plan för den hÀr koden fÄr du följande fel:
Error: Ogiltigt count-argument
pÄ main.tf rad 30, i resurs "aws_instance" "example_3":
30: count = random_integer.num_instances.result
VÀrdet för "count" beror pÄ resursattribut som inte kan bestÀmmas förrÀn tillÀmpningen, sÄ Terraform kan inte förutsÀga hur mÄnga instanser som kommer att skapas. För att lösa detta, anvÀnd argumentet -target för att först tillÀmpa endast de resurser som count beror pÄ.Terraform krÀver att count och for_each berÀknas i planeringssteget, före skapandet eller Àndringen av nÄgra resurser. Det innebÀr att count och for_each kan hÀnvisa till litteraler, variabler, datakÀllor och till och med resurslistor (förutsatt att deras lÀngd kan faststÀllas under planeringen), men inte till berÀknade utdataresursvariabler.
count och for_each kan inte anvÀndas i modulkonfigurationen
Du kan ibland frestas att lÀgga till parameter count i modulkonfigurationerna:
module "count_example" {
source = "..\/..\/..\/..\/modules\/services\/webserver-cluster"
count = 3
cluster_name = "terraform-up-and-running-example"
server_port = 8080
instance_type = "t2.micro"
}Den hÀr koden försöker anvÀnda count inne i modulen för att skapa tre kopior av resursen webserver-cluster. Eller sÄ kanske du vill göra modulkopplingen valfri beroende pÄ nÄgot boolean-villkor genom att tilldela parameter count vÀrdet 0. SÄdan kod kan verka rimlig, men som ett resultat av att köra terraform plan kommer du fÄ följande fel:
Error: Reservat argumentnamn i modulblock
pÄ main.tf rad 13, i modul "count_example":
13: count = 3
Namnet "count" Àr reserverat för anvÀndning i en framtida version av Terraform.TyvÀrr, vid lanseringen av Terraform 0.12.6 stöds inte anvÀndning av count eller for_each i resursmodulen. Enligt release-noterna för Terraform 0.12 (http://bit.ly/3257bv4) planerar HashiCorp att lÀgga till denna funktionalitet i framtiden, sÄ beroende pÄ nÀr du lÀser den hÀr boken kan den redan vara tillgÀnglig. För att veta sÀkert, .
BegrÀnsningar för distributioner med noll nedtid
AnvÀndningen av create_before_destroy-blocket i kombination med ASG Àr en utmÀrkt lösning för att organisera distributioner med noll driftstopp, med ett undantag: autoskalningsregler stöds inte. Eller för att vara mer exakt, detta ÄterstÀller storleken pÄ ASG till min_size vid varje distribution, vilket kan bli ett problem om du har anvÀnt autoskalningsregler för att öka antalet aktiva servrar.
Till exempel innehÄller modulen webserver-cluster ett par resurser aws_autoscaling_schedule som klockan 9 ökar antalet servrar i klustret frÄn tvÄ till tio. Om du utför en distribution, sÀg klockan 11, kommer den nya ASG-gruppen att laddas med endast tvÄ servrar istÀllet för tio och förbli i detta tillstÄnd till klockan 9 nÀsta dag.
Denna begrÀnsning kan kringgÄs pÄ flera sÀtt.
- Ăndra parametern recurrence i aws_autoscaling_schedule frĂ„n 0 9 * * * ("körs klockan 9") till nĂ„got som 0-59 9-17 * * * ("körs varje minut frĂ„n klockan 9 till 17"). Om det redan finns tio servrar i ASG kommer att Ă„terutföra denna autoskalningsregel inte att Ă€ndra nĂ„got, vilket Ă€r vad vi vill. Men om ASG-gruppen just har distribuerats garanterar denna regel att antalet servrar inom en minut nĂ„r tio. Det Ă€r inte den mest eleganta metoden, och stora hopp frĂ„n tio till tvĂ„ servrar och tillbaka kan ocksĂ„ orsaka problem för anvĂ€ndarna.
- Skapa ett anvÀndarscript som anvÀnder AWS API för att bestÀmma antalet aktiva servrar i ASG, anropa det med en extern datakÀlla (se avsnitt "Extern datakÀlla" pÄ s. 249) och tilldela parameter desired_capacity för ASG-gruppen det vÀrde som returneras av detta script. PÄ sÄ sÀtt kommer varje ny ASG-instans alltid att starta med samma kapacitet, vilket gör vÄr Terraform-kod mer komplex att underhÄlla.
SjÀlvklart, i idealfallet borde Terraform ha inbyggt stöd för distributioner med noll driftstopp, men i maj 2019 planerade HashiCorp-teamet inte att lÀgga till denna funktionalitet ().
En korrekt plan kan misslyckas med att implementeras
Ibland, nÀr du kör kommandot plan, fÄr du en helt rimlig distributionsplan, men kommandot apply returnerar ett felmeddelande. Försök till exempel att lÀgga till resursen aws_iam_user med samma namn som du anvÀnde för IAM-anvÀndaren som du skapade tidigare i kapitel 2:
resource "aws_iam_user" "existing_user" {
# Ange namnet pÄ den redan existerande IAM-anvÀndaren hÀr,
# för att öva pÄ att anvÀnda kommandot terraform import
name = "yevgeniy.brikman"
}Nu, om du kör kommandot plan, kommer Terraform att visa en till synes rimlig distributionsplan:
Terraform kommer att utföra följande ÄtgÀrder:
# aws_iam_user.existing_user kommer att skapas
+ resource "aws_iam_user" "existing_user" {
+ arn = (kÀnt efter apply)
+ force_destroy = false
+ id = (kÀnt efter apply)
+ name = "yevgeniy.brikman"
+ path = "\/"
+ unique_id = (kÀnt efter apply)
}
Plan: 1 att lÀgga till, 0 att förÀndra, 0 att förstöra.Om du kör kommandot apply fÄr du följande fel:
Error: Error creating IAM User yevgeniy.brikman: EntityAlreadyExists:
User with name yevgeniy.brikman already exists.
pÄ main.tf rad 10, i resource "aws_iam_user" "existing_user":
10: resource "aws_iam_user" "existing_user" {Problemet Àr uppenbarligen att en IAM-anvÀndare med det namnet redan finns. Och detta kan hÀnda, inte bara med IAM-anvÀndare, utan ocksÄ med nÀstan vilken resurs som helst. Kanske har nÄgon skapat denna resurs manuellt eller med hjÀlp av kommandoraden, men hur som helst, en identitetskonflikt leder till problem. Det finns mÄnga varianter av detta fel som ofta överraskar nybörjare i Terraform.
Den avgörande punkten Àr att kommandot terraform plan endast tar hÀnsyn till de resurser som anges i Terraform-statusfilen. Om resurser skapats pÄ nÄgot annat sÀtt (t.ex. manuellt, genom att klicka i AWS-konsolen) kommer de inte att hamna i statusfilen och dÀrför kommer Terraform inte att ta hÀnsyn till dem vid körning av kommandot plan. Resultatet blir att en till synes korrekt plan visar sig vara felaktig.
FrÄn detta kan man dra tvÄ lÀrdomar.
- Om du redan har börjat arbeta med Terraform, anvÀnd inte nÄgot annat. Om en del av din infrastruktur hanteras med Terraform, fÄr den inte lÀngre Àndras manuellt. Annars riskerar du inte bara att fÄ konstiga felmeddelanden i Terraform, utan du underminerar ocksÄ mÄnga av fördelarna med IaC, eftersom koden inte lÀngre kommer att vara en exakt representation av din infrastruktur.
- Om du redan har nÄgon infrastruktur, anvÀnd kommandot import. Om du börjar anvÀnda Terraform med redan existerande infrastruktur kan den lÀggas till i tillstÄndsfilen med kommandot terraform import. DÄ kommer Terraform att veta vilken infrastruktur som ska hanteras. Kommandot import tar tvÄ argument. Den första Àr adressen till resursen i dina konfigurationsfiler. HÀr Àr samma syntax som i referenser till resurser: _. (som aws_iam_user.existing_user). Det andra argumentet Àr ID:t för den resurs som ska importeras. LÄt oss sÀga att för resurser av typen aws_iam_user Àr anvÀndarnamnet (t.ex. yevgeniy.brikman) ID:t, medan för resurser av typen aws_instance Àr serverns identifierare EC2 (som i-190e22e5). Hur man importerar en resurs anges vanligtvis i dokumentationen lÀngst ner pÄ dess sida.
Nedanför visas kommandot import som möjliggör synkronisering av resursen aws_iam_user som du har lagt till i din Terraform-konfiguration tillsammans med IAM-anvÀndaren frÄn kapitel 2 (naturigtvis behöver du ersÀtta yevgeniy.brikman med ditt namn):
$ terraform import aws_iam_user.existing_user yevgeniy.brikmanTerraform kommer att kontakta AWS API för att hitta din IAM-anvÀndare och skapa en koppling mellan den och resursen aws_iam_user.existing_user i din Terraform-konfiguration i tillstÄndsfilen. FrÄn och med nu kommer Terraform att veta att IAM-anvÀndaren redan finns nÀr kommandot plan körs, och kommer inte att försöka skapa den igen.
Det bör noteras att om du redan har mÄnga resurser som du vill importera till Terraform, sÄ kan det bli en besvÀrlig uppgift att skriva koden manuellt och importera dem en och en. DÀrför bör du övervÀga att anvÀnda ett verktyg som Terraforming (http://terraforming.dtan4.net/), som kan automatiskt importera kod och tillstÄnd frÄn ditt AWS-konto.
Refaktorering kan ha sina fÀllor.
Refaktorering Àr en vanlig praxis inom programmering dÀr du Àndrar den interna strukturen i koden utan att pÄverka det externa beteendet. Detta görs för att göra koden mer förstÄelig, ordnad och lÀttare att underhÄlla. Refaktorering Àr en oumbÀrlig metodik som bör tillÀmpas regelbundet. Men nÀr det gÀller Terraform eller nÄgot annat verktyg för IaC, Àr det viktigt att vara extremt försiktig med vad som menas med "externt beteende" hos en koddel, annars kan oförutsedda problem uppstÄ.
Till exempel Àr en vanlig typ av refaktorering att byta namn pÄ variabler eller funktioner till mer begripliga namn. MÄnga IDE:er har inbyggt stöd för refaktorering och kan automatiskt byta namn pÄ variabler och funktioner inom hela projektet. I allmÀnna programmeringssprÄk Àr detta en trivial procedur som man kan bortse ifrÄn, men i Terraform mÄste man vara extremt försiktig, annars kan det leda till driftstopp.
Till exempel har modulen webserver-cluster en ingÄngsvariabel cluster_name:
variable "cluster_name" { description = "Namnet som ska anvÀndas för alla klusterresurser" type = string }FörestÀll dig att du har börjat anvÀnda denna modul för att distribuera en mikrotjÀnst med namnet foo. Senare vill du döpa om din tjÀnst till bar. Denna Àndring kan verka trivial, men i verkligheten kan det leda till driftstopp.
Det beror pÄ att modulen webserver-cluster anvÀnder variabeln cluster_name i ett antal resurser, inklusive namnet pÄ tvÄ sÀkerhetsgrupper och 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] }Om du Àndrar namnet pÄ en resurs, kommer Terraform att ta bort den gamla versionen av resursen och skapa en ny. Men om en sÄdan resurs Àr ALB, kommer du under tiden mellan borttagningen av den gamla och laddningen av den nya versionen inte att ha nÄgot sÀtt att omdirigera trafiken till din webbserver. PÄ samma sÀtt, om en sÀkerhetsgrupp tas bort, kommer dina servrar att börja avvisa all nÀtverks trafik tills en ny grupp har skapats.
En annan typ av refaktorering som kan intressera dig Àr att Àndra Terraform-ID:t. LÄt oss ta resurserna aws_security_group i modulen webserver-cluster som exempel:
resource "aws_security_group" "instance" { # (...) }ID:t för den hÀr resursen kallas instance. FörestÀll dig att du under refaktoreringen bestÀmmer dig för att byta det till ett mer begripligt (enligt din Äsikt) namn, cluster_instance:
resource "aws_security_group" "cluster_instance" { # (...) }Vad hÀnder dÄ? RÀtt: driftstopp.
Terraform kopplar varje resurs ID med leverantörens ID. Till exempel kopplas iam_user till IAM-anvÀndarens ID i AWS, och aws_instance kopplas till ID för AWS EC2-servern. Om resursens ID Àndras (sÀg frÄn instance till cluster_instance, som i fallet med aws_security_group), kommer Terraform att se detta som om du har tagit bort den gamla resursen och lagt till en ny. Om dessa Àndringar tillÀmpas kommer Terraform att ta bort den gamla sÀkerhetsgruppen och skapa en ny, vilket innebÀr att dina servrar börjar avvisa all nÀtverkstrafik.
HÀr Àr fyra viktiga lektioner du bör ta med dig frÄn denna diskussion.
- AnvÀnd alltid kommandot plan. Det lÄter dig identifiera alla dessa problem. GÄ noggrant igenom dess utdata och var uppmÀrksam pÄ situationer dÀr Terraform planerar att ta bort resurser som troligen inte bör tas bort.
- Skapa innan du tar bort. Om du vill ersÀtta en resurs, tÀnk noga pÄ om det Àr nödvÀndigt att skapa en ersÀttning innan du tar bort originalet. Om svaret Àr ja kan create_before_destroy hjÀlpa till. Du kan uppnÄ samma resultat manuellt genom att utföra tvÄ steg: först lÀgga till den nya resursen i konfigurationen och köra kommandot apply, och sedan ta bort den gamla resursen frÄn konfigurationen och köra kommandot apply igen.
- Ăndring av ID:n krĂ€ver en Ă€ndring av tillstĂ„ndet. Om du vill Ă€ndra ID:t relaterat till en resurs (till exempel byta namn pĂ„ aws_security_group frĂ„n instance till cluster_instance), utan att ta bort resursen och skapa en ny version av den, mĂ„ste du uppdatera Terraform-tillstĂ„ndsfilerna pĂ„ rĂ€tt sĂ€tt. Gör detta aldrig manuellt â anvĂ€nd istĂ€llet kommandot terraform state. Vid ombenĂ€mning av ID:n ska du köra kommandot terraform state mv, som har följande syntax:
terraform state mvORIGINAL_REFERENCE Àr ett uttryck som refererar till resursen i dess nuvarande form, medan NEW_REFERENCE Àr platsen dit du vill flytta den. Till exempel, vid ombenÀmning av aws_security_group frÄn instance till cluster_instance behöver du köra följande kommando:
$ terraform state mv aws_security_group.instance aws_security_group.cluster_instanceSÄ kommer du att meddela Terraform att tillstÄndet som tidigare gÀllde aws_security_group.instance nu ska kopplas till aws_security_group.cluster_instance. Om terraform plan inte visar nÄgra Àndringar efter att du har döpt om detta och kört kommandot, sÄ har du gjort rÀtt.
- Vissa parametrar kan inte Àndras. MÄnga resursparametrar Àr oförÀnderliga. Om du försöker Àndra dem kommer Terraform att ta bort den gamla resursen och skapa en ny istÀllet. PÄ varje resursens sida anges vanligtvis vad som hÀnder om en viss parameter Àndras, sÄ glöm inte att kolla dokumentationen. AnvÀnd alltid kommandot plan och övervÀg om det Àr lÀmpligt att anvÀnda strategin create_before_destroy.
Fördröjd konsistens överensstÀmmer⊠med fördröjningen
API:er frÄn vissa molnleverantörer, som AWS, Àr asynkrona och har fördröjd konsistens. Asynkronitet innebÀr att grÀnssnittet kan returnera ett svar omedelbart utan att vÀnta pÄ att den begÀrda ÄtgÀrden ska slutföras. Fördröjd konsistens betyder att det kan ta tid att sprida Àndringar i hela systemet; under tiden kan dina svar vara inkonsekventa och bero pÄ vilken replik av datakÀllan som svarar pÄ dina API-anrop.
TÀnk dig till exempel att du gör ett API-anrop till AWS för att skapa en EC2-server. API:et kommer att returnera ett "lyckat" svar (201 Created) nÀstan omedelbart, utan att vÀnta pÄ att sjÀlva servern ska skapats. Om du omedelbart försöker att ansluta till den kommer det nÀstan sÀkert att misslyckas, eftersom AWS fortfarande initierar resurserna eller alternativt att servern har Ànnu inte startat. Dessutom, om du gör ett annat anrop för att fÄ information om denna server, kan det komma ett felmeddelande (404 Not Found). Anledningen Àr att informationen om denna EC2-server fortfarande kan spridas genom AWS, och det kan ta ett par sekunder innan den blir tillgÀnglig överallt.
Vid varje anvÀndning av asynkront API med fördröjd konsistens mÄste du regelbundet upprepa din begÀran tills ÄtgÀrden Àr slutförd och har spridits i systemet. TyvÀrr tillhandahÄller AWS SDK inga bra verktyg för detta, och Terraform-projektet har tidigare lidit av mÄnga fel som exempelvis 6813 (https://github.com/hashicorp/terraform/issues/6813):
$ terraform apply aws_subnet.private-persistence.2: InvalidSubnetID.NotFound: SubnÀt-ID 'subnet-xxxxxxx' finns inteMed andra ord skapar du en resurs (till exempel ett subnÀt) och försöker sedan hÀmta information om det (som ID för det nyligen skapade subnettet), men Terraform kan inte hitta den. De flesta av sÄdana fel (inklusive 6813) har redan ÄtgÀrdats, men de dyker fortfarande upp dÄ och dÄ, sÀrskilt nÀr Terraform fÄr stöd för en ny typ av resurser. Det Àr frustrerande, men i de flesta fall Àr det ofarligt. Vid en ny körning av terraform apply bör allt fungera, eftersom informationen dÄ redan har spridits i systemet.
Detta utdrag Àr hÀmtat frÄn boken av Evgeny Birman .
KĂ€lla: habr.com
