La lliçó anterior va acabar assenyalant una esquerda incòmoda: cd.yml desplega tot sol, però dona per fet que existeixen un clúster reservalia-dev, un servei reservalia-api, una base de dades RDS, un ALB i uns rols IAM que la Nuria va crear a mà a la consola d'AWS fa mesos. L'única font de veritat sobre en què es diferencien staging i prod és el que hi ha dins d'AWS, i ningú no ho ha llegit sencer. Aquesta lliçó tanca aquella esquerda: veurem per què un desplegament automatitzat sobre infraestructura artesanal continua sent fràgil, què és exactament la Infraestructura com a Codi, i com el mòdul infra/ de Reservalia s'escriu una sola vegada en Terraform i s'instancia tres vegades perquè els tres entorns surtin literalment del mateix codi, amb les seves diferències escrites com a valors explícits en lloc de misteris.

Contingut

  1. El problema: deriva de configuració i el "a staging funcionava"
  2. Què és la Infraestructura com a Codi i els seus quatre principis
  3. Panorama d'eines
  4. El mòdul infra/ de Reservalia en Terraform
  5. Un mòdul, tres entorns: les diferències com a valors
  6. Estat remot i bloqueig: per què l'estat local és un accident esperant
  7. El pipeline d'infraestructura: plan al PR, apply a main
  8. Riscos reals: recreacions destructives, plans que menteixen i permisos excessius
  9. Entorns efímers de previsualització per pull request
  10. Errors Comuns i Consells
  11. Exercicis
  12. Conclusió

  1. El problema: deriva de configuració i el "a staging funcionava"

Fem l'exercici incòmode. La Marta pregunta en una reunió: "En què es diferencia staging de prod?". La Nuria respon que prod té quatre tasques i és Multi-AZ; en Diego es pensa que la retenció de logs de staging és d'una setmana, "o de tres dies, no me'n recordo". La Marta insisteix: "I el grup de seguretat de la base de dades? I el temps d'espera de l'ALB?". Silenci.

Això és deriva de configuració (configuration drift): la diferència acumulada i indocumentada entre entorns que s'haurien d'assemblar. No apareix de cop; es construeix a base d'arreglaments urgents. Un dimarts de fa vuit mesos, prod donava timeouts i la Nuria va apujar l'idle_timeout de l'ALB de 60 a 120 segons des de la consola. Va funcionar, l'incident es va tancar i ningú no va tocar staging. Avui hi ha una petició lenta que a staging talla als 60 segons i a prod no. O a l'inrevés, que encara és pitjor.

El resultat clàssic té nom propi: "a staging funcionava". I arrossega una conseqüència que trenca tot el mòdul: si staging no és equivalent a prod, aleshores passar per staging no autoritza res. El requisit 1 de la lliçó 03-01 —una suite de proves en la qual es confia— s'esfondra, no perquè les proves siguin dolentes, sinó perquè s'executen en un lloc que no representa el destí. La infraestructura creada a mà té a més quatre defectes que cap quantitat de disciplina no corregeix: no és revisable (ningú no pot comentar un canvi en un pull request abans que passi), no és reproduïble (recrear l'entorn en una altra regió vol dir reconstruir-lo de memòria), no és auditable (CloudTrail diu qui va tocar alguna cosa, però no per què ni contra quin disseny) i no és reversible (no hi ha cap "estat anterior" al qual tornar: la consola no té git revert).

Nuria: "Un desplegament que no es pot desfer en cinc minuts no és un desplegament, és una aposta. I un entorn que no es pot recrear en una hora tampoc no és un entorn: és una relíquia."

  1. Què és la Infraestructura com a Codi i els seus quatre principis

Infraestructura com a Codi (IaC) és descriure els recursos d'infraestructura —xarxes, bases de dades, clústers, permisos— en fitxers de text versionats, i deixar que una eina els creï i els mantingui. La infraestructura passa a ser un artefacte de programari: es revisa en un pull request, es prova, es versiona i es reverteix.

Principi 1: declaratiu, no imperatiu. Un enfocament imperatiu descriu passos ("crea un clúster, després afegeix un servei, després puja les tasques a quatre"). Un enfocament declaratiu descriu el resultat ("existeix un clúster amb un servei de quatre tasques") i l'eina calcula què cal fer per arribar-hi des d'on sigui.

Imperatiu (un script bash amb aws cli) Declaratiu (Terraform)
Què escrius La seqüència de passos L'estat final desitjat
Si s'executa dues vegades Sol fallar o duplicar No fa res: ja coincideix
Si algú ha tocat alguna cosa a mà L'script no se n'assabenta Ho detecta i ho corregeix
Llegibilitat Cal simular l'execució mentalment Es llegeix com una descripció del sistema

Principi 2: idempotència. Ja va aparèixer a la 03-02 parlant de desplegaments, i aquí encara és més central: aplicar la mateixa configuració deu vegades deixa el sistema igual que aplicar-la una. Això és el que permet executar terraform apply a cada merge sense por.

Principi 3: estat. Per saber què cal canviar, l'eina necessita recordar què va crear. Terraform desa un fitxer d'estat que mapeja cada bloc del codi amb el seu recurs real a AWS. No és un detall d'implementació: és l'actiu més delicat del sistema, i per això té el seu propi apartat (el 6).

Principi 4: pla abans d'aplicar. Abans de tocar res, l'eina calcula i mostra el diferencial entre el que hi ha i el que es demana. Aquest pla és el que converteix un canvi d'infraestructura en una cosa revisable, i és la peça que enganxa la IaC amb el pipeline.

  1. Panorama d'eines

Eina Model Llenguatge Àmbit Quan encaixa
Terraform / OpenTofu Declaratiu, amb estat HCL Multiproveïdor Estàndard de facto; OpenTofu és la bifurcació lliure després del canvi de llicència
AWS CloudFormation Declaratiu, estat gestionat per AWS YAML/JSON Només AWS Si no surts d'AWS i no vols gestionar l'estat
AWS CDK Imperatiu que genera CloudFormation TypeScript, Python… Només AWS Equips que prefereixen un llenguatge real i abstraccions pròpies
Pulumi Declaratiu amb llenguatge general TypeScript, Go… Multiproveïdor Com CDK però sense lligar-se a AWS
Ansible Procedimental idempotent, sense estat YAML Configuració dins de màquines Servidors que ja existeixen; complementa, no substitueix

La distinció que més confon al principi: aprovisionar (crear la màquina, la xarxa, la base de dades) no és el mateix que configurar (instal·lar paquets dins d'una màquina que ja existeix). Terraform fa el primer; Ansible, el segon. Reservalia gairebé no necessita el segon, perquè les seves càrregues van en contenidors i la "configuració de la màquina" ja és al Dockerfile de la 02-03. Reservalia tria Terraform: és multiproveïdor, el seu ecosistema de mòduls és el més ampli i l'equip ja sap llegir HCL. El codi d'aquesta lliçó funciona igual amb OpenTofu canviant el binari.

  1. El mòdul infra/ de Reservalia en Terraform

L'estructura que la Nuria crea al monorepo:

infra/
├── moduls/entorn/          # UN modul: main.tf · variables.tf · outputs.tf
└── entorns/
    ├── dev/main.tf         # instancia el modul amb els valors de dev
    ├── staging/main.tf
    └── prod/main.tf

Un mòdul de Terraform és una carpeta amb codi parametritzable, com una funció: infra/moduls/entorn/ descriu "un entorn de Reservalia" i no sap si és dev o prod, perquè rep aquestes dades per variables.

# infra/moduls/entorn/variables.tf
variable "entorn" {
  description = "Nom de l entorn: dev, staging o prod"
  type        = string
  validation {                                     # 1
    condition     = contains(["dev", "staging", "prod"], var.entorn)
    error_message = "L entorn ha de ser dev, staging o prod."
  }
}

variable "nombre_tasques"   { type = number }
variable "cpu_tasca"        { type = number, default = 512 }
variable "memoria_tasca"    { type = number, default = 1024 }
variable "classe_db"        { type = string }
variable "db_multi_az"      { type = bool,   default = false }
variable "retencio_logs"    { type = number, default = 7 }
variable "versio_postgres"  { type = string, default = "16.3" }   # 2
  1. El bloc validation converteix un error de dit en una fallada del plan amb un missatge clar, en lloc de crear un entorn anomenat prodd.
  2. versio_postgres té valor per defecte i cap entorn no el canvia. Això no és casualitat: és el requisit 3 de la 03-01 escrit en codi. Si demà algú volgués provar PostgreSQL 17 a staging, ho hauria d'escriure explícitament en un pull request, i la revisió preguntaria per què.

El cos del mòdul, amb l'essencial d'un entorn de Reservalia:

# infra/moduls/entorn/main.tf
locals {
  nom       = "reservalia-${var.entorn}"                     # 1
  etiquetes = { Projecte = "reservalia", Entorn = var.entorn, Gestio = "terraform" }  # 2
}

resource "aws_ecs_cluster" "aquest" {
  name = local.nom
  tags = local.etiquetes
}

resource "aws_db_instance" "postgres" {
  identifier              = "${local.nom}-db"
  engine                  = "postgres"
  engine_version          = var.versio_postgres
  instance_class          = var.classe_db
  allocated_storage       = 20
  multi_az                = var.db_multi_az
  backup_retention_period = var.entorn == "prod" ? 30 : 1    # 3
  deletion_protection     = var.entorn == "prod"             # 4
  skip_final_snapshot     = var.entorn != "prod"
  tags                    = local.etiquetes
}

resource "aws_ecs_service" "api" {
  name            = "reservalia-api"                         # 5
  cluster         = aws_ecs_cluster.aquest.id
  desired_count   = var.nombre_tasques
  launch_type     = "FARGATE"
  task_definition = aws_ecs_task_definition.api.arn

  lifecycle { ignore_changes = [task_definition, desired_count] }   # 6
}

resource "aws_cloudwatch_log_group" "api" {
  name              = "/ecs/${local.nom}/api"
  retention_in_days = var.retencio_logs
}
  1. locals són valors calculats una vegada i reutilitzats; eviten repetir la interpolació del nom en vint llocs.
  2. L'etiqueta Gestio = "terraform" sembla decorativa i és de les més útils: permet auditar quins recursos del compte estan gestionats per codi i quins continuen sent artesanals.
  3. Un condicional (condició ? valor_si : valor_no) expressa una diferència real entre entorns sense duplicar el recurs: 30 dies de còpies en producció, 1 a la resta.
  4. deletion_protection a prod impedeix que un terraform destroy accidental esborri la base de dades dels 340 negocis. És una xarxa de seguretat barata i obligatòria.
  5. El nom del servei és idèntic als tres entorns (reservalia-api), tal com assumeix el cd.yml de la 03-02; el que canvia és el clúster.
  6. ignore_changes = [task_definition, desired_count] és la línia més important del fitxer i la que més equips obliden. Terraform crea el servei, però qui canvia la versió desplegada és cd.yml. Sense aquesta línia, cada terraform apply retornaria el servei a la imatge que figura al codi d'infraestructura, desfent l'últim desplegament. La regla general: Terraform és amo de la forma de l'entorn; el pipeline de CD és amo de la versió que corre a dins.

  1. Un mòdul, tres entorns: les diferències com a valors

Aquí hi ha la idea central de la lliçó. Els tres entorns són tres crides al mateix mòdul:

# infra/entorns/dev/main.tf
module "entorn" {
  source         = "../../moduls/entorn"
  entorn         = "dev"
  nombre_tasques = 1
  classe_db      = "db.t4g.micro"
  retencio_logs  = 3
}

# infra/entorns/prod/main.tf
module "entorn" {
  source         = "../../moduls/entorn"
  entorn         = "prod"
  nombre_tasques = 4
  cpu_tasca      = 1024
  memoria_tasca  = 2048
  classe_db      = "db.m6g.large"
  db_multi_az    = true
  retencio_logs  = 30
}

Compara això amb l'escena de l'apartat 1. La pregunta "en què es diferencia staging de prod?" ja no requereix memòria ni arqueologia a la consola: és un diff de dos fitxers de deu línies.

Paràmetre dev staging prod
nombre_tasques 1 2 4
classe_db db.t4g.micro db.t4g.small db.m6g.large
db_multi_az false false true
retencio_logs 3 7 30
versio_postgres 16.3 16.3 16.3
Topologia de xarxa, grups de seguretat, ALB, IAM idèntics idèntics idèntics

Les diferències són sis números i un booleà, totes justificables per cost o per resiliència, i cap capaç de canviar el comportament del programari. Això és el que vol dir "entorns equivalents" del requisit 3. I hi ha un efecte secundari valuós: qualsevol ajust que la Nuria faci al mòdul —un paràmetre de PostgreSQL, un timeout de l'ALB— arriba als tres entorns alhora, així que la deriva ja no es pot acumular en silenci.

  1. Estat remot i bloqueig: per què l'estat local és un accident esperant

El fitxer terraform.tfstate desa la correspondència entre cada bloc del codi i el recurs real. Si viu al portàtil de la Nuria passen tres coses, totes dolentes: ningú més no pot aplicar canvis sense ell; si el portàtil es perd, Terraform ja no sap que aquells recursos són seus i intentarà crear-los de nou; i si la Nuria i en Diego apliquen alhora, cadascun escriu un estat que ignora el que ha fet l'altre. A més, l'estat conté valors sensibles en clar, com la contrasenya generada d'RDS: no ha d'acabar al repositori. La solució és un backend remot amb bloqueig:

# infra/entorns/prod/backend.tf
terraform {
  backend "s3" {
    bucket         = "reservalia-tfstate"          # 1
    key            = "entorns/prod/terraform.tfstate"    # 2
    region         = "eu-west-1"
    dynamodb_table = "reservalia-tflock"           # 3
    encrypt        = true                          # 4
  }
}
  1. Un bucket S3 amb versionat activat: si un apply corromp l'estat, es recupera la versió anterior.
  2. Una clau per entorn: tres estats independents. És el que garanteix que un error aplicant dev no pugui tocar recursos de prod, perquè l'estat de dev ni tan sols els coneix.
  3. La taula DynamoDB implementa el bloqueig: qui comença un apply pren el forrellat i la resta espera amb un missatge explícit en lloc de trepitjar-se. I encrypt = true xifra en repòs un fitxer que, com acabem de dir, conté secrets.

  1. El pipeline d'infraestructura: plan al PR, apply a main

Amb l'estat remot, la infraestructura pot entrar al pipeline amb el mateix flux que el codi: proposta, revisió, aplicació.

flowchart TD
    A["PR sobre infra/**"] --> B["infra.yml · job plan<br/>rol de nomes lectura"]
    B --> C{"Pla comentat al PR<br/>es destrueix alguna cosa?"}
    C -- "canvis no desitjats" --> A
    C -- "aprovat" --> E["Merge a main"]
    E --> F["job aplicar<br/>dev i staging: automatic"]
    F --> G{"environment: prod<br/>revisor requerit"}
    G -- aprova --> H["terraform apply a prod"]
    H --> I["cd.yml desplega sobre<br/>infraestructura coneguda"]
# .github/workflows/infra.yml
name: Infra
on:
  pull_request: { paths: ['infra/**'] }      # 1
  push:         { branches: [main], paths: ['infra/**'] }

permissions: { id-token: write, contents: read, pull-requests: write }   # 2
concurrency: { group: infra-${{ github.ref }}, cancel-in-progress: false }

jobs:
  plan:
    if: github.event_name == 'pull_request'
    runs-on: ubuntu-22.04
    strategy:
      matrix: { entorn: [dev, staging, prod] }        # 3
    steps:
      - uses: actions/checkout@v4
      - uses: hashicorp/setup-terraform@v3
        with: { terraform_version: 1.8.5 }             # 4
      - uses: aws-actions/configure-aws-credentials@v4
        with:
          role-to-assume: arn:aws:iam::…:role/reservalia-terraform-plan   # 5
          aws-region: eu-west-1
      - run: |
          cd infra/entorns/${{ matrix.entorn }}
          terraform init
          terraform plan -no-color -out=plan.bin
          terraform show -no-color plan.bin > plan.txt
      - uses: actions/github-script@v7                 # 6 · comenta plan.txt al PR
        with: { script: 'github.rest.issues.createComment({ …, body: llegirPla() })' }
  1. paths: ['infra/**'] evita executar el pipeline d'infraestructura a cada canvi de codi de l'API, i pull-requests: write (2) permet comentar el pla al PR.
  2. Una matriu executa el pla dels tres entorns: el revisor veu l'impacte complet del canvi, no només el de l'entorn que s'ha editat.
  3. Versió fixada de Terraform. Dues versions diferents poden produir plans diferents sobre el mateix codi; en infraestructura això és inacceptable.
  4. Un rol de només lectura per al pla. El job corre amb codi vingut d'un pull request i no necessita escriptura: donar-la-hi seria regalar producció a qui obri una branca.
  5. El pla com a comentari és el cor del flux: converteix un canvi d'infraestructura en una cosa que es llegeix i es discuteix abans que passi.

El job aplicar s'executa sobre main, amb environment: dev/staging automàtics i environment: prod amb revisor requerit —exactament el mecanisme de la 03-02—, executant terraform apply -auto-approve amb el rol reservalia-terraform-apply.

  1. Riscos reals: recreacions destructives, plans que menteixen i permisos excessius

Risc 1 (el més car): canvis que recreen recursos amb dades. Alguns atributs no es poden modificar en calent. Canviar l'identifier d'una instància RDS no la reanomena: la destrueix i en crea una altra de buida. El pla ho diu, però cal saber llegir-lo:

-/+ resource "aws_db_instance" "postgres" {   # must be replaced
      ~ identifier = "reservalia-prod-db" -> "reservalia-prod-postgres" # forces replacement

Els dos símbols que cal buscar sempre són -/+ i forces replacement. Reservalia adopta tres defenses: deletion_protection i prevent_destroy al lifecycle de la base de dades, revisió humana obligatòria del pla de prod, i una regla d'equip —qualsevol PR el pla del qual contingui forces replacement necessita aprovació de la Nuria, sense excepcions.

Risc 2: el pla no coincideix amb l'apply. Entre el plan del pull request del dilluns i l'apply del dimecres algú pot haver tocat alguna cosa a mà, o haver fusionat un altre PR d'infraestructura. El pla és una foto, no un contracte. Mitigacions: aplicar el fitxer plan.bin desat i no recalcular (Terraform avorta si l'estat ha canviat), concurrency per serialitzar, i un pla programat setmanalment que detecti deriva. Risc 3: el pipeline amb permisos excessius. El rol d'apply necessita crear i esborrar infraestructura, així que és la credencial més poderosa de l'organització: separa plan (lectura) d'apply (escriptura), acota la condició OIDC del sub a l'entorn concret sense comodins —com vam veure a la 03-02— i limita el rol als serveis que realment fa servir. La superfície d'atac del pipeline es tracta a fons a la lliçó 04-03, Seguretat en CI/CD.

  1. Entorns efímers de previsualització per pull request

Quan un entorn és una crida a un mòdul, crear-ne un de nou deixa de ser un projecte i passa a ser una variable. Això habilita una cosa que abans era impensable a Reservalia: un entorn per pull request, creat en obrir-lo i destruït en tancar-lo.

module "entorn" {
  source         = "../../moduls/entorn"
  entorn         = "pr-${var.numero_pr}"   # cluster reservalia-pr-482
  nombre_tasques = 1
  classe_db      = "db.t4g.micro"
}

La Marta pot provar la funcionalitat a https://pr-482.reservalia.com abans de fusionar, i el dissenyador la pot veure sense instal·lar res. Tres cauteles: destrueix-los sempre en tancar el PR (un job on: pull_request: types: [closed] més un escombrat nocturn dels que sobrevisquin més de 72 hores), no facis servir mai dades reals en ells, i vigila el cost, que és lineal en el nombre de PR oberts. Un mòdul d'entorn complet amb RDS pot ser massa car; molts equips fan servir una variant lleugera amb base de dades compartida i esquema per PR. I convé esmentar una família relacionada: GitOps (Argo CD, Flux) porta aquesta idea a l'extrem, amb un agent dins del clúster que vigila el repositori i reconcilia contínuament l'estat real amb el declarat, en lloc que un pipeline empenyi els canvis; és un model diferent i molt estès a Kubernetes, i es tracta a la lliçó 06-05.

Errors Comuns i Consells

Error 1: pujar terraform.tfstate al repositori. Conté secrets en clar i no resol el treball concurrent. Afegeix-lo al .gitignore i fes servir backend remot des del primer dia. Error 2: escriure tres còpies del codi, una per entorn, que és el que més deriva produeix, perquè un arreglament urgent s'aplica a un fitxer i no als altres dos.

Error 3: fer apply des del portàtil. Encara que l'estat sigui remot, un apply local no deixa rastre revisable ni comprovacions. La regla de Reservalia: si no ha passat per infra.yml, no existeix. Error 4: oblidar ignore_changes a la task definition, el símptoma del qual és desconcertant: un canvi d'infraestructura sense relació aparent retorna producció a una versió de fa tres setmanes.

Error 5: no llegir el pla. Un pla de 400 línies que ningú no mira és pitjor que no tenir-lo, perquè dona sensació de control; busca sempre forces replacement i el recompte final de destroy. Consell 1: comença important el que ja existeix. terraform import (o els blocs import) porta la infraestructura artesanal a l'estat sense recrear-la. La Nuria va fer exactament això: va importar prod, va ajustar el codi fins que terraform plan va dir "No changes", i només aleshores es va refiar del mòdul. Consell 2: fixa les versions del binari i dels proveïdors (required_providers) i puja .terraform.lock.hcl al repositori. Consell 3: programa un plan setmanal sobre els tres entorns; si apareix un diferencial sense que ningú hagi tocat el codi, algú ha tornat a la consola.

Exercicis

Exercici 1

La Nuria proposa accelerar així: "A dev faig servir PostgreSQL 14 perquè la instància és més barata, i a prod mantinc la 16". Explica per què aquesta decisió trenca una part del mòdul 3 i quines diferències que són acceptables entre entorns.

Exercici 2

Un pull request canvia classe_db de db.t4g.small a db.t4g.medium a staging. El pla mostra ~ instance_class i, més avall, Plan: 0 to add, 1 to change, 0 to destroy. Un altre PR canvia l'identifier de la base de dades i mostra Plan: 1 to add, 0 to change, 1 to destroy. Explica la diferència i què faries en cada cas.

Exercici 3

En Diego diu: "Ja que terraform apply corre al pipeline, traguem l'aprovació manual de prod: total, el pla ja s'ha revisat al PR". Dona dos arguments en contra i una condició sota la qual sí que seria raonable.

Solucions

Solució 1. Trenca el requisit 3 de la 03-01, entorns equivalents, i amb ell el valor de tota la cadena de la 03-02. Entre PostgreSQL 14 i 16 canvien el planificador de consultes, funcions disponibles i comportaments de tipus: una consulta de calcularForats pot funcionar a dev i fallar o degradar-se a prod, i —pitjor— a l'inrevés, amb la qual cosa dev donaria falsos positius que ningú no sabria interpretar. La regla: són acceptables les diferències d'escala (nombre de tasques, classe d'instància, Multi-AZ, retenció de logs, volum de dades), perquè afecten el cost i la resiliència però no el comportament funcional del programari; no són acceptables les diferències de versió o topologia (motor de base de dades, versió de Node, presència d'un ALB, arquitectura de CPU), perquè canvien el que el codi fa. Si el cost de dev preocupa, la palanca correcta és abaixar la classe d'instància o apagar l'entorn a les nits, no canviar la versió del motor.

Solució 2. El primer pla és una modificació al lloc: ~ indica que l'atribut es canvia sense destruir el recurs. AWS aplicarà el canvi de classe amb un breu reinici, però les dades es conserven. És un canvi de rutina; convé aplicar-lo en una finestra de trànsit baix si és prod. El segon és una substitució destructiva: 1 to destroy sobre una base de dades vol dir que Terraform esborrarà la instància actual —amb totes les dades— i en crearà una de buida amb el nom nou. A staging suposaria perdre la còpia anonimitzada; a prod, perdre les dades dels 340 negocis. Acció: no fusionar. Si el reanomenament és realment necessari, el camí segur és canviar l'adreça del recurs a l'estat amb un bloc moved o terraform state mv quan escaigui, o directament renunciar al canvi cosmètic. I el prevent_destroy del lifecycle hauria d'haver fet fallar el pla abans d'arribar a la revisió: si no ho ha fet, falta aquella defensa.

Solució 3. En contra: (1) el pla del PR i l'apply posterior no són el mateix càlcul —entre tots dos poden haver-se fusionat altres PR o haver canviat l'estat—, així que aprovar el pla del dilluns no equival a aprovar l'aplicació del dimecres; (2) el revisor del PR revisa codi, i qui aprova l'entorn prod revisa moment: pot haver-hi una campanya en marxa, un incident obert o un desplegament en curs que desaconsellin tocar la infraestructura ara, informació que no és al diff. Seria raonable treure-la quan el pipeline apliqui el mateix plan.bin aprovat (Terraform avorta si l'estat ha canviat), hi hagi una comprovació automàtica que falli davant de qualsevol forces replacement o destroy, i l'històric mostri que l'aprovació humana no ha rebutjat res en un nombre significatiu d'aplicacions —el mateix criteri de la 03-01.

Conclusió

L'esquerda amb què hem començat està tancada. Els tres entorns de Reservalia ja no són tres artesanies que s'assemblen per casualitat: són tres crides al mateix mòdul d'infra/moduls/entorn/, i les seves diferències són sis números i un booleà escrits en un fitxer que qualsevol pot llegir en vint segons. La pregunta "en què es diferencia staging de prod?" té per fi una resposta que no depèn de la memòria de ningú. Les idees que convé endur-se: la deriva de configuració s'acumula per arreglaments urgents i destrueix el valor de staging com a autorització per a producció; la IaC és declarativa, idempotent, amb estat i amb pla previ, i aquest pla és el que fa revisable un canvi d'infraestructura; l'estat remot amb bloqueig és innegociable tan bon punt hi ha més d'una persona; el pipeline separa plan amb permisos de lectura al PR i apply amb aprovació a main; cal llegir sempre el pla buscant forces replacement; i Terraform és amo de la forma de l'entorn mentre el CD és amo de la versió que corre a dins —d'aquí l'ignore_changes. Com a regal, els entorns efímers per pull request passen a ser possibles.

Amb els cinc requisits, Reservalia va per tres: proves fiables, artefacte immutable i ara entorns equivalents. Però el cd.yml continua desplegant a prod de la manera més simple possible, substituint tasques sense més control, i encara no hi ha prod al pipeline. La lliçó següent, Estratègies de Desplegament, se n'ocupa: veurem recreate, rolling update, blue-green, canari, proves A/B i shadow, amb el seu cost, el seu risc i la seva facilitat de tornada enrere; muntarem un rolling update fi a ECS i un canari per pes d'ALB al 10 %; distingirem liveness de readiness i veurem per què un health check que respon 200 sense comprovar res és pitjor que no tenir-ne cap; i acceptarem el requisit que totes aquestes estratègies comparteixen: que dues versions del programari conviuran, i s'han d'entendre.

Curs de CI/CD: Integració i Desplegament Continu

Mòdul 1: Introducció al CI/CD

Mòdul 2: Integració Contínua (CI)

Mòdul 3: Desplegament Continu (CD)

Mòdul 4: Pràctiques Avançades de CI/CD

Mòdul 5: Implementació de CI/CD en Projectes Reals

Mòdul 6: Eines i Tecnologies

Mòdul 7: Exercicis Pràctics

Mòdul 8: Recursos Addicionals

© Copyright 2026. Tots els drets reservats