Durant set mòduls el curs ha anat deixant portes ajustades. A la 03-04 vas configurar un canary movent pesos de l'ALB a mà i es va dir que existien eines que ho fan soles analitzant mètriques. A la 04-03 vas desar els secrets al magatzem del repositori i es va dir que arriba un punt en què això deixa de bastar. A la 02-04 vas mesurar cobertura i es va admetre que la cobertura menteix. A la 07-05 vas signar imatges amb Cosign i es va esmentar un marc anomenat SLSA sense desenvolupar-lo.
Aquesta lliçó obre aquestes portes. Però amb una disciplina estricta, que és la mateixa que la 08-02 et va ensenyar a aplicar als consells aliens: cada eina es presenta pel problema que resol, no per allò que és. Per a cadascuna hi trobaràs el moment del curs on el problema va aparèixer, com encaixa al pipeline que ja tens, un exemple breu de configuració i —la part que gairebé mai no s'escriu— quan NO la necessites.
No és un tutorial de res. És un catàleg de reconeixement: perquè quan un problema aparegui, sàpigues que hi ha alguna cosa que el resol i què et costarà.
Contingut
- Gestió de secrets més enllà del CI
- Lliurament progressiu automatitzat
- GitOps
- Polítiques com a codi
- Qualitat i anàlisi
- Cadena de subministrament
- Proves
- Desenvolupament local i reproduïbilitat
- Plataforma interna
- Observabilitat
- Taula d'adopció per mida d'equip
- El criteri per deixar entrar una eina
- Errors Comuns i Consells
- Exercicis
- Conclusió
- Gestió de secrets més enllà del CI
El problema, i on va aparèixer. A la 04-03 i la 07-05 vas desar els secrets al magatzem de secrets del repositori o de l'organització. Funciona bé i és el correcte per començar. Deixa de bastar quan apareix alguna d'aquestes quatre condicions:
- El secret el necessita també l'aplicació en execució, no només el pipeline. El magatzem del CI injecta variables durant el workflow; no li serveix de res al contenidor que fa tres setmanes que corre a ECS.
- Cal rotar i no es pot. Rotar la contrasenya de la base de dades implica canviar-la al magatzem del CI, a la definició de tasca i a la mateixa base de dades, coordinadament i a mà.
- Cal auditoria. «Qui ha llegit aquest secret i quan?» El magatzem del CI et diu qui el va editar, no qui el va fer servir.
- Els secrets es multipliquen per entorns i equips i ja no caben en una llista plana.
1.1. AWS Secrets Manager (i Parameter Store)
És l'opció de menys fricció si ja ets a AWS, com Reservalia. El secret es desa al servei, la tasca d'ECS el rep per referència i no passa mai pel pipeline: el workflow ja no veu la contrasenya de la base de dades en cap moment, cosa que redueix la superfície d'exposició de cop.
{
"containerDefinitions": [{
"name": "api",
"image": "<compte>.dkr.ecr.eu-west-1.amazonaws.com/reservalia-api@sha256:abc123...",
"secrets": [
{
"name": "DATABASE_URL",
"valueFrom": "arn:aws:secretsmanager:eu-west-1:<compte>:secret:reservalia/prod/db-Ab3xY9"
}
]
}]
}L'agent d'ECS resol l'ARN en arrencar la tasca fent servir el rol d'execució. La rotació automàtica es configura al mateix servei amb una funció Lambda.
Contrapartides. Costa diners per secret i per crida a l'API —poc, però en Diego ho preguntarà; Parameter Store amb paràmetres SecureString és l'alternativa més barata i menys capaç—. I et lliga al proveïdor: els ARN no es migren.
Quan NO el necessites. Si tots els teus secrets són de pipeline (tokens de registre, credencials de desplegament) i la teva aplicació no en consumeix cap en execució, el magatzem del CI més OIDC és suficient i més simple.
1.2. HashiCorp Vault
El gestor de secrets de propòsit general i agnòstic de proveïdor. Aporta dues coses que un servei de núvol bàsic no dóna:
- Secrets dinàmics. En lloc de desar una credencial de base de dades, Vault la genera sota demanda amb caducitat. L'aplicació demana credencials, rep un usuari i una contrasenya vàlids una hora, i Vault els revoca sols. Un secret que caduca en una hora és un secret que gairebé no es pot filtrar.
- Polítiques i auditoria fines, amb motor d'identitat propi i registre de cada lectura.
# En un workflow: autenticar-se a Vault amb el token OIDC de GitHub Actions
- uses: hashicorp/vault-action@<sha-complet>
with:
url: https://vault.intern.example.com
method: jwt
role: github-actions-reservalia
secrets: |
secret/data/reservalia/prod token | TOKEN_DESPLEGAMENT ;Igual que amb AWS a la 03-02, aquí no hi ha credencial de llarga durada: el runner presenta el seu token OIDC i Vault decideix si aquell repositori i aquella branca poden llegir aquella ruta.
Contrapartides, i són grans. Vault autoallotjat és un sistema crític més per operar: alta disponibilitat, segellat i dessegellat, còpies de seguretat, actualitzacions. Si Vault cau, no desplega ningú i possiblement no arrenca res. És una peça d'infraestructura seriosa, i la seva versió gestionada costa diners. La 06-01 deia de Jenkins que el cost real no és la llicència sinó qui el manté; amb Vault passa exactament igual.
Quan NO el necessites. Gairebé sempre, amb menys de 20 persones i un sol núvol. La resposta correcta per a Reservalia és Secrets Manager, no Vault. Vault comença a compensar amb diversos núvols, requisits d'auditoria estrictes o quan els secrets dinàmics resolen un problema de compliment normatiu concret.
1.3. SOPS + age: secrets versionats a Git
Un enfocament diferent i molt infravalorat: xifrar els secrets i cometre'ls. SOPS xifra només els valors d'un YAML o JSON, deixant les claus llegibles, de manera que el git diff continua sent útil.
# config/prod.enc.yaml — comes sense cap problema
database:
host: db.reservalia.internal
password: ENC[AES256_GCM,data:pQx8fK2m...,tag:9dK...,type:str]
sops:
age:
- recipient: age1ql3z7hjy54pw3hyww5ayyfg7zqgvc7w3j2elw8zmrj2kg5sfn9aqmcac8page és l'eina de xifratge moderna que substitueix GPG per a aquest ús: claus curtes, sense anell de claus, sense la complexitat històrica de GPG. Al pipeline, la clau de desxifratge és l'únic secret que viu al magatzem del CI — has reduït N secrets a un.
Avantatges reals: els secrets es versionen, es revisen en PR, segueixen la mateixa promoció que el codi, i l'historial diu qui va canviar què i quan. Contrapartides: rotar un destinatari exigeix rexifrar tots els fitxers; i encara que estigui xifrat, el material és al repositori per sempre, així que un algorisme trencat o una clau filtrada d'aquí a cinc anys compromet tot l'històric.
Quan sí que és l'elecció correcta: equips petits amb molts fitxers de configuració per entorn, sobretot amb Kubernetes, on encaixa de manera natural.
1.4. External Secrets Operator
Específic de Kubernetes. Sincronitza secrets des d'un gestor extern (Secrets Manager, Vault, etc.) cap a Secret natius del clúster, perquè les aplicacions els consumeixin de la manera estàndard sense saber d'on vénen.
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
name: reservalia-db
spec:
refreshInterval: 1h
secretStoreRef:
name: aws-secrets-manager
kind: ClusterSecretStore
target:
name: reservalia-db-secret
data:
- secretKey: DATABASE_URL
remoteRef:
key: reservalia/prod/db
property: urlQuan NO el necessites. Si no fas servir Kubernetes, és irrellevant. Reservalia, a ECS, no el necessita.
1.5. El criteri de decisió
graph TD
A["El secret el fa servir nomes el pipeline?"] -->|Si| B["Magatzem del CI + OIDC<br/>Suficient"]
A -->|"No: l aplicacio el necessita<br/>en execucio"| C["Nomes un nuvol?"]
C -->|Si| D["Gestor del nuvol<br/>Secrets Manager / Parameter Store"]
C -->|"No, o cal<br/>auditoria fina"| E["Vault"]
B --> F["Molts fitxers de config<br/>per entorn?"]
F -->|Si| G["SOPS + age<br/>com a complement"]
I una regla que val més que el diagrama: la millor gestió de secrets és no tenir el secret. OIDC va eliminar les claus d'AWS del pipeline a la 03-02; els rols d'IAM eliminen credencials entre serveis del núvol. Cada secret que aconsegueixes eliminar és un que no cal gestionar, rotar ni auditar.
- Lliurament progressiu automatitzat
El problema, i on va aparèixer. A la 03-04 vas muntar un canary desviant un 10 % del trànsit amb pesos d'ALB, i després algú —tu— mirava Grafana i decidia si promocionar o revertir. Això té tres defectes: depèn que hi hagi un humà mirant, el criteri és subjectiu, i a la pràctica s'acaba promocionant per impaciència.
El lliurament progressiu automatitzat converteix aquell judici en una especificació declarativa: quines mètriques, quins llindars, cada quant, i què fer si fallen.
2.1. Argo Rollouts
Substitueix el Deployment de Kubernetes per un recurs Rollout que sap fer canary i blue-green amb anàlisi automàtica.
apiVersion: argoproj.io/v1alpha1
kind: Rollout
metadata:
name: reservalia-api
spec:
replicas: 6
strategy:
canary:
steps:
- setWeight: 10
- pause: { duration: 5m }
- analysis:
templates:
- templateName: taxa-error
- setWeight: 50
- pause: { duration: 10m }
- setWeight: 100apiVersion: argoproj.io/v1alpha1
kind: AnalysisTemplate
metadata:
name: taxa-error
spec:
metrics:
- name: taxa-error-5xx
interval: 1m
count: 5
successCondition: result[0] < 0.01 # menys de l 1 % de 5xx
failureLimit: 1
provider:
prometheus:
address: http://prometheus.monitoring:9090
query: |
sum(rate(http_requests_total{job="reservalia-api",status=~"5.."}[2m]))
/
sum(rate(http_requests_total{job="reservalia-api"}[2m]))Si la consulta supera el llindar una vegada, el rollout reverteix sol, sense que ningú miri res. És exactament el rollback automàtic per mètriques de la 03-05, però declarat al recurs en comptes d'escrit en un script del workflow.
2.2. Flagger
Resol el mateix amb una altra filosofia: en lloc de substituir el Deployment, l'embolcalla. Es declara un recurs Canary que referencia el Deployment existent, i Flagger orquestra el desplegament progressiu manipulant la malla de servei o l'ingress.
apiVersion: flagger.app/v1beta1
kind: Canary
metadata:
name: reservalia-api
spec:
targetRef:
apiVersion: apps/v1
kind: Deployment
name: reservalia-api
analysis:
interval: 1m
threshold: 5 # 5 fallades consecutives -> rollback
maxWeight: 50
stepWeight: 10
metrics:
- name: request-success-rate
thresholdRange: { min: 99 }
interval: 1m
- name: request-duration
thresholdRange: { max: 500 } # p99 en ms
interval: 1m
webhooks:
- name: prova-de-fum
type: pre-rollout
url: http://flagger-loadtester.test/
metadata:
cmd: "curl -sf http://reservalia-api-canary/health"Fixa't en el webhook de tipus pre-rollout: és el smoke test de la 03-02, executat contra la versió canary abans d'enviar-li trànsit real.
Diferència pràctica entre tots dos. Argo Rollouts dóna control explícit pas a pas i encaixa bé amb Argo CD; Flagger és més automàtic i menys intrusiu amb els manifestos existents. Tots dos requereixen mètriques fiables i un control de trànsit capaç (malla de servei, ingress compatible o proveïdor de trànsit suportat).
Quan NO els necessites —i és important—. Tots dos són exclusius de Kubernetes. Reservalia és a ECS: no s'apliquen. I hi ha una condició prèvia més dura que la plataforma: necessites mètriques de qualitat i volum de trànsit suficient. Un canary al 10 % en un servei amb 5 peticions per minut no genera senyal estadística: 30 peticions en cinc minuts no distingeixen una taxa d'error de l'1 % d'una del 4 %. En aquest règim, l'anàlisi automàtica és teatre i un rolling amb health checks més un rollback ràpid és honestament millor.
La condició d'entrada: trànsit suficient perquè una degradació de l'1 % sigui detectable a la finestra d'anàlisi, mètriques ja instrumentades i en què confies (03-06 feta de debò), i desplegaments prou freqüents perquè automatitzar el judici compensi configurar-ho.
- GitOps
El problema, i on va aparèixer. A la 03-02 el teu cd.yml fa push: el pipeline té credencials sobre producció i executa el desplegament. Això implica que el CI és una peça amb permisos molt alts, i que l'estat real de l'entorn pot divergir del codi sense que ningú se n'assabenti. Algú toca alguna cosa a mà a la consola, i el repositori deixa de descriure la realitat. La 03-03 en va dir drift.
GitOps inverteix la direcció: un agent dins del clúster observa un repositori i aplica el que hi hagi. El pipeline ja no desplega: només escriu al repositori de desplegaments.
graph LR
A[ci.yml<br/>construeix i publica<br/>imatge per digest] --> B[Repositori de desplegaments<br/>manifestos + digest]
B --> C{Agent GitOps<br/>Argo CD / Flux}
C -->|"reconcilia cada 3 min"| D[Cluster]
D -.->|"detecta drift"| C
C -.->|"reverteix canvis<br/>manuals"| D
El patró del repositori de desplegaments. Se separa el codi de l'aplicació dels manifestos de desplegament. El CI, en publicar la imatge, fa commit del nou digest al repositori de desplegaments; l'agent ho detecta i reconcilia. L'historial de git és l'historial de desplegaments, i revertir és un git revert.
# Argo CD: una Application que observa el repositori de desplegaments
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: reservalia-prod
spec:
project: default
source:
repoURL: https://github.com/reservalia/desplegaments.git
targetRevision: main
path: entorns/produccio
destination:
server: https://kubernetes.default.svc
namespace: reservalia
syncPolicy:
automated:
prune: true # esborra el que ja no es a git
selfHeal: true # reverteix canvis manuals al clusterselfHeal: true és el drift detection en la seva forma més contundent: si algú edita alguna cosa a mà, es desfà sol a la reconciliació següent.
Argo CD enfront de Flux. Argo CD té interfície web i és més accessible per a equips que comencen; Flux és més lleuger, més orientat a CLI i a compondre's amb altres peces. Tots dos són projectes graduats de la CNCF —amb el que això significa segons la 08-02— i l'elecció entre ells rarament és el factor decisiu.
Contrapartides. Una altra peça per operar; dos repositoris i per tant dos historials que cal saber correlacionar quan alguna cosa falla; i una depuració menys directa («per què no ha sincronitzat?» és una pregunta nova). A més, no tot hi encaixa: les migracions de base de dades de la 04-06 no són declaratives i continuen necessitant el seu propi mecanisme.
Quan NO el necessites. Sense Kubernetes, no s'aplica en la seva forma canònica. I amb un sol entorn i un equip petit, el cd.yml de la 03-02 amb OIDC fa la mateixa feina amb moltes menys peces. GitOps compensa amb diversos clústers, diversos entorns, diversos equips i quan el drift és un problema real i mesurat, no hipotètic.
- Polítiques com a codi
El problema, i on va aparèixer. A la 04-03 i la 03-03 vas establir regles: els contenidors no corren com a root, els buckets no són públics, les accions es fixen per SHA. Aquestes regles viuen avui a la revisió de codi, és a dir, a l'atenció d'un humà cansat un divendres a la tarda. Les polítiques com a codi les converteixen en una verificació automàtica que fa fallar el PR.
4.1. OPA / Rego i Conftest
OPA és un motor genèric de polítiques; Rego, el seu llenguatge; Conftest, l'eina que l'aplica a fitxers de configuració al pipeline.
# politiques/terraform.rego
package main
deny[msg] {
recurs := input.resource_changes[_]
recurs.type == "aws_s3_bucket_public_access_block"
recurs.change.after.block_public_acls == false
msg := sprintf("El bucket '%s' permet ACL publiques", [recurs.address])
}
deny[msg] {
recurs := input.resource_changes[_]
recurs.type == "aws_db_instance"
not recurs.change.after.storage_encrypted
msg := sprintf("La base de dades '%s' no te l emmagatzematge xifrat", [recurs.address])
}- name: Validar politiques d infraestructura
run: |
terraform plan -out=plan.tfplan
terraform show -json plan.tfplan > plan.json
conftest test plan.json --policy politiques/El que és potent és que s'avalua el pla, no l'estat aplicat: la política falla abans de tocar res, al PR, amb un missatge que diu quin recurs i per què. És la porta de qualitat de la 02-05 aplicada a la infraestructura.
I funciona sobre qualsevol estructura de dades, inclosos els teus propis workflows:
package main
deny[msg] {
job := input.jobs[nom]
pas := job.steps[_]
contains(pas.uses, "@v") # fa servir etiqueta en comptes de SHA
not startswith(pas.uses, "actions/")
msg := sprintf("El job '%s' fa servir una accio de tercers sense fixar per SHA: %s", [nom, pas.uses])
}Això converteix en automàtica la regla de la 07-05 que fins ara depenia que algú se'n recordés a la revisió.
4.2. Kyverno
Alternativa específica de Kubernetes que s'escriu en YAML en lloc de Rego, cosa que baixa molt la barrera d'entrada. Actua com a admission controller: rebutja recursos que violin la política en el moment d'aplicar-los al clúster.
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: exigir-imatges-per-digest
spec:
validationFailureAction: Enforce
rules:
- name: nomes-digest
match:
any:
- resources:
kinds: [Pod]
validate:
message: "Les imatges s han de referenciar per digest (sha256:), no per etiqueta."
pattern:
spec:
containers:
- image: "*@sha256:*"És la regla de l'artefacte immutable de la 02-06, aplicada pel clúster i no per la bona voluntat de qui escriu el manifest.
Contrapartides. Rego té una corba d'aprenentatge real i poc transferible. I hi ha un risc més gran: una política mal escrita bloqueja tothom. Es despleguen sempre primer en mode avís (Audit), s'observa què trenquen durant unes setmanes, i només llavors es passen a bloqueig.
Quan NO ho necessites. Amb tres persones que es revisen els PR entre elles, escriure Rego per a regles que s'apliquen dues vegades al mes és sobrecost. Compensa quan la regla és important, es repeteix molt, i hi ha més gent de la que cap en una conversa —tres o quatre equips endavant—. Excepció: si tens una sola regla crítica que no pot fallar mai, un grep en un pas del workflow l'aplica igual de bé i en dos minuts.
- Qualitat i anàlisi
El problema, i on va aparèixer. La 02-05 va muntar ESLint, Prettier, tsc i una porta de qualitat. Hi falten dues capes: anàlisi semàntica més profunda, i els linters de les mateixes eines del pipeline, que és el buit que gairebé tothom té obert.
5.1. Els linters del pipeline (comença per aquí)
Aquesta és la secció de millor relació cost/benefici de tota la lliçó. Cinc eines, totes gratuïtes, totes d'una línia:
| Eina | Verifica | Quin error real detecta |
|---|---|---|
actionlint |
Workflows de GitHub Actions | Expressions invàlides, needs a un job inexistent, shell mal escrit dins del run, contextos no disponibles en aquell esdeveniment |
yamllint |
YAML en general | Indentació, duplicats de clau, el clàssic on: interpretat com a booleà |
hadolint |
Dockerfile | apt-get install sense --no-install-recommends, capes mal ordenades, USER root oblidat, ús de latest |
tflint |
Terraform | Tipus d'instància inexistents, atributs obsolets, convencions no seguides |
shellcheck |
Scripts de shell | Variables sense cometes, comparacions mal escrites, errors que només apareixen amb un nom de fitxer amb espais |
- name: Lint del pipeline
run: |
actionlint
hadolint Dockerfile
shellcheck scripts/*.sh
tflint --recursivePer què importa més del que sembla. La 04-05 insistia que el pipeline és codi i s'ha de tractar com a tal. Aquests linters són les proves unitàries del pipeline: detecten en segons errors que, sense ells, es descobreixen després d'un cicle complet de commit, espera i fallada. actionlint en particular estalvia la majoria de les iteracions de prova i error amb YAML d'Actions.
Quan NO els necessites: mai. Són l'excepció d'aquesta lliçó: gratis, instantanis i sense contrapartida. Si t'emportes una sola eina, que sigui aquesta línia.
5.2. SonarQube / SonarCloud
Ja va aparèixer a la 02-05. Aporta anàlisi semàntica, seguiment de deute tècnic en el temps i —el més útil— el concepte de codi nou: la porta de qualitat s'aplica només al que canvia el PR, no als 200.000 errors heretats. És el que permet aplicar-lo a un legacy com el de la 05-04 sense bloquejar ningú.
Contrapartides. SonarQube autoallotjat és un servei més per mantenir, amb la seva base de dades; SonarCloud és gratuït només per a projectes públics. I genera molt soroll inicial que cal calibrar o l'equip aprèn a ignorar-lo.
5.3. Semgrep
Cerca de patrons amb consciència de sintaxi. El seu valor és que pots escriure les teves pròpies regles en minuts, amb una sintaxi semblant al codi que busques:
rules:
- id: sense-console-log-en-produccio
pattern: console.log(...)
paths:
include: ["apps/api/src/**"]
message: "Fes servir el logger estructurat, no console.log (vegeu PIPELINE.md)"
severity: WARNING
languages: [typescript]
- id: sql-per-concatenacio
pattern: |
$DB.query("..." + $VAR)
message: "Possible injeccio SQL: fes servir consultes parametritzades"
severity: ERROR
languages: [typescript]Enfront de CodeQL (04-03), Semgrep és més ràpid i molt més fàcil d'estendre; CodeQL és més profund en anàlisi de flux de dades. No competeixen tant com sembla: Semgrep per a les regles teves, CodeQL per a les vulnerabilitats genèriques.
Quan NO el necessites. Si encara no tens regles pròpies que vulguis imposar, ESLint amb els seus plugins de seguretat cobreix bona part en un projecte TypeScript. Semgrep entra quan dius «això ja ho hem comentat tres vegades a revisions».
- Cadena de subministrament
La 04-03 i la 07-05 van cobrir l'essencial. Aquí hi ha el que faltava i el marc que ho ordena.
6.1. SLSA: el marc que dóna nom al que ja vas fer
SLSA (Supply-chain Levels for Software Artifacts, slsa.dev) defineix nivells progressius d'integritat de la cadena de construcció. La idea central: poder demostrar que l'artefacte que desplegues ve del codi que creus, construït pel procés que creus.
| Nivell | Què exigeix, en essència | Què vas fer al curs |
|---|---|---|
| Nivell 1 | El procés de construcció està documentat i genera procedència | La 07-05 genera atestació de procedència |
| Nivell 2 | La construcció passa en un servei allotjat, amb procedència signada i verificable | Runners de GitHub + signatura amb Cosign: hi ets |
| Nivell 3 | A més, el procés està aïllat i la procedència és infalsificable fins i tot davant d'un constructor compromès | Requereix garanties del constructor; no s'aconsegueix només amb configuració |
La seva utilitat pràctica no és certificar-se: és tenir vocabulari per dir on ets i què et falta. En una entrevista o davant d'un client que pregunta per la cadena de subministrament, «complim SLSA nivell 2 i aquesta és l'atestació» val molt més que enumerar eines.
6.2. Sigstore i Cosign
Ja fets servir a la 07-05. El que convé entendre: la signatura sense claus (keyless). En lloc de gestionar una clau privada —el problema que arruïna la majoria d'intents de signatura—, Cosign obté un certificat efímer lligat a la identitat OIDC del workflow i registra la signatura en un log públic de transparència (Rekor).
# Signar (al workflow, sense cap clau per gestionar)
cosign sign --yes "$IMATGE@$DIGEST"
# Verificar abans de desplegar: no nomes que estigui signada,
# sino QUI la va signar i des d on
cosign verify \
--certificate-identity-regexp "https://github.com/reservalia/.*" \
--certificate-oidc-issuer "https://token.actions.githubusercontent.com" \
"$IMATGE@$DIGEST"L'error freqüent és verificar només que hi ha signatura. Sense --certificate-identity-regexp, qualsevol pot signar aquella imatge i la verificació passarà. La signatura respon «qui?», no «és bona?».
6.3. Syft, Grype, Trivy i Dependency-Track
- Syft genera SBOM (CycloneDX o SPDX) a partir d'una imatge o d'un directori.
- Grype escaneja vulnerabilitats sobre aquell SBOM.
- Trivy fa totes dues coses i a més escaneja IaC, secrets i configuració de Kubernetes. És el que vas fer servir a la 07-05 i continua sent l'elecció assenyada per consolidació.
El que és nou és Dependency-Track: un servei on emmagatzemes els SBOM de tots els teus artefactes desplegats i que t'avisa quan apareix una vulnerabilitat nova que afecta alguna cosa que ja és en producció.
- name: Publicar SBOM a Dependency-Track
run: |
curl -X POST "https://dtrack.intern.example.com/api/v1/bom" \
-H "X-Api-Key: ${{ secrets.DTRACK_API_KEY }}" \
-F "project=${{ vars.DTRACK_PROJECT_ID }}" \
-F "[email protected]"Per què això importa i és el buit real que deixa el curs. El teu escaneig de la 07-05 respon «aquesta imatge té vulnerabilitats conegudes avui?». La pregunta que trenca empreses és la inversa: «acaba de publicar-se una vulnerabilitat crítica en una llibreria, en quins dels meus 40 serveis en producció és?». Sense un inventari d'SBOM, aquesta resposta costa dies de feina manual. Amb ell, és una consulta.
Quan NO el necessites. Amb dos serveis, l'inventari cap al teu cap. A partir de deu o quinze artefactes desplegats, deixa de cabre-hi.
6.4. Renovate
Alternativa a Dependabot (04-02) considerablement més potent:
{
"extends": ["config:recommended"],
"packageRules": [
{
"matchUpdateTypes": ["minor", "patch"],
"matchCurrentVersion": "!/^0/",
"groupName": "dependencies no majors",
"automerge": true,
"schedule": ["before 6am on monday"]
},
{
"matchManagers": ["github-actions"],
"pinDigests": true,
"groupName": "accions de GitHub"
}
],
"vulnerabilityAlerts": { "labels": ["seguretat"], "automerge": false }
}Tres coses que Dependabot no fa igual de bé: agrupar actualitzacions en un sol PR (10 PR setmanals passen a 1), automerge condicional quan el CI passa, i pinDigests que fixa automàticament les accions per SHA i les manté al dia — resolent la tensió de la 08-02 entre fixar i quedar-se enrere. A més cobreix més gestors i més fitxers (Dockerfile, Terraform, workflows).
Contrapartides. Més configuració i una corba més pronunciada. I l'automerge exigeix confiar de debò en la teva suite de proves: si el teu CI té «verd fals» (04-04), acabes d'automatitzar la introducció de fallades.
Quan NO el necessites. Dependabot està integrat i basta fins que el soroll de PR sigui insuportable. El senyal per migrar: quan la gent comenci a tancar els PR de dependencies sense mirar-los.
- Proves
7.1. Testcontainers
El problema. A la 02-04 les proves d'integració feien servir una base de dades com a servei del workflow, o dobles. El primer és configuració duplicada entre local i CI; el segon menteix sobre el comportament real de PostgreSQL.
Testcontainers aixeca dependencies reals en contenidors des del codi de la prova, amb cicle de vida gestionat:
import { PostgreSqlContainer } from "@testcontainers/postgresql";
let contenidor: StartedPostgreSqlContainer;
beforeAll(async () => {
contenidor = await new PostgreSqlContainer("postgres:16-alpine").start();
process.env.DATABASE_URL = contenidor.getConnectionUri();
await executarMigracions();
}, 60_000);
afterAll(async () => { await contenidor.stop(); });
test("reserva encavalcada es rebutjada per la restriccio d exclusio", async () => {
await crearReserva({ inici: "10:00", fi: "11:00" });
await expect(crearReserva({ inici: "10:30", fi: "11:30" }))
.rejects.toThrow(/exclusion/);
});Aquest test verifica una restricció de PostgreSQL que cap doble no pot simular. I funciona igual al teu portàtil que al runner, sense duplicar configuració: és la reproduïbilitat de la 02-03 aplicada a les proves.
Contrapartides. Més lent (arrencar contenidors costa segons) i necessita Docker disponible al runner. Es mitiga reutilitzant contenidors entre suites.
7.2. Pact i les proves de contracte
El problema, de la 05-03. Amb diversos serveis, les proves E2E completes són lentes i fràgils, però sense elles ningú no sap si un canvi a l'API trenca un consumidor.
Pact inverteix el plantejament: el consumidor declara què espera, es publica aquell contracte, i el proveïdor verifica al seu propi pipeline que el compleix. Sense aixecar els dos serveis junts.
// Al pipeline del consumidor (apps/web)
await proveidor.addInteraction({
state: "existeix una reserva amb id 42",
uponReceiving: "peticio de la reserva 42",
withRequest: { method: "GET", path: "/reserves/42" },
willRespondWith: {
status: 200,
body: { id: 42, estat: like("confirmada"), inici: iso8601DateTime() }
}
});El valor real no és tècnic sinó organitzatiu: el proveïdor descobreix que trencarà algú abans de desplegar, al seu propi CI i sense coordinar-se amb ningú.
Quan NO el necessites. Amb un sol backend i un sol frontend al mateix repositori i desplegats junts —Reservalia— el contracte el garanteixen els tipus compartits de packages/compartit i unes poques proves d'integració. Pact entra quan els serveis es despleguen per separat i els equips són diferents. Abans d'això és cerimònia.
7.3. k6
Proves de càrrega com a codi, executables al pipeline:
import http from 'k6/http';
import { check } from 'k6';
export const options = {
stages: [
{ duration: '30s', target: 50 },
{ duration: '1m', target: 50 },
{ duration: '20s', target: 0 },
],
thresholds: {
http_req_duration: ['p(95)<400'], // p95 per sota de 400 ms
http_req_failed: ['rate<0.01'],
},
};
export default function () {
const res = http.get(`${__ENV.URL_BASE}/api/disponibilitat?negoci=42`);
check(res, { 'estat 200': (r) => r.status === 200 });
}L'important són els thresholds: si no es compleixen, k6 surt amb codi diferent de zero i el job falla. Això converteix el rendiment en una porta de qualitat més, no en un informe que ningú no llegeix.
On posar-lo. No a cada PR: a la branca principal després del desplegament a staging, o en una execució nocturna. És l'única manera realista de detectar una regressió de rendiment abans que la detecti un client — el problema que va aparèixer a la 07-04 amb l'endpoint lent.
Quan NO el necessites. Si el teu trànsit és lluny de qualsevol límit i no hi ha queixes de latència, és optimització prematura convertida en temps de CI.
7.4. Mutation testing (Stryker)
El problema, i és la resposta honesta a la 02-04. La cobertura mesura quines línies s'executen durant els tests, no quin comportament es verifica. Un test sense ni una asserció dóna cobertura del 100 %. És la mètrica més fàcil d'enganyar de tot el pipeline, i tothom ho sap però pocs hi fan res.
El mutation testing fa el que cap altra tècnica: introdueix fallades deliberades al teu codi (canvia un > per un >=, inverteix una condició, esborra una crida) i comprova si algun test falla. Si ningú no es queixa, aquell test no verificava res.
{
"testRunner": "vitest",
"mutate": ["apps/api/src/dominio/**/*.ts"],
"thresholds": { "high": 80, "low": 60, "break": 50 },
"incremental": true
}És, literalment, verificar pel costat negatiu —el costum que la 07-06 va assenyalar com el més valuós del curs— aplicat automàticament a la suite sencera. La primera execució sobre un projecte amb 85 % de cobertura i 45 % de mutants morts és una experiència formativa que cap argument no substitueix.
Contrapartides, i són serioses. És lentíssim: cada mutant executa la suite. Per això mutate apunta només al domini, incremental està activat, i s'executa setmanalment o sota demanda, mai a cada PR.
Quan NO el necessites. Si la teva suite és petita o la teva cobertura és baixa, arregla això primer. El mutation testing és una eina de refinament, no d'arrencada. I en codi de lampisteria (controladors, mapatges) dóna mutants supervivents irrellevants que només generen soroll.
- Desenvolupament local i reproduïbilitat
El problema. «A la meva màquina funciona», que la 02-03 va atacar des del costat del build, continua viu del costat de l'entorn de desenvolupament. I el seu cosí: el cicle de commit-esperar-fallar per depurar un workflow.
8.1. act
Executa workflows de GitHub Actions localment en Docker.
act pull_request -W .github/workflows/ci.yml -j test
act -W .github/workflows/cd.yml --secret-file .secrets.localConverteix un cicle de 6 minuts en un de 40 segons. Limitacions importants: no emula fidelment els entorns amb revisors, workflow_run, la memòria cau d'Actions ni els permisos del token. Serveix per a la lògica dels passos, no per al comportament de la plataforma. Amb això clar, estalvia molt temps.
8.2. Dev Containers
Defineix l'entorn de desenvolupament com a codi, al repositori:
{
"name": "Reservalia",
"image": "mcr.microsoft.com/devcontainers/typescript-node:20",
"features": {
"ghcr.io/devcontainers/features/docker-in-docker:2": {},
"ghcr.io/devcontainers/features/terraform:1": { "version": "1.7.5" }
},
"postCreateCommand": "npm ci",
"customizations": {
"vscode": { "extensions": ["dbaeumer.vscode-eslint", "esbenp.prettier-vscode"] }
}
}El valor mesurable: el temps d'incorporació d'algú nou passa d'un dia a mitja hora. I elimina tota la classe de bugs «jo tinc Node 18».
8.3. asdf / mise i Nix
asdf i mise fixen versions d'eines per projecte amb un fitxer declaratiu:
Barat, útil i amb adopció immediata. Nix ofereix reproduïbilitat molt més forta —fins a les dependencies del sistema— a canvi d'una corba d'aprenentatge notòria. Nix és de les poques eines d'aquesta lliçó de les quals es pot dir amb honestedat: extraordinària si l'equip sencer s'hi compromet, contraproduent si només l'entén una persona.
8.4. pre-commit
Executa verificacions abans del commit, amb un marc multillenguatge que va més enllà d'Husky (02-05):
repos:
- repo: https://github.com/gitleaks/gitleaks
rev: v8.18.2
hooks: [{ id: gitleaks }]
- repo: https://github.com/rhysd/actionlint
rev: v1.6.27
hooks: [{ id: actionlint }]Un advertiment que la 02-05 ja donava: els hooks locals són una comoditat, mai una garantia. Se salten amb --no-verify. Tot el que importi s'ha de verificar també al CI. Gitleaks en pre-commit està bé perquè evita la vergonya; Gitleaks al CI és el que et protegeix.
- Plataforma interna
Backstage (creat a Spotify, avui a la CNCF) és un portal de desenvolupament: catàleg de serveis amb propietari, plantilles per crear serveis nous ja amb pipeline, documentació tècnica al costat del codi i una interfície única sobre eines disperses.
apiVersion: backstage.io/v1alpha1
kind: Component
metadata:
name: reservalia-api
annotations:
github.com/project-slug: reservalia/monorepo
grafana/dashboard-selector: "tags @> 'reservalia-api'"
spec:
type: service
lifecycle: production
owner: equip-reserves
system: reservesLa idea que sí que importa: els golden paths. Un camí recomanat, documentat i automatitzat per fer el que és habitual. «Crear un servei nou» passa de dos dies de copiar i enganxar a deu minuts amb pipeline, observabilitat i alertes ja configurades. I —això és el que ho fa estratègic— qui se surt del camí ho pot fer, però n'assumeix el manteniment.
L'advertiment, i és el més important de la lliçó. Backstage és un projecte de desenvolupament de programari en si mateix: cal desplegar-lo, mantenir-lo, escriure plugins i actualitzar-lo. Una plataforma interna sense usuaris és un projecte de vanitat, i n'hi ha moltíssims: portals preciosos que ningú no obre perquè l'equip tenia deu serveis i ja sabia on era tot.
Quan NO el necessites. Gairebé sempre. El senyal legítim: diversos equips, desenes de serveis, i gent perdent temps real i mesurable a trobar qui és el propietari de què o a muntar serveis nous. Si pots anomenar de memòria tots els teus serveis i els seus propietaris, no el necessites.
I l'ordre correcte: primer el golden path, després el portal. Una plantilla de repositori amb el pipeline ja muntat —el que la 04-05 anomenava workflows reutilitzables— dóna el 80 % del benefici amb el 5 % del cost.
- Observabilitat
OpenTelemetry és l'estàndard transversal: un conjunt d'API, SDK i un col·lector que instrumenten traces, mètriques i logs de manera independent del backend. El seu valor no és tècnic sinó estratègic: instrumentes una vegada i canvies de proveïdor sense tocar el codi.
import { NodeSDK } from '@opentelemetry/sdk-node';
import { getNodeAutoInstrumentations } from '@opentelemetry/auto-instrumentations-node';
new NodeSDK({
serviceName: 'reservalia-api',
instrumentations: [getNodeAutoInstrumentations()],
}).start();Amb les auto-instrumentacions s'obtenen traces d'HTTP, PostgreSQL i les llibreries comunes sense escriure instrumentació manual. És l'ampliació natural de la 03-06, on les traces van quedar com el pilar menys desenvolupat.
La resta de l'ecosistema completa els pilars: Prometheus (mètriques, ja fet servir a la 07-04), Grafana (visualització), Loki (logs amb el mateix model d'etiquetes que Prometheus, cosa que permet saltar d'un pic als seus logs), Tempo (traces). Sentry cobreix una altra cosa diferent i complementària: errors d'aplicació amb traça de pila, agrupació intel·ligent i context de la versió desplegada — respon «quina excepció i en quina línia», que ni les mètriques ni els logs contesten bé.
Mètriques DORA automatitzades. Hi ha eines específiques per calcular-les, però la conclusió honesta —la mateixa de la 07-04— és que les quatre mètriques es calculen amb consultes a l'API de la teva forja i del teu sistema d'incidents, i que fer-ho tu t'obliga a definir amb precisió què comptes com a desplegament i com a fallada. Aquesta definició és la meitat del valor, i una eina que la pren per tu te la roba.
Quan NO ho necessites. OpenTelemetry complet amb col·lector propi és sobrecost per a un servei petit on les mètriques de la 07-04 i uns logs estructurats basten. Comença per les quatre senyals d'or i afegeix traces quan tinguis un problema de latència distribuïda que no sàpigues explicar.
- Taula d'adopció per mida d'equip
| Eina | 3 persones | 15 persones | 50+ persones |
|---|---|---|---|
Linters de pipeline (actionlint, hadolint, shellcheck) |
Imprescindible | Imprescindible | Imprescindible |
| Trivy / escaneig d'imatge | Imprescindible | Imprescindible | Imprescindible |
| Dependabot | Imprescindible | Migrar a Renovate | Renovate |
| Gestor de secrets del núvol | Recomanable | Imprescindible | Imprescindible |
| Testcontainers | Recomanable | Recomanable | Recomanable |
| Cosign + SBOM | Recomanable | Imprescindible | Imprescindible |
| Dev Containers / mise | Recomanable | Recomanable | Imprescindible |
act |
Útil | Útil | Útil |
| Semgrep amb regles pròpies | Prematur | Recomanable | Imprescindible |
| SonarQube | Prematur | Recomanable | Recomanable |
| k6 al pipeline | Prematur | Recomanable | Imprescindible |
| Dependency-Track | Prematur | Recomanable | Imprescindible |
| Mutation testing | Prematur | Útil, setmanal | Útil, setmanal |
| Pact | Llast | Recomanable si hi ha serveis separats | Imprescindible |
| GitOps (Argo CD / Flux) | Llast | Recomanable amb Kubernetes | Imprescindible amb Kubernetes |
| Lliurament progressiu (Flagger / Rollouts) | Llast | Depèn del trànsit | Recomanable |
| Polítiques com a codi (OPA / Kyverno) | Llast | Útil per a 2-3 regles crítiques | Imprescindible |
| Vault autoallotjat | Llast | Prematur tret que hi hagi requisit | Recomanable |
| Backstage | Llast | Prematur | Recomanable si hi ha dolor real |
| OpenTelemetry complet | Prematur | Recomanable | Imprescindible |
| Nix | Llast tret de compromís total | Depèn | Depèn |
Com llegir «llast». No vol dir mala eina: vol dir que en aquella mida el cost d'operar-la supera el benefici. Vault en un equip de tres no és un Vault petit; és un Vault sencer mantingut per gent que també escriu el producte. La complexitat no escala cap avall.
- El criteri per deixar entrar una eina
Tres preguntes. Si alguna no té resposta, l'eina no entra.
1. Quin problema mesurat resol?
Mesurat és la paraula clau. No «millora la seguretat» sinó «el mes passat vam perdre 6 hores rotant un secret filtrat, i això ho hauria evitat». Si no pots citar un incident, un temps perdut o una mètrica que empitjora, no tens un problema: tens curiositat. La curiositat és legítima —explora-la en un projecte personal, no al pipeline de producció—.
2. Qui la manté?
Doble pregunta. Fora: qui desenvolupa el projecte? Una empresa, una fundació, una persona? Quan va ser l'últim commit? Un projecte CNCF graduated (08-02) és una aposta molt diferent d'un repositori personal amb dotze estrelles. Dins: qui del teu equip la manté quan falli un divendres? Si la resposta és «el qui la va portar», tens un factor bus d'un sobre una peça del camí a producció.
3. Què passa si la traiem?
Si la resposta és «res greu», no hauria d'haver entrat. Si és «no podem desplegar», necessites un pla de sortida abans d'adoptar-la: com se'n surt, quant costa, quines dades cal migrar. Les eines s'adopten en una tarda i s'abandonen durant anys.
I una quarta, opcional però molt reveladora: podríem aconseguir el 80 % del benefici amb 20 línies de script? Amb sorprenent freqüència, sí. Un grep en un pas del workflow substitueix una política d'OPA per a una sola regla. Una plantilla de repositori substitueix Backstage al principi. La resposta no sempre és «fes servir l'script», però la pregunta t'obliga a anomenar què aporta l'eina més enllà del que és obvi.
Errors Comuns i Consells
- Adoptar per moda i no per problema. L'error central del mòdul. Es reconeix per un senyal: no pots explicar en una frase què deixarà de fer mal.
- Adoptar-ne diverses alhora. Quan alguna cosa es trenca no saps quina va ser. Una eina cada vegada, amb almenys dues setmanes d'ús real abans de la següent.
- Confondre «ho fa servir una empresa gran» amb «em serveix». Ells tenen equip de plataforma dedicat. És el senyal d'alarma de la 08-02 aplicat a les eines.
- Ficar eines lentes al camí crític. Mutation testing o k6 a cada PR converteixen un CI de 4 minuts en un de 40, i l'equip comença a saltar-se'l. El que és lent va fora del PR: nocturn, setmanal o post-merge (04-04).
- Desplegar polítiques en mode bloqueig des del primer dia. Mode avís primer, sempre. Una política mal escrita bloqueja tothom i crema la idea sencera durant mesos.
- Verificar signatures sense verificar identitat.
cosign verifysense--certificate-identity-regexpdóna una sensació de seguretat falsa. Qualsevol pot signar. - Automerge sense confiar en la suite. Renovate amb automerge sobre un CI amb verd fals automatitza la introducció de fallades.
- Construir la plataforma abans que el camí. Primer la plantilla i el workflow reutilitzable; el portal després, i només si fa mal.
- Consell: comença sempre pels linters del pipeline. Cinc minuts, cost zero, benefici immediat. No hi ha cap altra eina d'aquesta lliçó amb aquesta relació.
- Consell: documenta cada adopció al
PIPELINE.mdamb el format de la 07-06: quin problema, què es va descartar, quina condició obligaria a revisar-ho. El teu jo d'aquí a dos anys necessita saber per què això és allà. - Consell: escriu també els descarts. «Vam avaluar Vault al març i el vam descartar perquè X» estalvia que algú reobri la discussió cada sis mesos.
Exercicis
Exercici 1 — Tres equips, tres decisions
Per a cada situació, decideix quina eina (o cap) d'aquesta lliçó adoptaries, justifica-ho amb les tres preguntes de l'apartat 12, i digues què descartes i per què:
a) Equip de 4 persones, monòlit Node.js a ECS, un entorn. Han patit dos incidents en tres mesos per credencials de base de dades rotades malament: algú canvia la contrasenya a RDS i s'oblida d'actualitzar-la en algun lloc. Pressupost d'eines: baix però no nul.
b) Equip de 18 persones, 12 microserveis a Kubernetes, despleguen unes 30 vegades per setmana. Quan es publica una vulnerabilitat crítica en una llibreria popular, triguen entre un i dos dies a saber quins serveis estan afectats.
c) Equip de 6 persones, aplicació web amb 400 usuaris interns. El tech lead proposa muntar Backstage «perquè tot estigui organitzat i perquè és el que es porta».
Exercici 2 — El pla d'adopció de Reservalia
Reservalia és avui on va acabar el mòdul 4: 12 desplegaments/setmana, 3,5 h de lead time, 3,8 % de CFR, 9 min de restore. Equip de tres. ECS Fargate, GitHub Actions, sense Kubernetes. En Diego continua vigilant cada euro.
Tria tres eines d'aquesta lliçó per adoptar en els propers sis mesos, en ordre, i per a cadascuna: quina mètrica DORA (o quin altre dolor concret) pretén moure, com li ho justificaries a en Diego, i quin senyal indicaria que l'adopció va ser un error.
Exercici 3 — El descart raonat
Tria una eina d'aquesta lliçó que et resulti atractiva i que hagis decidit no adoptar en el teu context. Escriu l'entrada de PIPELINE.md corresponent, amb el format de la 07-06: quin problema resoldria, per què es descarta avui, quina alternativa més barata es fa servir al seu lloc, i la condició concreta i observable que obligaria a reobrir la decisió.
Solucions
Solució 1
a) Equip de 4, incidents per rotació de credencials.
Adoptar: AWS Secrets Manager, amb l'aplicació llegint el secret per referència des de la definició de tasca d'ECS.
- Quin problema mesurat? Dos incidents en tres mesos amb causa idèntica. La causa arrel no és el descuit: és que el mateix valor viu en tres llocs i la consistència depèn que algú recordi els tres. És un problema de disseny, i eliminar la duplicació l'elimina.
- Qui la manté? AWS. Servei gestionat, sense operació per part de l'equip. Rotació automàtica configurable amb Lambda si més endavant cal.
- Què passa si la traiem? Es torna a l'estat anterior amb un canvi a la definició de tasca. Sortida barata i sense migració de dades.
Descartat: Vault. Resoldria el mateix i més, però exigeix operar un sistema crític amb quatre persones que ja estan al 100 %. És exactament el cas de «llast» de la taula de l'apartat 11.
Descartat: SOPS. Versionaria els secrets, però no resol el problema declarat —l'aplicació continuaria llegint el valor des de la configuració desplegada, i la rotació continuaria sent un canvi coordinat—.
b) 18 persones, 12 microserveis, un o dos dies per saber si estan afectats.
Adoptar: Dependency-Track, alimentat amb els SBOM que el pipeline ja hauria d'estar generant amb Syft o Trivy.
- Quin problema mesurat? De 24 a 48 hores de feina manual per cada vulnerabilitat crítica publicada. És temps car de gent cara, és recurrent i —el més greu— és temps durant el qual estàs exposat sense saber-ho.
- Qui la manté? Projecte OWASP, amb comunitat establerta. Dins: cal assignar-hi propietari; amb 18 persones és viable.
- Què passa si la traiem? Es torna a l'escaneig per artefacte i a la resposta manual. Els SBOM continuen generant-se, així que la sortida no perd res.
La clau del raonament: el seu escaneig actual respon «aquesta imatge és segura avui?» i el seu problema és l'invers, «on és aquesta llibreria ara mateix?». Cap quantitat d'escaneig al pipeline no respon això; cal inventari.
Complement raonable: Renovate amb agrupació, perquè amb 12 serveis el soroll de Dependabot és alt i la majoria d'aquestes vulnerabilitats es tanquen actualitzant.
c) 6 persones, 400 usuaris interns, proposta de Backstage.
Cap. Rebutjar la proposta, i fer-ho amb les tres preguntes en comptes d'amb una opinió:
- Quin problema mesurat? Cap de declarat. «Que tot estigui organitzat» no és un problema: és una aspiració estètica. La pregunta que tanca la conversa: quantes hores hem perdut els últims tres mesos buscant qui és el propietari d'un servei, o muntant un servei nou? Amb sis persones, la resposta serà propera a zero, perquè el catàleg cap en una conversa.
- «Perquè és el que es porta» és literalment el senyal d'alarma de la 08-02 i l'antipatró central d'aquesta lliçó.
- Què passa si el traiem? Res. Que la resposta sigui «res» abans ni tan sols d'adoptar-lo és la prova que no ha d'entrar.
Contraproposta constructiva, perquè rebutjar sense alternativa sol fracassar: si el que de debò molesta és la inconsistència entre serveis, el 80 % del benefici és en una plantilla de repositori amb el pipeline ja muntat i un CODEOWNERS ben mantingut (02-07, 04-05). Cost: una tarda. I si d'aquí a un any hi ha 30 serveis i gent perdent-s'hi, Backstage entra amb un problema mesurat al darrere.
Solució 2
Hi ha diverses combinacions defensables. Aquesta prioritza cost zero, risc baix i benefici verificable, que és el que el context de Reservalia exigeix.
Primera: els linters del pipeline (actionlint, hadolint, shellcheck, tflint). Mes 1.
- Què mou: lead time. Cada iteració de prova i error amb YAML d'Actions costa un cicle complet de CI.
actionlintels detecta en segons, en local i al PR. - A en Diego: cost zero en euros, uns 15 segons de CI, i una tarda de configuració. L'argument que el convenç és de minuts de runner: menys execucions fallides per errors de sintaxi és literalment menys despesa.
- Senyal d'error: si genera avisos que l'equip silencia sistemàticament, la configuració està mal calibrada, no l'eina. S'ajusten regles, no es treu.
Segona: Testcontainers per a les proves d'integració. Mesos 2-3.
- Què mou: taxa de fallada de canvis (3,8 %). Les fallades que arriben a producció en un sistema amb bona cobertura unitària solen venir de la frontera amb la base de dades: restriccions, transaccions, comportament de PostgreSQL que cap doble no reprodueix. I de passada redueix el temps de restauració, perquè són fallades que es detecten abans.
- A en Diego: gratis, i l'argument és de cost evitat. Un incident de producció a Reservalia costa més en temps de les tres persones que tot el CI del mes. Es pot quantificar mirant els últims tres incidents i preguntant quants n'hauria caçat un test contra PostgreSQL real.
- Senyal d'error: si el CI s'allarga més de dos minuts i no apareix cap fallada capturada en tres mesos, sobra. Es mesura abans i després.
Tercera: k6 amb thresholds, després del desplegament a staging, no a cada PR. Mesos 4-6.
- Què mou: taxa de fallada i temps de restauració, en el mode de fallada que pitjor porta Reservalia: la degradació gradual. Un endpoint que passa de 200 ms a 900 ms no trenca cap test i no dispara cap alerta fins que l'SLO comença a consumir-se. És exactament el que va aparèixer a la 07-04.
- A en Diego: gratis, executat una vegada per desplegament a staging, uns 2 minuts fora del camí crític del PR. I l'argument de negoci és fort: la finestra crítica de les reserves és on hi ha més càrrega i on una caiguda costa clients —la restricció 3 de l'encàrrec de la 07-06—.
- Senyal d'error: si els llindars fallen constantment per soroll de l'entorn de staging i la gent comença a reintentar el job sense mirar, el problema és l'entorn o els llindars. Un llindar que s'ignora és pitjor que no tenir-lo, perquè ensenya l'equip a ignorar portes.
Descartats explícitament i per què: Vault (llast per a tres persones), GitOps i lliurament progressiu (exigeixen Kubernetes; Reservalia és a ECS), Backstage (sense problema mesurat), polítiques com a codi (amb tres persones les regles caben a la revisió; si alguna és crítica, un grep al workflow basta), mutation testing (interessant, però la prioritat és que els tests capturin fallades reals abans de refinar els que ja hi ha).
Solució 3
Exemple, sobre lliurament progressiu automatitzat:
## Decisió 14 — Lliurament progressiu automatitzat (Flagger / Argo Rollouts)
**Data:** 2026-08-02
**Estat:** Descartat de moment
**Problema que resoldria.** Avui el desplegament a producció és rolling amb
health checks, i la decisió de «això va malament, reverteix» la pren una persona
mirant Grafana durant els deu minuts següents. Això depèn que
algú miri, el criteri és subjectiu i a la pràctica es promociona per
impaciència. Flagger o Argo Rollouts convertirien aquell judici en llindars
declarats amb anàlisi automàtica de mètriques.
**Per què es descarta avui.** Dos motius, i el segon és el decisiu:
1. Totes dues eines són exclusives de Kubernetes. Reservalia corre a ECS
Fargate i adoptar-les implicaria migrar la plataforma sencera — un cost
desproporcionat per al problema que resolen.
2. Encara que fóssim a Kubernetes, **no tenim volum de trànsit perquè
l'anàlisi sigui estadísticament significativa**. Amb el nostre pic
actual, un canary al 10 % durant 5 minuts rep unes desenes de
peticions: no distingeix una taxa d'error de l'1 % d'una del 4 %.
L'anàlisi automàtica donaria una falsa sensació de rigor.
**Alternativa en ús.** Rolling amb health checks + rollback per digest en
~4 minuts (decisió 6) + alerta per símptoma sobre l'SLO (decisió 11). El
temps de restauració mesurat és de 9 minuts, dins de l'objectiu.
**Condició de revisió.** Es reobre aquesta decisió si es compleixen totes dues:
- (a) el trànsit sostingut supera les 500 peticions/minut a la finestra de
desplegament habitual, de manera que un canary al 10 % generi senyal en 5 min; i
- (b) migrem a Kubernetes per un altre motiu independent, o adoptem una
eina equivalent compatible amb ECS.
Mentre només es compleixi (a), l'acció correcta és allargar la finestra
d'observació postdesplegament i automatitzar el rollback per mètriques dins
del mateix `cd.yml`, no canviar de plataforma.El que fa vàlida aquesta entrada no és el descart, sinó que la condició de revisió sigui observable i no opinable: «500 peticions per minut» es comprova a Grafana; «quan tinguem més trànsit» no es comprova mai i garanteix que la discussió es reobri cada sis mesos sense dades noves.
Conclusió
Aquest mòdul sencer s'ha ordenat per una idea que convé deixar dita amb claredat: una eina és una resposta, i només té sentit si existeix la pregunta. Totes les d'aquesta lliçó són bones; cap no és bona per a tothom.
L'essencial:
- Secrets: el millor secret és el que no existeix (OIDC, rols). Quan l'aplicació necessita secrets en execució, el gestor del núvol és la resposta assenyada; Vault entra amb diversos núvols o requisits d'auditoria; SOPS+age és una opció infravalorada per versionar configuració xifrada.
- Lliurament progressiu i GitOps resolen problemes reals de la 03-04 i la 03-02, però exigeixen Kubernetes i, en el cas del canary automàtic, trànsit suficient perquè les mètriques signifiquin alguna cosa.
- Polítiques com a codi converteixen regles de revisió en verificacions automàtiques: valuoses a partir de diversos equips, sobrecost abans, i sempre desplegades primer en mode avís.
- Els linters del pipeline són l'única recomanació sense contrapartida de tota la lliçó. Cinc minuts, zero euros, benefici immediat.
- Cadena de subministrament: SLSA dóna el vocabulari per dir on ets; verificar signatura amb identitat és el que fa que signar serveixi; i l'inventari d'SBOM respon la pregunta que trenca empreses, que no és «és segura aquesta imatge?» sinó «on és aquesta llibreria ara?».
- Proves: Testcontainers acosta les proves a la realitat, k6 converteix el rendiment en porta, i el mutation testing és la resposta honesta a la mentida de la cobertura — és verificar pel costat negatiu, automatitzat.
- Plataforma interna: primer el golden path, després el portal. Una plataforma sense usuaris és un projecte de vanitat.
- I el criteri, que val més que el catàleg: quin problema mesurat resol?, qui la manté dins i fora?, què passa si la traiem? Més la quarta, incòmoda i reveladora: bastarien vint línies de script?
La taula d'adopció per mida diu una cosa que val la pena repetir: la complexitat no escala cap avall. Vault en un equip de tres no és un Vault petit. Backstage amb sis serveis no és un Backstage lleuger. Copiar l'arquitectura d'una empresa de cinc-centes persones sense tenir-ne els problemes et deixa amb els seus costos i sense els seus beneficis.
Ja tens el fonament (08-01), el context (08-02) i el catàleg (08-03). Falta la pregunta que cap dels tres no respon: cap on vas tu? Què saps fer ara i què no, en quina direcció aprofundir, quines certificacions serveixen d'alguna cosa i quines no, com demostrar el que saps sense un paper, i com mantenir-se al dia sense cremar-se.
Aquesta és l'última lliçó del curs: 08-04, Ruta d'Aprenentatge, Certificacions i Propers Passos.
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
