La lliçó anterior va deixar el mapa dibuixat: l'artefacte reservalia/api:a3f9c21 ha de recórrer dev → staging → prod 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
- Què vol dir exactament "desplegar" a ECS Fargate
- Encadenar CI i CD:
workflow_rundavant deworkflow_call - Entorns de GitHub: regles de protecció i revisors requerits
- Autenticació amb AWS per OIDC, sense claus de llarga vida
- Desplegar l'API: definició de tasca amb el digest exacte
- Configuració per entorn: l'artefacte és igual, la configuració canvia
- Comprovacions posteriors al desplegament: estabilització i smoke tests
- Idempotència i reintents
- Desplegar la web: S3, CloudFront i invalidació de memòria cau
- El
cd.ymlde Reservalia, de principi a fi - Errors Comuns i Consells
- Exercicis
- Conclusió
- 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.
- Encadenar CI i CD:
workflow_run davant de workflow_call
workflow_run davant de workflow_callEl 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 }workflows: [CI]referencia elname:de l'altre workflow, no el nom del fitxer: font clàssica de confusió.branches: [main]filtra per la branca sobre la qual va córrer la CI; sense això, qualsevol CI d'un PR intentaria desplegar.permissions:id-token: writehabilita el token OIDC de l'apartat 4 icontents: readja n'hi ha prou per al checkout.concurrencyambgroupfix per branca impedeix que dos desplegaments es trepitgin: si en Diego fusiona dos PR seguits, el segon espera.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.
- 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: prodes 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: devno pot llegir l'ARN del rol deprod, encara que el secret existeixi al repositori. És la barrera contra l'error de copiar i enganxar. deployment branchesimpedeix desplegarproddes d'una altra branca, i GitHub registra un historial per entorn amb qui va aprovar i quin commit es va desplegar: auditoria de franc.
- 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.
- 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_OUTPUTurl:mostra un enllaç a l'entorn desplegat a la interfície de GitHub.ref: head_shaés imprescindible ambworkflow_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.secrets.AWS_ROLE_DEPLOYes resol al secret de l'entorndev, així que el mateix text YAML assumeix un rol diferent a cada job. És el patró que evita duplicar codi per entorn.- 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- La plantilla
infra/ecs/taskdef-api.jsonestà versionada al repositori: CPU, memòria, ports, variables i referències a secrets. L'acció substitueix únicament el campimage, 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. serviceicluster: Reservalia fa servir el mateix nom de servei als tres entorns i clústers diferents (reservalia-dev,reservalia-staging,reservalia-prod).wait-for-service-stabilityconverteix "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.
- 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.
- 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- 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: 20del job. /salutcomprova les dependències, no només el procés; hi tornarem sobre la diferència a la 03-04.- 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.
- 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 |
Sí | L'estat desitjat ja coincideix; ECS no fa res |
| Invalidació de CloudFront | Sí | 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ó.
- 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" "/"- 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. - L'HTML va després i amb
no-cache. L'ordre importa: pujar l'index.htmlnou abans que els assets deixa uns segons d'usuaris demanant un JavaScript que encara no existeix. El--deleteesborra el que és obsolet però exclouassets/*, per no trencar qui tingui carregat l'HTML anterior. - La invalidació només afecta l'HTML. Invalidar
/*funciona, però és lent i AWS només regala 1.000 rutes al mes.
- El
cd.yml de Reservalia, de principi a fi
cd.yml de Reservalia, de principi a fiAmb 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:
- S'ha desplegat la imatge equivocada. Es comprova amb
aws ecs describe-task-definitionmirant el campimage: diu quin digest s'ha registrat de debò. - 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. - L'ALB encara envia trànsit a les tasks antigues perquè no ha vençut el deregistration delay. Es reconeix perquè
/versionalterna entre els dos SHA: és intermitent, no constant. Es resol reintentant, com fa el smoke test. - La imatge s'ha construït sense
build-arg COMMIT_SHA, així que l'endpoint menteix encara que el codi sigui el nou. Sidescribe-task-definitionmostra el digest correcte però/versionno, 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
- Conceptes Bàsics de CI/CD
- Beneficis del CI/CD
- Eines Populars de CI/CD
- El Projecte del Curs: l'Aplicació que Automatitzarem
- Mètriques DORA: Com es Mesura el Lliurament de Programari
Mòdul 2: Integració Contínua (CI)
- Introducció a la Integració Contínua
- Configuració d'un Entorn de CI
- Automatització de la Construcció
- Proves Automatitzades
- Qualitat de Codi i Anàlisi Estàtica
- Artefactes, Versionat i Promoció
- Integració amb el Control de Versions
Mòdul 3: Desplegament Continu (CD)
- Introducció al Desplegament Continu
- Automatització del Desplegament
- Infraestructura com a Codi i Entorns Reproduïbles
- Estratègies de Desplegament
- Feature Flags, Rollback i Recuperació davant Errors
- Monitoratge i Retroalimentació
Mòdul 4: Pràctiques Avançades de CI/CD
- Pipelines de CI/CD
- Gestió de Dependències
- Seguretat en CI/CD
- Escalabilitat i Rendiment
- Pipeline as Code: Plantilles, Reutilització i Proves del Pipeline
- Bases de Dades al Pipeline: Migracions Segures
Mòdul 5: Implementació de CI/CD en Projectes Reals
- Cas d'Estudi: Projecte Web
- Cas d'Estudi: Aplicació Mòbil
- Cas d'Estudi: Microserveis
- Cas d'Estudi: Modernitzar un Projecte Legacy
Mòdul 6: Eines i Tecnologies
- Jenkins
- GitLab CI/CD
- CircleCI
- Travis CI
- Docker i Kubernetes
- GitHub Actions a Fons
- Comparativa i Criteris per Triar Eina
Mòdul 7: Exercicis Pràctics
- Exercici 1: Configuració d'un Pipeline Bàsic
- Exercici 2: Integració de Proves Automatitzades
- Exercici 3: Desplegament en un Entorn de Producció
- Exercici 4: Monitoratge i Retroalimentació
- Exercici 5: Enfortir el Pipeline amb Seguretat i Secrets
- Projecte Final: Pipeline Complet d'Extrem a Extrem
