La lliçó anterior va deixar el mapa dibuixat: l'artefacte reservalia/api:a3f9c21 ha de recórrer devstagingprod sense que ningú obri la consola d'AWS. Ara escriurem el fitxer que ho fa. En acabar aquesta lliçó, Reservalia tindrà el seu segon workflow —.github/workflows/cd.yml— que es dispara tot sol quan la CI publica un artefacte nou, desplega a dev de manera automàtica, comprova amb un smoke test que el que s'ha desplegat és exactament el que crèiem, i continua cap a staging. Veurem com s'encadenen dos workflows, què són els entorns de GitHub i les seves regles de protecció, com s'obté accés a AWS sense desar ni una sola clau, i on viu la configuració de cada entorn, que és la pregunta que trenca més desplegaments.

Contingut

  1. Què vol dir exactament "desplegar" a ECS Fargate
  2. Encadenar CI i CD: workflow_run davant de workflow_call
  3. Entorns de GitHub: regles de protecció i revisors requerits
  4. Autenticació amb AWS per OIDC, sense claus de llarga vida
  5. Desplegar l'API: definició de tasca amb el digest exacte
  6. Configuració per entorn: l'artefacte és igual, la configuració canvia
  7. Comprovacions posteriors al desplegament: estabilització i smoke tests
  8. Idempotència i reintents
  9. Desplegar la web: S3, CloudFront i invalidació de memòria cau
  10. El cd.yml de Reservalia, de principi a fi
  11. Errors Comuns i Consells
  12. Exercicis
  13. Conclusió

  1. Què vol dir exactament "desplegar" a ECS Fargate

A ECS Fargate, "desplegar" no és copiar fitxers a un servidor: és canviar una declaració i deixar que l'orquestrador la faci realitat. Tres peces:

Peça Què és Analogia
Task definition JSON versionat: imatge, CPU, memòria, variables, secrets, port La recepta
Task Una execució concreta d'aquesta recepta: un contenidor corrent El plat servit
Service Manté N tasks vives, les registra a l'ALB i les substitueix en canviar la recepta El cuiner

Desplegar són tres passos: registrar una revisió nova de la task definition amb la imatge nova; dir al service que la faci servir; i esperar que hagi substituït les tasks velles sense que l'ALB deixi de rebre trànsit. Com se substitueixen exactament és l'estratègia de desplegament, contingut de la 03-04; aquí fem servir el comportament per defecte d'ECS, que ja és un rolling update raonable.

  1. Encadenar CI i CD: workflow_run davant de workflow_call

El ci.yml de Reservalia acaba publicant a ECR. Com arrenca el desplegament a partir d'aquí? GitHub Actions ofereix dos mecanismes i la tria té conseqüències.

workflow_run workflow_call
Com funciona El CD escolta "el workflow CI ha acabat" La CI crida el CD com una funció
Visibilitat a la UI Dues execucions separades Una sola execució amb tots els jobs
Pas de dades Via outputs del run anterior o artefactes Amb inputs: i secrets: explícits
Reintent manual Es pot rellançar només el CD Cal rellançar-ho tot
Parany conegut Només funciona si el workflow és a la branca per defecte Cap de rellevant

Reservalia tria workflow_run: vol poder rellançar només el desplegament quan falla per un problema transitori d'AWS, sense repetir quatre minuts de proves, i vol un historial de desplegaments llegible en lloc de jobs perduts dins d'execucions de CI. (Els workflows reutilitzables amb workflow_call es tracten a fons a la 04-05.)

# .github/workflows/cd.yml
name: CD

on:
  workflow_run:
    workflows: [CI]              # 1 · el name: de l altre workflow, no el seu fitxer
    types: [completed]
    branches: [main]             # 2 · nomes les CI que van correr sobre main

permissions: { id-token: write, contents: read }                # 3
concurrency: { group: cd-main, cancel-in-progress: false }      # 4 i 5
env: { TZ: Europe/Madrid, AWS_REGION: eu-west-1 }
  1. workflows: [CI] referencia el name: de l'altre workflow, no el nom del fitxer: font clàssica de confusió.
  2. branches: [main] filtra per la branca sobre la qual va córrer la CI; sense això, qualsevol CI d'un PR intentaria desplegar.
  3. permissions: id-token: write habilita el token OIDC de l'apartat 4 i contents: read ja n'hi ha prou per al checkout.
  4. concurrency amb group fix per branca impedeix que dos desplegaments es trepitgin: si en Diego fusiona dos PR seguits, el segon espera.
  5. cancel-in-progress: false és crític i oposat al que fèiem a la CI. Cancel·lar un desplegament a mig fer deixa el servei amb la meitat de les tasks en cada versió i ningú vigilant.

I falta una comprovació essencial: workflow_run es dispara també quan la CI ha fallat, així que cada job necessita if: github.event.workflow_run.conclusion == 'success'. Oblidar-ho és l'error número u d'aquest patró: desplegar el resultat d'un pipeline vermell.

  1. Entorns de GitHub: regles de protecció i revisors requerits

Un environment de GitHub és més que una etiqueta: és on viuen els secrets i les variables d'un entorn concret i on es configuren les regles que un desplegament ha de complir. Reservalia en crea tres a Settings → Environments:

Regla dev staging prod
Required reviewers Marta o Nuria
Deployment branches main main main
Variables pròpies ENTORN=dev ENTORN=staging ENTORN=prod
Secrets propis ARN del rol dev ARN del rol staging ARN del rol prod
URL api-dev.reservalia.com api-staging.reservalia.com api.reservalia.com

El que els fa una mica més que documentació:

  • Un job amb environment: prod es queda pausat fins que un revisor l'aprova. Aquesta és, literalment, la porta manual de la lliçó anterior; treure-la el dia que Reservalia passi a Desplegament Continu serà esborrar la llista de revisors.
  • Els secrets són d'àmbit d'entorn. Un job amb environment: dev no pot llegir l'ARN del rol de prod, encara que el secret existeixi al repositori. És la barrera contra l'error de copiar i enganxar.
  • deployment branches impedeix desplegar prod des d'una altra branca, i GitHub registra un historial per entorn amb qui va aprovar i quin commit es va desplegar: auditoria de franc.

  1. Autenticació amb AWS per OIDC, sense claus de llarga vida

La manera temptadora de donar permisos al pipeline és crear un usuari IAM i desar AWS_ACCESS_KEY_ID i AWS_SECRET_ACCESS_KEY com a secrets. És mala idea: són credencials permanents, s'han de rotar a mà i, si es filtren en un log, demà encara valdran. OIDC (OpenID Connect) inverteix el model: GitHub emet a cada execució un token JWT de curta durada que diu "sóc el workflow cd.yml del repositori reservalia/reservalia, a la branca main, amb l'entorn prod", i AWS —que confia en GitHub com a proveïdor d'identitat— lliura credencials temporals d'una hora si aquestes afirmacions encaixen amb la política de confiança del rol.

{
  "Effect": "Allow",
  "Principal": { "Federated": "arn:aws:iam::…:oidc-provider/token.actions.githubusercontent.com" },
  "Action": "sts:AssumeRoleWithWebIdentity",
  "Condition": { "StringEquals": {
      "token.actions.githubusercontent.com:aud": "sts.amazonaws.com",
      "token.actions.githubusercontent.com:sub": "repo:reservalia/reservalia:environment:prod" } }
}

La línia decisiva és la del sub: aquest rol només el pot assumir un job d'aquell repositori que declari environment: prod. Un fork, un altre repositori o un job sense entorn reben un AccessDenied. Reservalia té tres rols —reservalia-deploy-dev, reservalia-deploy-staging i reservalia-deploy-prod— cadascun amb la seva condició i amb permisos només sobre els recursos del seu entorn. Un error freqüent és fer servir comodins (repo:reservalia/reservalia:*): això permet assumir el rol de producció des de qualsevol branca i qualsevol workflow, inclòs un que algú afegeixi en un pull request.

  1. Desplegar l'API: definició de tasca amb el digest exacte

  desplegar-dev:
    runs-on: ubuntu-22.04
    timeout-minutes: 20
    if: github.event.workflow_run.conclusion == 'success'
    environment: { name: dev, url: 'https://api-dev.reservalia.com' }   # 1
    steps:
      - uses: actions/checkout@v4
        with: { ref: '${{ github.event.workflow_run.head_sha }}' }      # 2
      - id: meta
        run: echo "sha=$(git rev-parse --short=7 HEAD)" >> $GITHUB_OUTPUT

      - uses: aws-actions/configure-aws-credentials@v4
        with:
          role-to-assume: ${{ secrets.AWS_ROLE_DEPLOY }}                # 3
          aws-region: ${{ env.AWS_REGION }}

      - name: Resoldre el digest de la imatge
        id: imatge
        run: |                                                          # 4
          DIGEST=$(aws ecr describe-images --repository-name reservalia/api \
            --image-ids imageTag=${{ steps.meta.outputs.sha }} \
            --query 'imageDetails[0].imageDigest' --output text)
          echo "uri=${{ vars.ECR_REGISTRY }}/reservalia/api@${DIGEST}" >> $GITHUB_OUTPUT
  1. url: mostra un enllaç a l'entorn desplegat a la interfície de GitHub.
  2. ref: head_sha és imprescindible amb workflow_run: per defecte el checkout portaria la branca per defecte en el seu estat actual, que pot ser ja un altre commit. Volem el commit exacte que va produir l'artefacte.
  3. secrets.AWS_ROLE_DEPLOY es resol al secret de l'entorn dev, així que el mateix text YAML assumeix un rol diferent a cada job. És el patró que evita duplicar codi per entorn.
  4. Resolem el digest, no fem servir l'etiqueta: a3f9c21 és immutable per convenció, sha256:… ho és per criptografia. És "construir una vegada, promocionar el mateix digest" aplicat literalment.

Amb l'URI resolt es registra la revisió nova de la task definition i s'actualitza el servei:

      - uses: aws-actions/amazon-ecs-render-task-definition@v1
        id: taskdef
        with:
          task-definition: infra/ecs/taskdef-api.json     # 5 · plantilla versionada
          container-name: api
          image: ${{ steps.imatge.outputs.uri }}

      - uses: aws-actions/amazon-ecs-deploy-task-definition@v2
        with:
          task-definition: ${{ steps.taskdef.outputs.task-definition }}
          service: reservalia-api                          # 6
          cluster: reservalia-dev
          wait-for-service-stability: true                 # 7
          wait-for-minutes: 10
  1. La plantilla infra/ecs/taskdef-api.json està versionada al repositori: CPU, memòria, ports, variables i referències a secrets. L'acció substitueix únicament el camp image, així que el desplegament és reproduïble i revisable en un pull request en lloc de dependre de l'estat que hi hagués a AWS.
  2. service i cluster: Reservalia fa servir el mateix nom de servei als tres entorns i clústers diferents (reservalia-dev, reservalia-staging, reservalia-prod).
  3. wait-for-service-stability converteix "he llançat un desplegament" en "el desplegament ha acabat". Sense això el job sortiria verd en segons mentre ECS encara arrenca contenidors que potser fallen.

  1. Configuració per entorn: l'artefacte és igual, la configuració canvia

Ja va aparèixer a la 02-06 com a corol·lari de la immutabilitat: si l'artefacte és el mateix als tres entorns, no pot contenir res específic d'un entorn. Construir una imatge reservalia/api:a3f9c21-prod diferent de la de staging destrueix la cadena sencera, perquè el que arribaria a producció seria un binari mai provat. La configuració entra des de fora, en l'arrencada, i és de dues menes:

Configuració no sensible Secrets
Exemples ENTORN, LOG_NIVELL, PORT DATABASE_URL, clau de la passarel·la de pagament
On viu Task definition, bloc environment Secrets Manager, referenciat des de secrets
Qui la veu Qualsevol amb lectura sobre ECS Només la task en execució, i queda auditat
Es rota Amb un desplegament Sense desplegar

El fragment rellevant d'infra/ecs/taskdef-api.json:

{
  "family": "reservalia-api",
  "containerDefinitions": [{
    "name": "api",
    "image": "SUBSTITUIT_PEL_PIPELINE",
    "environment": [
      { "name": "ENTORN", "value": "dev" },
      { "name": "LOG_NIVELL", "value": "debug" },
      { "name": "TZ", "value": "Europe/Madrid" }
    ],
    "secrets": [
      { "name": "DATABASE_URL",
        "valueFrom": "arn:aws:secretsmanager:eu-west-1:…:secret:reservalia/dev/api:DATABASE_URL::" }
    ]
  }]
}

La diferència entre tots dos blocs és substancial. environment és text pla visible a la consola d'AWS i en qualsevol describe-task-definition. secrets són referències: ECS resol el valor en l'arrencada contra Secrets Manager fent servir el rol d'execució de la tasca, i el valor no apareix mai ni a la definició ni als logs del pipeline. Rotar la contrasenya de la base de dades és canviar-la a Secrets Manager i reiniciar el servei: no cal reconstruir ni redesplegar res. I una regla operativa que estalvia incidents: l'aplicació ha de fallar en arrencar si falta una variable obligatòria, en lloc de tirar d'un valor per defecte silenciós. Val més que el desplegament caigui a dev amb un missatge clar que no pas arrencar a prod apuntant a la base de dades equivocada.

  1. Comprovacions posteriors al desplegament: estabilització i smoke tests

wait-for-service-stability respon a "ha acabat ECS?", no a "funciona?": un contenidor pot estar corrent i contestar 500 a tot. El smoke test cobreix aquesta diferència, i no es limita a comprovar que l'API respon: verifica que respon la versió que acabem de desplegar. És l'ús pràctic de l'endpoint /version de la 02-06.

      - name: Smoke test
        env: { HOST: 'https://api-dev.reservalia.com', ESPERAT: '${{ steps.meta.outputs.sha }}' }
        run: |
          for intent in $(seq 1 10); do                        # 1
            DESPLEGAT=$(curl -sf --max-time 5 "$HOST/version" | jq -r '.commit' || echo "")
            if [ "$DESPLEGAT" = "$ESPERAT" ]; then
              echo "OK: $HOST esta servint $DESPLEGAT"; break
            fi
            echo "Intent $intent: veig '$DESPLEGAT', espero '$ESPERAT'"
            sleep 10
            [ "$intent" = "10" ] && { echo "::error::el desplegament no ha arribat"; exit 1; }
          done

          curl -sf --max-time 5 "$HOST/salut" > /dev/null      # 2
          curl -sf --max-time 5 "$HOST/api/negocis/demo/forats?data=2026-03-10" > /dev/null   # 3
  1. Es reintenta, perquè el DNS i l'ALB triguen uns segons a dirigir tot el trànsit a les tasks noves. Un smoke test sense reintents és una prova flaky que ensenyarà l'equip a ignorar el vermell. Deu intents de deu segons donen 100 s de marge, molt per sota del timeout-minutes: 20 del job.
  2. /salut comprova les dependències, no només el procés; hi tornarem sobre la diferència a la 03-04.
  3. Una ruta de negoci real que consulta la base de dades detecta el cas clàssic de "l'aplicació arrenca però no té permisos sobre RDS". En conjunt, un smoke test ha de ser ràpid, estable i cobrir el camí crític: no és la suite de proves, és la comprovació que el desplegament ha fet efecte i que l'essencial respon.

  1. Idempotència i reintents

Un desplegament automatitzat es rellançarà: per una fallada de xarxa, perquè algú prem Re-run, perquè dos merges seguits disparen dues execucions. La propietat que ho fa segur és la idempotència: executar-lo dues vegades deixa el sistema igual que executar-lo una.

Operació És idempotent? Per què
update-service amb el mateix digest L'estat desitjat ja coincideix; ECS no fa res
Invalidació de CloudFront Es pot repetir sense efecte addicional
INSERT a la taula desplegaments No Duplica files i contamina les mètriques DORA
Migració ALTER TABLE … ADD COLUMN No, llevat d'IF NOT EXISTS Falla la segona vegada
Enviar un correu als negocis No El reben dues vegades

Tres regles pràctiques: fer servir operacions declaratives ("vull aquest digest") i no imperatives ("afegeix una instància"); inserir a desplegaments amb ON CONFLICT DO NOTHING sobre un identificador derivat del run; i protegir amb concurrency, com hem fet a l'apartat 2. Sobre els reintents: són correctes per a fallades transitòries (timeout de xarxa, ThrottlingException) i perillosos per a fallades deterministes —reintentar deu vegades un desplegament al qual li falta una variable només produeix deu fallades idèntiques—. La regla: reintenta la comprovació, no la decisió.

  1. Desplegar la web: S3, CloudFront i invalidació de memòria cau

apps/web no és un contenidor: són fitxers estàtics generats per Vite que es pugen a S3 i se serveixen per CloudFront. La seva subtilesa pròpia és la memòria cau.

  desplegar-web-dev:
    needs: [desplegar-dev]
    environment: dev
    steps:
      # checkout(head_sha) · setup-node(.nvmrc) · npm ci · npm run build --workspace apps/web
      # · credencials OIDC
      - name: Assets amb hash — memoria cau llarga            # 1
        run: aws s3 sync apps/web/dist/assets s3://reservalia-web-dev/assets
             --cache-control "public,max-age=31536000,immutable"

      - name: HTML — sense memoria cau                        # 2
        run: aws s3 sync apps/web/dist s3://reservalia-web-dev
             --exclude "assets/*" --delete --cache-control "no-cache"

      - name: Invalidar CloudFront                            # 3
        run: aws cloudfront create-invalidation
             --distribution-id ${{ vars.CLOUDFRONT_DIST_ID }} --paths "/index.html" "/"
  1. Els assets van primer i amb memòria cau d'un any: Vite els posa un hash al nom (index-4f3c21a9.js), així que cada build genera fitxers nous i mai no cal invalidar-los.
  2. L'HTML va després i amb no-cache. L'ordre importa: pujar l'index.html nou abans que els assets deixa uns segons d'usuaris demanant un JavaScript que encara no existeix. El --delete esborra el que és obsolet però exclou assets/*, per no trencar qui tingui carregat l'HTML anterior.
  3. La invalidació només afecta l'HTML. Invalidar /* funciona, però és lent i AWS només regala 1.000 rutes al mes.

  1. El cd.yml de Reservalia, de principi a fi

Amb la capçalera de l'apartat 2, el workflow té tres jobs: desplegar-dev (~3 min: checkout del head_sha, OIDC, digest, taskdef, deploy i smoke), desplegar-web-dev (needs: [desplegar-dev], build de Vite, sync a S3 i invalidació) i desplegar-staging (~4 min, needs els dos anteriors, environment: staging, el mateix digest sobre el clúster reservalia-staging, amb smoke i les proves E2E crítiques de la 02-04).

flowchart LR
    CI["ci.yml verd a main<br/>publica a3f9c21"] --> W["workflow_run"]
    W --> D["desplegar-dev<br/>~3 min"]
    D --> S1{"smoke /version"}
    S1 -- ok --> WD["desplegar-web-dev"]
    WD --> ST["desplegar-staging<br/>mateix digest"]
    ST --> S2{"smoke + E2E"}
    S2 -- ok --> P["prod · pendent<br/>03-04"]
    S1 -- falla --> X["workflow vermell<br/>rollback a la 03-05"]

Amb la perspectiva del mòdul 1: des que en Diego fusiona un PR fins que el canvi és a staging passen uns onze minuts —quatre de CI, tres de dev, quatre de staging— sense que ningú escrigui una ordre; el ritual del divendres durava tres hores i només arribava a un entorn. Falta prod, que s'afegeix a la 03-04 amb una estratègia progressiva, i falta què passa quan el smoke test falla: avui el workflow es posa vermell i dev es queda amb la versió trencada. Aquell forat el tapa la 03-05.

Errors Comuns i Consells

Error 1: oblidar if: github.event.workflow_run.conclusion == 'success'. workflow_run es dispara també quan la CI falla; sense aquest filtre despleges el resultat d'un pipeline vermell. Error 2: fer checkout sense ref: head_sha, amb la qual cosa despleges la imatge d'un commit i executes els scripts d'un altre.

Error 3: desar claus d'AWS de llarga vida com a secrets. No caduquen, s'han de rotar a mà i si es filtren continuen servint. Amb OIDC no hi ha res per filtrar.

Error 4: enfornar la configuració dins de la imatge. Una imatge per entorn vol dir que el que arriba a producció és un artefacte que no es va provar mai.

Error 5: donar per bo el desplegament sense verificar la versió. El pipeline verd només diu que l'ordre no ha donat error; si /version continua retornant el SHA anterior, no has desplegat res. Error 6: cancel-in-progress: true al CD, que deixa el servei amb dues versions convivint i ningú vigilant.

Consell 1: versiona la definició de tasca al repositori, perquè un canvi de memòria o de variable passi per pull request. Consell 2: desplega per digest, no per etiqueta. Consell 3: mesura el temps de cada desplegament i tracta'l com un pressupost: un que creix de 3 a 12 minuts frena tot el que ve darrere.

Exercicis

Exercici 1

El desplegament a dev de Reservalia surt verd, però https://api-dev.reservalia.com/version continua retornant b7e2d10 en lloc d'a3f9c21. Enumera quatre causes possibles i com distingir-les.

Exercici 2

Un company proposa: "En comptes de Secrets Manager, passem DATABASE_URL com a secret de GitHub i l'injectem a la task definition durant el desplegament". Explica tres problemes concrets d'aquest disseny.

Exercici 3

En Diego fusiona dos pull requests amb dos minuts de diferència. Descriu què passa amb la configuració de concurrency mostrada, què passaria amb cancel-in-progress: true i què passaria sense cap concurrency.

Solucions

Solució 1. Quatre causes i com distingir-les:

  1. S'ha desplegat la imatge equivocada. Es comprova amb aws ecs describe-task-definition mirant el camp image: diu quin digest s'ha registrat de debò.
  2. Les tasks noves no han arrencat i ECS ho ha revertit tot sol. Apareix a aws ecs describe-services --query 'services[0].events' com a tasks que neixen i moren; gairebé sempre falta una variable o no pot llegir un secret.
  3. L'ALB encara envia trànsit a les tasks antigues perquè no ha vençut el deregistration delay. Es reconeix perquè /version alterna entre els dos SHA: és intermitent, no constant. Es resol reintentant, com fa el smoke test.
  4. La imatge s'ha construït sense build-arg COMMIT_SHA, així que l'endpoint menteix encara que el codi sigui el nou. Si describe-task-definition mostra el digest correcte però /version no, el problema és al build, no al desplegament.

Solució 2. Tres problemes: (1) rotació acoblada al desplegament —canviar la contrasenya exigeix editar un secret de GitHub i llançar un desplegament complet, quan amb Secrets Manager n'hi ha prou de reiniciar el servei—; (2) el secret queda escrit a la task definition, un document llegible per qualsevol amb permisos sobre ECS i conservat en totes les seves revisions; (3) més superfície d'exposició: el valor passa pel runner, on un set -x o una acció de tercers compromesa el poden veure, mentre que amb valueFrom el runner només coneix un ARN i cada lectura queda auditada a CloudTrail.

Un quart argument, menys obvi: si el secret només existeix a GitHub, l'aplicació no pot arrencar fora del pipeline. La configuració pertany a l'entorn, no al sistema que desplega.

Solució 3. Amb la configuració mostrada, el primer s'executa sencer i el segon espera a la cua: dos desplegaments consecutius, cadascun complet i verificat. Amb cancel-in-progress: true, el segon cancel·laria el primer a mitja execució; en el pitjor moment —durant el rolling update— ECS es queda amb tasks de dues versions, sense ningú esperant l'estabilització ni executant el smoke test. I sense concurrency tots dos correrien en paral·lel sobre el mateix servei: l'estat final depèn de l'ordre en què arribin les crides a l'API d'AWS, així que és possible que el segon acabi abans i el servei quedi amb el commit més antic, amb els dos pipelines en verd. És la pitjor opció, perquè la fallada és silenciosa.

Conclusió

Reservalia ja té desplegament automatitzat fins a staging. L'artefacte reservalia/api:a3f9c21 surt d'ECR, es desplega a dev en uns tres minuts, es verifica amb un smoke test contra /version i continua fins a staging amb el mateix digest, sense reconstruir res. La web viatja en paral·lel cap a S3 i CloudFront amb la seva invalidació de memòria cau.

Les decisions que sostenen el disseny: workflow_run per encadenar CI i CD mantenint execucions separades i rellançables, amb el filtre obligatori sobre conclusion == 'success'; els entorns de GitHub com a lloc on viuen secrets, variables i regles de protecció, de manera que el mateix YAML assumeix un rol diferent a cada entorn; OIDC per obtenir credencials temporals sense desar claus; el desplegament per digest amb una task definition versionada; la separació entre configuració no sensible i secrets, amb Secrets Manager resolent els segons en l'arrencada; i les comprovacions posteriors —estabilització més smoke test— que distingeixen "he llançat un desplegament" de "el desplegament funciona".

Queda una fragilitat de fons. Tot aquest workflow 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… i aquests recursos els va crear la Nuria a mà a la consola d'AWS fa mesos. Ningú no sap del cert en què es diferencia staging de prod, perquè l'única font de veritat és el que hi ha dins d'AWS. És el requisit 3 de la lliçó anterior —entorns equivalents— i continua sense complir-se.

La lliçó següent, Infraestructura com a Codi i Entorns Reproduïbles, ataca justament això: per què un desplegament automatitzat sobre infraestructura artesanal continua sent fràgil, què és la deriva de configuració, i com el mòdul infra/ de Reservalia s'escriu una sola vegada en Terraform i s'instancia tres vegades perquè dev, staging i prod surtin literalment del mateix codi.

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