Modul 10 — Infrastructure as Code

49. Terraform moduly a správa více prostředí

Jak zabalit opakující se Terraform konfiguraci do znovupoužitelných modulů a jak spravovat oddělená prostředí (dev/staging/produkce) pomocí adresářů nebo workspaces.

Odhadovaná délka studia: 55 minut · Stav: nehotovo

Technologie: Terraform

Úvod a kontext

Minulá lekce ukázala základní stavební bloky Terraformu na jednom
zdroji v jednom souboru. Reálné projekty ale typicky potřebují stejnou
sadu zdrojů (např. "webová aplikace = load balancer + instance +
databáze") zopakovat pro víc prostředí — vývojové, testovací,
produkční — a nechtějí přitom kopírovat stejný HCL kód třikrát. K tomu
slouží moduly a strategie organizace více prostředí.

Teorie

Moduly

Modul je adresář s Terraform konfigurací, na který se lze odkázat
z jiné konfigurace a parametrizovat ho proměnnými — obdoba funkce
v programovacím jazyce. Každá Terraform konfigurace je sama o sobě
modul (tzv. root module), moduly se ale typicky píšou tak, aby byly
volatelné i z jiných konfigurací.

Struktura znovupoužitelného modulu pro webovou aplikaci:

modules/web-app/
├── main.tf       # resources
├── variables.tf   # vstupní parametry
└── outputs.tf     # exportované hodnoty
# modules/web-app/variables.tf
variable "environment" {
  type        = string
  description = "Název prostředí (dev, staging, production)"
}

variable "instance_type" {
  type        = string
  default     = "t3.micro"
}

# modules/web-app/main.tf
resource "aws_instance" "web" {
  ami           = "ami-0c55b159cbfafe1f0"
  instance_type = var.instance_type

  tags = {
    Name        = "web-${var.environment}"
    Environment = var.environment
  }
}

# modules/web-app/outputs.tf
output "public_ip" {
  value = aws_instance.web.public_ip
}

Volání modulu ze skutečné konfigurace prostředí:

module "web_app" {
  source        = "../../modules/web-app"
  environment   = "staging"
  instance_type = "t3.small"
}

output "staging_ip" {
  value = module.web_app.public_ip
}

Zdroj modulu (source) může být místní cesta, ale i Git repozitář nebo
veřejný Terraform Registry modul (source = "terraform-aws-modules/vpc/ aws"), což umožňuje sdílet moduly i mezi týmy či firmami, podobně jako
balíčky v programovacím jazyce.

Verzování modulů

Podobně jako u CI/CD šablon z minulého modulu je u modulů z externích
zdrojů vhodné pinovat konkrétní verzi:

module "vpc" {
  source  = "terraform-aws-modules/vpc/aws"
  version = "5.5.1"
  # ...
}

Strategie více prostředí: adresář na prostředí

Nejjednodušší a nejtransparentnější přístup je mít pro každé prostředí
samostatný adresář s vlastní konfigurací (a vlastním state souborem),
který ale volá stejné sdílené moduly:

envs/
├── dev/
│   └── main.tf        # module "web_app" { environment = "dev" ... }
├── staging/
│   └── main.tf        # module "web_app" { environment = "staging" ... }
└── production/
    └── main.tf        # module "web_app" { environment = "production" ... }
modules/
└── web-app/
    ├── main.tf
    ├── variables.tf
    └── outputs.tf

Výhody: naprosto jasné oddělení — chyba nebo apply ve staging
nemůže nijak zasáhnout production, protože jde o zcela oddělené state
soubory i adresáře; snadné mít pro produkci přísnější schvalovací proces
v CI. Nevýhoda: drobná duplicita konfigurace mezi adresáři prostředí
(mitigovaná tím, že sdílená logika je v modulu, ne v adresářích
prostředí).

Strategie více prostředí: workspaces

Terraform nabízí i vestavěný mechanismus workspaces — jeden adresář
s konfigurací, ale více pojmenovaných instancí state:

terraform workspace new staging
terraform workspace new production
terraform workspace select staging
terraform apply   # aplikuje se do state "staging"

V konfiguraci lze na aktuální workspace odkázat přes terraform.workspace:

resource "aws_instance" "web" {
  instance_type = terraform.workspace == "production" ? "t3.medium" : "t3.micro"
  tags = {
    Name = "web-${terraform.workspace}"
  }
}

Výhody: méně duplicitních souborů, rychlé přepínání. Nevýhody:
snadné omylem apply do špatného workspace, protože je to jen
přepínač stavu v jednom adresáři, ne fyzicky oddělené soubory — chybí
vizuální rozdíl mezi prostředími v code review. Proto se pro produkčně
kritické, zásadně odlišné prostředí (typicky produkce vs. zbytek) často
dává přednost oddělení adresářem, zatímco workspaces se hodí spíš pro
krátkodobá, podobná prostředí (např. review prostředí pro jednotlivé
pull requesty).

Praktický příklad

Kompletní ukázka adresářové struktury se sdíleným modulem a dvěma
prostředími, včetně odlišné konfigurace backendu (state) pro každé:

# envs/staging/main.tf
terraform {
  backend "s3" {
    bucket = "firma-tfstate"
    key    = "web-app/staging/terraform.tfstate"
    region = "eu-central-1"
  }
}

module "web_app" {
  source        = "../../modules/web-app"
  environment   = "staging"
  instance_type = "t3.micro"
}
# envs/production/main.tf
terraform {
  backend "s3" {
    bucket = "firma-tfstate"
    key    = "web-app/production/terraform.tfstate"
    region = "eu-central-1"
  }
}

module "web_app" {
  source        = "../../modules/web-app"
  environment   = "production"
  instance_type = "t3.medium"
}

Nasazení do konkrétního prostředí se pak dělá vstupem do jeho adresáře:

cd envs/staging
terraform init
terraform apply

Shrnutí

Moduly zabalí opakovanou sadu zdrojů do parametrizovatelné, znovu
volatelné jednotky — obdoba funkce. Pro správu více prostředí (dev/
staging/produkce) existují dva hlavní přístupy: samostatný adresář
(a state) na prostředí, který dává silné, explicitní oddělení a je
bezpečnější pro produkci, a vestavěné workspaces, které jsou rychlejší
na přepínání, ale nesou riziko omylem aplikovat změnu do špatného
prostředí. V praxi se oba přístupy i kombinují podle kritičnosti
prostředí.

Kontrolní otázky

  1. Čím je Terraform modul podobný funkci v programovacím jazyce?
  2. Jaké jsou hlavní výhody a nevýhody strategie "adresář na prostředí"
    oproti workspaces?
  3. Proč je vhodné u externích modulů (z registru nebo Git repozitáře)
    pinovat konkrétní verzi?
  4. Mini cvičení: navrhněte (jen popisem, ne kódem) strukturu modulu pro
    "databázi jako službu" (např. aws_db_instance) s proměnnými pro
    velikost instance a název prostředí, a výstupem s connection stringem.

Lekce na sebe nejsou zamčené — libovolnou lekci můžete otevřít i označit jako hotovou v jakémkoliv pořadí.