El que queda ja no és construir, sinó lliurar. La xarxa de Ribalta funciona, és observable, resisteix i està empaquetada, però continua vivint en màquines de desenvolupament: ningú de la ciutat no pot llogar encara una bicicleta. Aquesta lliçó és la frontissa del mòdul 8. No conté els passos de cap plataforma concreta —això arriba a les quatre següents— sinó les decisions que cal prendre abans de tocar cap consola, perquè són les mateixes a Heroku, a AWS i a Kubernetes, i equivocar-s'hi surt molt més car que triar malament el proveïdor.
Veurem què canvia exactament quan una aplicació passa del teu portàtil a un servidor que atén ciutadans reals, quin artefacte es desplega i per què CicloUrbana viatja com a imatge, els dotze factors aplicats un a un al nostre projecte, els models d'allotjament amb els seus costos i compromisos, l'anatomia completa d'un entorn de producció, una llista de comprovació raonada de tot el que cal decidir —secrets, migracions, còpies de seguretat, memòria de la JVM, zona horària, HTTPS, pool de connexions, aturada ordenada— i les quatre estratègies de desplegament amb allò que cadascuna exigeix de l'aplicació.
Contingut
- Què significa desplegar
- L'artefacte: JAR, WAR o imatge
- Construir una vegada, desplegar a molts llocs
- Els dotze factors aplicats a CicloUrbana
- Models d'allotjament
- Anatomia d'un entorn de producció
- Decisions prèvies al desplegament
- Estratègies de desplegament
- Estat, sessions i escalat horitzontal
- Saber si un desplegament ha anat bé, i revertir
- Entorns i paritat
- Errors Comuns i Consells
- Exercicis
- Què significa desplegar
Executar ./mvnw spring-boot:run al teu portàtil i desplegar CicloUrbana a Ribalta s'assemblen en una sola cosa: en tots dos casos arrenca una JVM. Tota la resta canvia.
| Aspecte | En desenvolupament | En producció |
|---|---|---|
| Qui arrenca el procés | Tu, a mà | Un supervisor, un orquestrador o una plataforma |
| Què passa si el procés mor | Ho veus i el rellances | Ningú no ho veu: s'ha de reiniciar sol |
| Quantes instàncies hi ha | Una | Diverses, i canviants |
| On és la base de dades | En un contenidor local, llencable | Gestionada, amb dades reals i irrecuperables si es perden |
| Qui pot cridar l'API | Tu | Qualsevol amb connexió a Internet |
| Quant duren les dades | Fins al pròxim docker compose down -v |
Anys |
| Com es veu un error | A la consola | En un agregador de logs, potser hores després |
| Cost d'una fallada | Perdre cinc minuts | Ciutadans sense bicicleta i una trucada de l'ajuntament |
| Cost econòmic | Zero | Facturat per hora, hi hagi trànsit o no |
Desplegar és, per tant, fer que una versió concreta del programari estigui disponible i operativa per als seus usuaris reals, de manera repetible i reversible. Les tres qualitats d'aquesta definició són les que importen:
- Disponible i operativa: no n'hi ha prou que el procés estigui viu; ha d'atendre peticions correctament, amb la seva base de dades, els seus secrets i la seva configuració.
- Repetible: si el desplegament depèn que algú recordi una seqüència de passos, no és un desplegament, és una cerimònia. L'objectiu del mòdul és que la seqüència estigui escrita i l'executi una màquina (08-05).
- Reversible: tot desplegament pot sortir malament. Un desplegament del qual no es pot tornar enrere en minuts és una aposta, no un lliurament.
Convé separar tres termes que s'utilitzen com a sinònims i no ho són:
| Terme | Què és |
|---|---|
| Construcció (build) | Convertir el codi font en un artefacte: ./mvnw package, docker build |
| Publicació (release) | Combinar l'artefacte amb la configuració d'un entorn concret i donar-li un identificador |
| Desplegament (deploy) | Posar aquesta publicació en execució substituint l'anterior |
Aquesta separació no és acadèmica: és el cinquè dels dotze factors de l'apartat 4 i explica per què l'artefacte no pot portar dins la configuració de cap entorn.
- L'artefacte: JAR, WAR o imatge
La primera decisió és quina cosa exactament es copia al servidor. Hi ha tres respostes històriques i només dues continuen sent raonables.
| JAR executable | WAR en servidor d'aplicacions | Imatge de contenidor | |
|---|---|---|---|
| Què conté | Classes, dependències i un servidor incrustat (Tomcat) | Classes i dependències, sense servidor | Sistema de fitxers complet: JRE, aplicació, zona horària, certificats |
| Com s'executa | java -jar ciclourbana.jar |
Es desplega en un Tomcat/WildFly extern | docker run o l'orquestrador |
| Qui aporta el JRE | La màquina amfitriona | La màquina amfitriona | La mateixa imatge |
| Mida típica | 55-70 MB | 40-55 MB | 250-350 MB (amb capes compartides) |
| Aïllament | Cap | Comparteix JVM amb altres aplicacions | Procés, xarxa i sistema de fitxers aïllats |
| Reproductibilitat | Depèn de la versió de Java instal·lada | Depèn del servidor i la seva configuració | Alta: l'entorn viatja dins |
| Reversió | Desar el JAR anterior | Redesplegar el WAR anterior | Arrencar l'etiqueta anterior |
| On es desplega | VPS, PaaS, systemd | Servidors corporatius heretats | PaaS, ECS, Kubernetes, qualsevol lloc |
| Estat el 2026 | Vigent i perfectament vàlid | Només per obligació organitzativa | L'estàndard de la indústria |
Per què el WAR és pràcticament mort. Requereix convertir l'aplicació en un war (<packaging>war</packaging>, estendre SpringBootServletInitializer, marcar Tomcat com a provided), i a canvi s'hereta tota l'operativa del servidor d'aplicacions: la seva versió de Java, la seva configuració global, els seus reinicis que afecten diverses aplicacions alhora, i la vella font d'errors de les biblioteques compartides entre desplegaments. L'única raó legítima per fer-lo servir avui és que la destinació sigui un servidor corporatiu que no es pot canviar.
Per què CicloUrbana es desplega com a imatge. El JAR és una opció digna —Heroku el fa servir a 08-02, i Elastic Beanstalk també— però deixa fora de la caixa una part de l'entorn: quina versió exacta del JRE hi ha a la màquina, quina zona horària té el sistema, quins certificats arrel coneix, quin locale està configurat. La imatge que vam construir a 07-04 fica tot això dins i amb això aconsegueix tres coses que el mòdul necessita:
- El mateix artefacte s'executa idèntic al portàtil, a
prei aprod. No hi ha cap «funcionava a preproducció». - És la unitat que entenen les plataformes dels pròxims capítols: ECS Fargate (08-03), Kubernetes (08-04) i les canalitzacions de lliurament (08-05) despleguen imatges, no fitxers.
- La reversió és trivial i exacta:
ciclourbana:2.3.0continua existint al registre, byte a byte, i tornar-hi no reconstrueix res.
La regla pràctica: construeix sempre el JAR —és el pas intermedi— i publica la imatge com a artefacte de desplegament.
- Construir una vegada, desplegar a molts llocs
Aquest principi ja va aparèixer a 07-02, però aquí és on es torna operatiu. Enunciat formalment:
L'artefacte es construeix una sola vegada, a partir d'un commit concret, i aquesta mateixa còpia binària recorre
preiprodsense tornar-se a compilar. L'única cosa que canvia entre entorns és la configuració injectada des de fora.
flowchart LR
C[commit 9f3a2b1] --> B[Construcció única<br/>ciclourbana:2.4.0]
B --> R[(Registre d'imatges)]
R --> P1[pre<br/>config de pre]
R --> P2[prod<br/>config de prod]
P1 -->|mateix binari| OK1[Provat aquí...]
P2 -->|mateix binari| OK2[...és el que corre aquí]
Per què importa tant. Si es recompila per a producció, el que es desplega és un binari que ningú no ha provat. Pot diferir per una dependència que va resoldre una altra versió, per un perfil de Maven diferent, per una variable de l'entorn de construcció, o simplement perquè entre les dues compilacions algú va fer un commit. Les proves del mòdul 6 es van executar sobre un artefacte i a Ribalta n'arriba un altre: la garantia s'evapora.
La conseqüència directa: l'artefacte no pot portar configuració d'entorn a dins. Res d'application-prod.yml amb la contrasenya real empaquetat al JAR, res d'un Dockerfile amb ENV SPRING_PROFILES_ACTIVE=prod, res de perfils de Maven que produeixin un JAR «de producció» diferent del «de pre». Si el binari conté una decisió d'entorn, deixa de ser un binari i passa a ser-ne diversos.
El que sí que pot —i ha de— viatjar a dins:
| Va dins de l'artefacte | Va fora, a l'entorn |
|---|---|
| El codi i les dependències | Contrasenyes, secrets, claus d'API |
Els application-*.yml sense secrets (valors per entorn) |
Quin perfil s'activa (SPRING_PROFILES_ACTIVE) |
| Les migracions de Flyway | URL i credencials de la base de dades |
| Els valors per defecte raonables | Mides de pool, límits de memòria, nivell de log |
| El JRE i la zona horària (a la imatge) | Adreces de serveis externs |
Aquest és el motiu pel qual a 07-02 els fitxers de perfil es van versionar buits de secrets, amb marcadors ${JWT_SECRET} sense valor per defecte: el fitxer descriu la forma de la configuració, l'entorn aporta el contingut perillós.
- Els dotze factors aplicats a CicloUrbana
The Twelve-Factor App és una metodologia publicada el 2011 per l'equip de Heroku que descriu com s'ha de construir una aplicació per ser desplegable, escalable i operable en plataformes modernes. Quinze anys després continua sent la millor llista de comprovació prèvia a un desplegament. Aplicada al nostre projecte:
| # | Factor | Què exigeix | Situació de CicloUrbana |
|---|---|---|---|
| 1 | Codi base | Un repositori, molts desplegaments | Un repositori Git; pre i prod són desplegaments del mateix codi en un commit diferent |
| 2 | Dependències | Declarades explícitament, mai implícites del sistema | pom.xml amb versions gestionades pel BOM de Spring Boot; res instal·lat a mà al servidor |
| 3 | Configuració | A l'entorn, no al codi | SPRING_PROFILES_ACTIVE, JWT_SECRET, SPRING_DATASOURCE_* com a variables (07-02) |
| 4 | Serveis de suport | PostgreSQL, correu o pagaments són recursos connectables, intercanviables per URL | Canviar de PostgreSQL local a RDS és canviar SPRING_DATASOURCE_URL, sense tocar codi |
| 5 | Construir, publicar, executar | Tres etapes separades i estrictes | mvn verify → docker build+push → desplegament d'una etiqueta concreta |
| 6 | Processos | Sense estat, sense res per compartir en memòria o disc local | La sessió no existeix: el JWT de 05-04 porta la identitat; res no es desa a disc |
| 7 | Assignació de ports | L'aplicació exposa el seu servei per un port, ella mateixa | Tomcat incrustat al 8080, gestió al 8081 (07-01); no hi ha servidor extern que la contingui |
| 8 | Concurrència | Escalar afegint processos, no fils dins d'un procés gegant | S'afegeixen rèpliques de la imatge darrere del balancejador |
| 9 | Llencabilitat | Arrencada ràpida i aturada ordenada | SIGTERM → graceful shutdown de 01-05, amb preStop i període de gràcia folgat |
| 10 | Paritat d'entorns | dev, pre i prod tan semblants com sigui possible |
Testcontainers amb PostgreSQL 16 a les proves (06-05) i PostgreSQL 16 gestionat en producció |
| 11 | Logs | Un flux d'esdeveniments a stdout, que la plataforma recull |
Logback escriu a consola; res de fitxers rotats per l'aplicació (s'amplia a 09-05) |
| 12 | Processos d'administració | Tasques puntuals com a processos efímers del mateix codi | Les migracions de Flyway com a pas previ del desplegament, amb la mateixa imatge |
Val la pena aturar-se en els tres que trenquen més desplegaments:
Factor 3 (configuració). La prova del cotó és senzilla: podries fer públic el repositori ara mateix sense comprometre res? Si la resposta és no, la configuració és al codi.
Factor 6 (processos sense estat). El dia que una instància desa en memòria alguna cosa que una altra necessita —un carret, una sessió, una memòria cau d'escriptura, un fitxer pujat a /tmp— l'escalat horitzontal deixa de funcionar i apareixen errors intermitents impossibles de reproduir: depenen de a quina instància hagi anat a parar la petició.
Factor 11 (logs com a flux). L'aplicació no ha d'obrir fitxers, no els ha de rotar i no ha de saber on acaben els seus missatges. Escriu a stdout i és la plataforma —Heroku, CloudWatch, l'agent de Kubernetes— qui els recull. Si l'aplicació escriu a /var/log/ciclourbana.log dins d'un contenidor, aquests logs desapareixen amb el contenidor.
- Models d'allotjament
| Model | Què gestiones tu | Esforç operatiu | Cost típic (mes) | Quan triar-lo |
|---|---|---|---|---|
| Servidor propi / VPS | Sistema operatiu, Java, actualitzacions, seguretat, TLS, backups, arrencada | Molt alt | 5-40 € | Pressupost mínim, una sola instància, algú que sàpiga administrar Linux |
| PaaS (Heroku, Render, Railway) | Només el codi i les variables | Molt baix | 25-120 € | Primer desplegament, equips petits, temps escàs (08-02) |
| Contenidors gestionats (ECS Fargate, Cloud Run, App Runner) | La imatge i la seva definició de tasca | Baix-mitjà | 60-250 € | Producció real sense voler operar un orquestrador (08-03) |
| Kubernetes (EKS, GKE, AKS) | Manifestos, versions del clúster, complements | Alt | 200 € + clúster | Molts serveis, diversos equips, requisits de portabilitat (08-04) |
| Funcions sense servidor (Lambda) | Només el codi de cada funció | Baix | Per invocació | Càrregues esporàdiques i molt irregulars |
Què triaria un projecte com CicloUrbana en cada moment de la seva vida:
- Prototip i demostració a l'ajuntament: un PaaS. Un
git pushi ja hi ha URL amb HTTPS. El cost d'aprenentatge és gairebé zero i el focus continua al producte. - Servei real de la ciutat, un sol equip: contenidors gestionats. És el punt d'equilibri: infraestructura seriosa (xarxa privada, base de dades gestionada, balancejador, autoescalat) sense la càrrega d'operar un clúster. És l'opció que 08-03 desenvolupa en detall.
- La xarxa creix a diversos serveis i equips: Kubernetes, quan el cost de coordinar desplegaments superi el d'aprendre l'orquestrador.
- Mai, en el nostre cas: funcions sense servidor. Una aplicació Spring Boot amb un pool de connexions a PostgreSQL encaixa malament en un model de processos efímers; l'arrencada en fred de la JVM (mitigada, no eliminada, per SnapStart) i la multiplicació de connexions a la base de dades són problemes reals.
I un advertiment que travessa tot el mòdul: aquests serveis es facturen, gairebé sempre per hora de recurs actiu i no per trànsit. Una base de dades gestionada i un balancejador costen el mateix amb zero peticions que amb mil. Quan acabis una pràctica de 08-02, 08-03 o 08-04, destrueix els recursos. Al final de cada lliçó hi ha una llista exacta de què cal eliminar.
- Anatomia d'un entorn de producció
Un desplegament seriós mai no és «un servidor amb l'aplicació». És un conjunt de peces amb responsabilitats separades:
flowchart TD
U[Ciutadans de Ribalta] --> DNS[DNS<br/>ciclourbana.ribalta.example]
DNS --> LB[Balancejador<br/>terminació TLS · certificat]
LB --> A1[CicloUrbana rèplica 1]
LB --> A2[CicloUrbana rèplica 2]
LB --> A3[CicloUrbana rèplica 3]
A1 --> BD[(PostgreSQL 16 gestionat<br/>xarxa privada · backups)]
A2 --> BD
A3 --> BD
A1 -.llegeix en arrencar.-> S[Magatzem de secrets]
A2 -.-> S
A3 -.-> S
REG[(Registre d'imatges<br/>ciclourbana:2.4.0)] -.desplega.-> A1
A1 -.logs i mètriques.-> O[Observabilitat]
A2 -.-> O
A3 -.-> O
| Peça | Responsabilitat | Què passa si falta |
|---|---|---|
| DNS | Traduir el nom públic a l'adreça del balancejador | Els usuaris haurien de conèixer una IP que a més canvia |
| Balancejador / terminació TLS | Repartir trànsit, comprovar salut, tancar l'HTTPS | Sense ell no hi ha diverses rèpliques ni certificat gestionat |
| Instàncies de l'aplicació | Executar la imatge; són bestiar, no mascotes | Una sola instància significa aturada a cada desplegament |
| Base de dades gestionada | Persistència, backups, actualitzacions menors, alta disponibilitat | Operar PostgreSQL a mà és la tasca que consumeix més temps i surt pitjor |
| Magatzem de secrets | Desar i rotar credencials fora del repositori | Els secrets acaben a Git o en un xat |
| Registre d'imatges | Desar cada versió publicada i immutable | No hi ha reversió exacta |
| Observabilitat | Logs, mètriques i traces agregats | Una fallada en producció s'investiga a cegues (mòdul 9) |
Dues idees que convé fixar. La primera: les instàncies són llencables. Cap no té nom propi, cap no desa res valuós, qualsevol pot morir en qualsevol moment i ser substituïda. Tot el que cal conservar viu a la base de dades. La segona: la base de dades mai no està exposada a Internet. Viu en una xarxa privada i només accepta connexions de les instàncies de l'aplicació. És la regla de seguretat que més vegades s'incompleix en desplegaments improvisats.
- Decisions prèvies al desplegament
Aquesta és la llista de comprovació que cal respondre abans de triar plataforma. Cada punt té una resposta correcta amb matisos, no una preferència.
7.1 Com es gestionen els secrets?
CicloUrbana en necessita almenys tres: la contrasenya de PostgreSQL, el secret de signatura HS256 del JWT (05-04) i la clau de la passarel·la de pagaments. Opcions, de pitjor a millor:
| Forma | Valoració |
|---|---|
A application-prod.yml versionat |
Inacceptable. Queda a l'historial de Git per sempre |
A la imatge (ENV del Dockerfile) |
Inacceptable. Qualsevol amb accés al registre els llegeix amb docker history |
| Variable d'entorn posada a mà a la plataforma | Acceptable com a mínim viable; sense rotació ni auditoria |
| Magatzem de secrets (Secrets Manager, SSM, Vault, Sealed Secrets) | El correcte. Xifrat, auditat, rotable, amb permisos per identitat |
I tres regles invariables: mai versionar credencials; rotar qualsevol secret que hagi pogut quedar exposat, encara que el commit s'esborrés després; i aplicar mínim privilegi: l'usuari de PostgreSQL de l'aplicació no necessita ser superusuari ni poder crear bases de dades.
7.2 Com s'executen les migracions de Flyway?
Hi ha dos models i l'elecció canvia amb el nombre d'instàncies.
| En arrencar l'aplicació | Pas previ del desplegament | |
|---|---|---|
| Com | spring.flyway.enabled: true, s'apliquen a l'arrencada |
Un procés efímer (Job, tasca puntual, release phase) executa les migracions i després es despleguen les instàncies |
| Amb una instància | Perfecte | Correcte, una mica més de maquinària |
| Amb diverses instàncies | Flyway pren un bloqueig a la base de dades: una migra i les altres esperen | Les instàncies arrenquen amb l'esquema ja llest |
| Si la migració falla | L'aplicació no arrenca, i pot reintentar en bucle | El desplegament s'atura abans de tocar les instàncies vives |
| Migració llarga (índex sobre milions de files) | Bloqueja l'arrencada i dispara les sondes de salut | S'executa amb el seu propi temps, sense sondes a sobre |
| Visibilitat | Barrejada al log d'arrencada | Un pas propi, amb el seu resultat explícit |
| Permisos de base de dades | L'aplicació necessita permisos DDL sempre | L'aplicació pot córrer amb permisos només de dades |
La recomanació del curs: migrar en arrencar és perfectament vàlid mentre hi hagi una sola instància o el desplegament sigui recreate; així que hi hagi diverses rèpliques i desplegaments sense tall, convé el pas previ. Amb ddl-auto: validate de 04-08, a més, una instància que arrenqui contra un esquema que no coincideix falla immediatament i de manera clara, en lloc de corrompre dades.
El matís important amb diverses instàncies: encara que Flyway serialitzi correctament mitjançant un bloqueig, durant un rolling update conviuen la versió antiga i la nova contra el mateix esquema. Això obliga que cada migració sigui compatible cap enrere —el patró expand/contract de 04-08— i és el punt que enllaça aquesta llista amb l'apartat 8.
7.3 Còpies de seguretat i restauració
Una còpia de seguretat que mai no s'ha restaurat no és una còpia de seguretat, és una intenció. Tres decisions:
- RPO (Recovery Point Objective): quantes dades es pot permetre perdre. Amb backups diaris, fins a 24 hores de lloguers. Amb recuperació a un punt en el temps (PITR), segons.
- RTO (Recovery Time Objective): quant es pot trigar a tornar. Restaurar 20 GB des d'una instantània són desenes de minuts.
- Prova de restauració: almenys una vegada, restaurar en un entorn a part i comprovar que l'aplicació arrenca contra aquesta còpia. És l'única manera de saber que funciona.
Les bases de dades gestionades (Heroku Postgres, RDS) fan backups automàtics; només cal verificar la finestra de retenció i que l'esborrat de la instància no s'endugui per davant les còpies.
7.4 Dimensionament de la memòria de la JVM en contenidor
Ja vist a 07-04, aquí com a decisió de desplegament: el contenidor té un límit de memòria i la JVM l'ha de respectar amb marge.
# Variable estàndard reconeguda per la JVM sense tocar l'ENTRYPOINT
JAVA_TOOL_OPTIONS="-XX:MaxRAMPercentage=75 -XX:+ExitOnOutOfMemoryError"MaxRAMPercentage=75deixa el 25 % restant per a metaespai, piles de fils, búfers directes de xarxa i el procés mateix. Fixar el 100 % —o no fixar res i confiar en el 25 % per defecte de l'heurística antiga— és la causa número u de contenidorsOOMKilledsense cap traça al log.ExitOnOutOfMemoryErrorfa que unOutOfMemoryErrormati el procés en lloc de deixar una JVM mig viva que respon malament. Amb reinici automàtic de la plataforma, això és el desitjable.- Un punt de partida raonable per a CicloUrbana: 1 GB de límit de memòria i 1 vCPU per rèplica.
7.5 Zona horària i Locale
Un contenidor sense configurar corre en UTC i amb Locale POSIX. Conseqüències reals: els informes programats a les 3 de la matinada (07-03) s'executen a les 5 hora de Ribalta a l'estiu; les dates formatades surten en format anglosaxó; i l'import d'un lloguer s'imprimeix amb punt decimal.
La decisió correcta és doble: fixar TZ=Europe/Madrid a la imatge o l'entorn, i emmagatzemar sempre els instants en UTC (TIMESTAMPTZ a PostgreSQL, Instant a Java), convertint a la zona local només en presentar. El primer evita sorpreses operatives; el segon fa el sistema correcte encara que canviï la zona.
7.6 HTTPS i capçaleres de proxy
En producció, el TLS el termina el balancejador i l'aplicació rep HTTP pla per dins de la xarxa privada. Això està bé —és l'habitual i l'eficient— però introdueix un problema: Tomcat creu que la petició va arribar per http al port 8080, així que les URL que genera (redireccions, Location d'un 201 Created, enllaços de Swagger) surten malament.
Amb aquesta propietat, Spring Boot registra un filtre que reconstrueix l'esquema, l'amfitrió i el port originals a partir de les capçaleres X-Forwarded-* que afegeix el balancejador. Sense ella, un POST /api/v1/lloguers correcte retorna Location: http://10.0.2.31:8080/api/v1/lloguers/1042 en lloc de https://ciclourbana.ribalta.example/api/v1/lloguers/1042.
Avís de seguretat: forward-headers-strategy s'ha d'activar només quan hi ha de debò un proxy de confiança al davant. Si l'aplicació és accessible directament, un client pot falsificar X-Forwarded-For i contaminar els logs o les decisions basades en IP. A Kubernetes i a ECS, on el Service o el grup de seguretat garanteixen que només el balancejador arriba a l'aplicació, és segur.
7.7 Pool d'HikariCP davant de max_connections
Aquest és l'error de dimensionament més freqüent en escalar. HikariCP obre per defecte 10 connexions per instància (04-02). Amb 4 rèpliques són 40 connexions permanents; si a més es desplega en rolling update, durant uns segons hi ha 5 rèpliques i 50. I les migracions, les tasques puntuals i qualsevol eina d'administració en sumen més.
Una instància petita de PostgreSQL gestionat sol venir amb max_connections entre 80 i 120, i el motor mateix en reserva unes quantes per a superusuari. La regla:
spring:
datasource:
hikari:
maximum-pool-size: 10
minimum-idle: 5
connection-timeout: 3000 # falla ràpid si no hi ha connexió lliure
max-lifetime: 1200000 # 20 min, per sota del tall del balancejador i del motorI un principi contraintuïtiu que convé interioritzar: un pool gran no dona més rendiment. PostgreSQL atén cada connexió amb un procés; passat el punt en què se saturen els nuclis i el disc, més connexions només afegeixen contenció. Un pool de 10 per instància és gairebé sempre més ràpid que un de 50.
7.8 Aturada ordenada i preStop
Quan la plataforma decideix retirar una instància li envia SIGTERM. Amb l'aturada ordenada de 01-05 configurada, Spring Boot deixa d'acceptar peticions noves, acaba les que estan en curs i tanca els executors i el pool.
Falta una peça subtil: el balancejador triga uns segons a assabentar-se que aquella instància ja no ha de rebre trànsit. Si el procés deixa d'acceptar connexions en el mateix instant en què rep el SIGTERM, les peticions que el balancejador continuï enviant durant aquesta finestra fallen amb 502. La solució universal és dormir uns segons abans de començar a aturar-se, cosa que a Kubernetes s'expressa amb un preStop (08-04) i en altres plataformes amb un marge equivalent:
Durant aquests 10 segons, la instància continua atenent amb normalitat mentre el balancejador la retira de la rotació. Després comença l'aturada ordenada. El període de gràcia total ha de ser més gran que preStop + timeout-per-shutdown-phase, o la plataforma matarà el procés a mitges.
- Estratègies de desplegament
La pregunta és com es passa de la versió N a la N+1 sense deixar Ribalta sense bicicletes.
flowchart TD
subgraph Recreate
R1[v1 v1 v1] --> R2[--- aturada ---] --> R3[v2 v2 v2]
end
subgraph Rolling
O1[v1 v1 v1] --> O2[v2 v1 v1] --> O3[v2 v2 v1] --> O4[v2 v2 v2]
end
subgraph Blue-Green
B1[blau v1 actiu · verd v2 en proves] --> B2[commutar trànsit a verd]
end
subgraph Canary
C1[95% a v1 · 5% a v2] --> C2[50/50 si les mètriques acompanyen] --> C3[100% a v2]
end
| Estratègia | Tall de servei | Cost de recursos | Complexitat | Reversió | Exigeix de l'aplicació |
|---|---|---|---|---|---|
| Recreate | Sí, segons o minuts | Cap d'extra | Mínima | Tornar a desplegar l'anterior | Res d'especial: mai no conviuen versions |
| Rolling update | No | +1 instància temporal | Baixa (nativa a ECS i Kubernetes) | rollout undo o redesplegar l'etiqueta prèvia |
Convivència de N i N+1: esquema i API compatibles cap enrere |
| Blue-green | No | El doble durant la transició | Mitjana | Commutar de tornada: segons | Convivència durant la finestra; dos entorns complets |
| Canary | No | +1 instància | Alta: requereix encaminament per percentatge i mètriques per versió | Retirar el canari | Convivència prolongada i mètriques separades per versió |
Què exigeix realment la convivència de versions. Durant un rolling update de CicloUrbana hi ha, diguem, dues rèpliques amb la versió 2.4.0 i una amb la 2.3.0 parlant amb la mateixa base de dades. Això obliga a dues compatibilitats:
- Esquema cap enrere. La migració que acompanya 2.4.0 s'ha de poder aplicar sense trencar 2.3.0. Reanomenar
capacitataplaces_totalsen un sol pas mata la versió antiga tan bon punt s'aplica. El patró expand/contract de 04-08 ho resol en tres desplegaments: primer afegir la columna nova i escriure a les dues (expand), després desplegar el codi que només fa servir la nova, i finalment eliminar la vella (contract), cada pas compatible amb l'anterior. - API cap enrere. Si l'aplicació mòbil de Ribalta està demanant
/api/v1/lloguersa les tres rèpliques, no pot rebre respostes amb formes diferents. Afegir camps és segur; treure'ls o canviar-ne el tipus, no.
L'elecció per a CicloUrbana: rolling update com a norma —és el que ECS i Kubernetes fan per defecte i n'hi ha prou de respectar la compatibilitat cap enrere—, recreate només per a migracions destructives que exigeixin aturar (i amb avís als ciutadans), i blue-green si algun dia l'ajuntament exigeix poder validar la versió nova amb trànsit real abans de commutar.
- Estat, sessions i escalat horitzontal
Amb diverses rèpliques darrere d'un balancejador, una mateixa persona pot caure a la rèplica 1 en una petició i a la 3 en la següent. Tot el que estigui desat a la memòria d'una instància deixa d'existir per a les altres.
La solució clàssica —sessions enganxoses (sticky sessions)— consisteix que el balancejador lligui cada usuari a una instància. Funciona, però és una mala solució: en retirar aquella instància en un desplegament, tots els seus usuaris perden la sessió; i el repartiment de càrrega es desequilibra. La segona solució clàssica és una sessió compartida a Redis (Spring Session), vàlida i molt utilitzada, però afegeix una peça d'infraestructura més.
CicloUrbana no necessita cap de les dues, i la raó és a 05-04: l'autenticació és per JWT. El testimoni el desa el client, viatja a cada petició i conté la identitat i els rols signats. El servidor no recorda res entre peticions: valida la signatura i decideix. Qualsevol rèplica pot atendre qualsevol petició de qualsevol usuari, i una rèplica que mor no s'endú cap sessió. És el factor 6 complert de debò, i per això l'escalat horitzontal dels pròxims capítols és simplement «posar més còpies».
Queda una excepció que convé vigilar: les memòries cau locals. Quan a 09-02 aparegui Spring Cache, una memòria cau en memòria per instància significa que cada rèplica pot tenir un valor diferent i que invalidar en una no invalida a les altres. És acceptable per a dades que toleren estar desactualitzades uns segons, i requereix una memòria cau distribuïda quan no ho toleren.
- Saber si un desplegament ha anat bé, i revertir
Un desplegament no acaba quan la plataforma diu «completat». Acaba quan hi ha evidència que la versió nova es comporta almenys tan bé com l'anterior. Què mirar, en els primers deu minuts:
| Senyal | Què indica | D'on surt |
|---|---|---|
Totes les rèpliques en readiness 200 |
El context va arrencar i la base de dades respon | /actuator/health/readiness (07-01) |
| Cap reinici del contenidor | No hi ha OOMKilled ni fallada de liveness |
La plataforma |
Taxa d'errors 5xx estable |
La versió nova no trenca casos que funcionaven | Mètriques (09-03) |
| Latència p95 i p99 estables | No hi ha una consulta nova sense índex ni un pool saturat | Mètriques |
| Sense excepcions noves al log | Fallades que no arriben a 5xx però trenquen funcionalitat |
Logs agregats (09-05) |
/actuator/info amb el SHA esperat |
El desplegat és el que creus que està desplegat | build-info i dades de Git (07-01) |
| Una prova de fum real | Un usuari fictici s'autentica i consulta estacions | La canalització (08-05) |
Aquest últim punt de la taula —build-info amb el SHA del commit— semblava un detall menor a 07-01 i aquí mostra el seu valor: és el que converteix «crec que es va desplegar» en un fet verificable.
I la reversió. La regla és tenir decidit abans de desplegar com es torna enrere i quant es triga:
- Aplicació: tornar a desplegar l'etiqueta anterior de la imatge. És ràpid (la imatge és al registre) i exacte. Segons o pocs minuts.
- Base de dades: no es reverteix. Una migració aplicada es queda aplicada; desfer-la amb una altra migració de tornada és perillós i de vegades impossible sense perdre dades. Per això l'esquema ha de ser compatible cap enrere: per poder revertir l'aplicació sense tocar la base de dades.
- Criteri de decisió: fixat per endavant. «Si la taxa de
5xxsupera el 2 % durant cinc minuts, es reverteix», i es reverteix abans d'investigar la causa. Investigar amb producció trencada és la pitjor combinació possible.
- Entorns i paritat
| Entorn | Per a què | Dades | Qui hi accedeix | Diferències acceptables amb prod |
|---|---|---|---|---|
| dev | Feina diària | Ficticis, llencables | El desenvolupador | Moltes: Swagger obert, logs DEBUG, base de dades local |
| test | Execució de la suite (mòdul 6) | Generades per la prova | La canalització | Contenidors efímers amb Testcontainers |
| pre | Validar la versió candidata | Còpia anonimitzada o volum realista | Equip i ajuntament | Les mínimes: mateix tipus d'infraestructura, menys rèpliques |
| prod | El servei real | Reals | Els ciutadans | — |
El desè factor demana reduir la distància entre entorns en tres dimensions: temps (que el que s'escriu avui es desplegui avui, no d'aquí a un mes), persones (que qui escriu el codi participi en el seu desplegament) i eines (que el motor, la versió i la configuració siguin els mateixos).
La tercera és on més es peca, i on aquest curs ja va prendre les decisions correctes: PostgreSQL 16 a tot arreu —també a les proves, gràcies a Testcontainers (06-05), en lloc d'H2—, ddl-auto: validate amb Flyway a tots els entorns amb base de dades persistent (04-08), i la mateixa imatge recorrent pre i prod.
El que sí que pot diferir legítimament entre pre i prod: el nombre de rèpliques, la mida de la instància de base de dades, el volum de dades, els serveis externs (simulats a pre, reals a prod) i el nivell de log. Res d'això no canvia el binari.
Errors Comuns i Consells
Recompilar per a cada entorn. Un perfil de Maven que produeix un JAR «de producció» diferent del que es va provar trenca la garantia de tot el mòdul 6. Construeix una vegada; configura moltes vegades.
Ficar secrets a la imatge. ENV JWT_SECRET=... al Dockerfile queda escrit en una capa i es llegeix amb docker history sense ni tan sols executar el contenidor. Els secrets entren en temps d'execució.
Exposar la base de dades a Internet «per poder connectar-m'hi amb DBeaver». És la porta d'entrada més habitual a una filtració. S'hi accedeix per un túnel, un bastió o una sessió gestionada, i sempre amb credencials pròpies i de només lectura quan n'hi hagi prou.
Desplegar sense pla de reversió. «Si surt malament ja ho veurem» significa investigar sota pressió amb el servei caigut. Decideix el criteri de reversió abans de prémer el botó.
Confondre liveness amb readiness. Si la sonda de vida inclou la base de dades, una caiguda momentània del motor reinicia totes les rèpliques en cadena i converteix una incidència de dos minuts en una de vint. Vida = el procés és irrecuperable? Disponibilitat = pot atendre ara? (07-01).
Ignorar el pool davant de max_connections. El sistema funciona amb dues rèpliques i falla en escalar a sis amb FATAL: sorry, too many clients already. Calcula el màxim abans d'escalar, no després.
Desplegar el divendres a la tarda. No és superstició: és que el cost d'una fallada depèn de quanta gent hi hagi disponible per arreglar-la. Quan la canalització i la reversió són sòlides (08-05), deixa d'importar; fins llavors, importa.
Consell: escriu el runbook abans del primer desplegament. Un document curt que respongui: com es desplega, com es reverteix, on són els logs, com es restaura la base de dades i a qui es truca. Mitja hora d'escriptura que s'amortitza el primer dia dolent.
Consell: destrueix sempre els recursos de pràctica. Tot el que creïs a les lliçons següents es factura per hora. Una base de dades gestionada oblidada durant un mes és una factura real i evitable.
Exercicis
Exercici 1
Audita CicloUrbana davant dels dotze factors tal com està en acabar el mòdul 7. Per a cada factor, indica si es compleix, es compleix parcialment o no es compleix, amb una frase de justificació i, quan no es compleixi, la correcció concreta. Presta atenció especial als factors 3, 5, 6 i 12.
Exercici 2
L'ajuntament de Ribalta planteja tres escenaris i demana per a cadascun un model d'allotjament, una estratègia de desplegament i un model d'execució de les migracions, amb justificació:
- (a) Demostració per al ple municipal d'aquí a dues setmanes, un desenvolupador, pressupost gairebé nul, sense dades reals.
- (b) Servei en producció per a 40.000 ciutadans, un equip de quatre persones, sense especialista en infraestructura, amb exigència de no tallar el servei en horari diürn.
- (c) La xarxa creix a cinc aplicacions (bicicletes, patinets, aparcament, incidències, portal ciutadà) amb tres equips i requisit de poder canviar de proveïdor de núvol.
Exercici 3
CicloUrbana s'ha desplegat amb tres rèpliques darrere d'un balancejador. La versió 2.5.0 inclou una migració que reanomena la columna capacitat d'estacions a places_totals i el codi corresponent. Es llança un rolling update amb les migracions executant-se en arrencar cada instància. Descriu minut a minut què passa, què veuen els ciutadans, per què revertir l'aplicació no arregla el problema, i reescriu el pla complet perquè el mateix canvi funcional es lliuri sense tall de servei.
Solucions
Solució 1
| # | Factor | Estat | Justificació i correcció |
|---|---|---|---|
| 1 | Codi base | Compleix | Un repositori Git, diversos desplegaments del mateix codi |
| 2 | Dependències | Compleix | pom.xml amb el BOM de Spring Boot; el JRE viatja a la imatge des de 07-04 |
| 3 | Configuració | Parcial | 07-02 va treure els secrets a variables, però es posen a mà. Correcció: magatzem de secrets amb rotació (08-03) |
| 4 | Serveis de suport | Compleix | PostgreSQL i la passarel·la es configuren per URL; canviar d'instància no toca codi |
| 5 | Construir, publicar, executar | No compleix | Avui no hi ha separació real: es construeix a mà i s'executa a mà. Correcció: la canalització de 08-05 |
| 6 | Processos | Compleix | Sense sessió de servidor gràcies al JWT; res al disc local |
| 7 | Assignació de ports | Compleix | Tomcat incrustat, 8080 i 8081 |
| 8 | Concurrència | Parcial | L'aplicació ho suporta, però mai no s'ha executat amb més d'una instància. Correcció: verificar a pre amb dues rèpliques, revisant ShedLock i el pool |
| 9 | Llencabilitat | Compleix | shutdown: graceful des de 01-05 i stop_grace_period a Compose (07-04). Pendent el marge tipus preStop |
| 10 | Paritat d'entorns | Compleix | PostgreSQL 16 a les proves amb Testcontainers i en producció; Flyway a tots dos |
| 11 | Logs | Parcial | S'escriu a stdout, però ningú no els agrega. Correcció: agregador (09-05) |
| 12 | Processos d'administració | No compleix | Les migracions s'apliquen en arrencar i no hi ha manera estàndard de llançar una tasca puntual. Correcció: un pas previ de desplegament amb la mateixa imatge |
Conclusió de l'auditoria: l'aplicació està ben construïda; el que falta és el procés que l'envolta (factors 5 i 12), que és exactament l'objecte d'aquest mòdul.
Solució 2
(a) Demostració per al ple. Allotjament: PaaS (08-02). En una tarda hi ha URL amb HTTPS i base de dades gestionada, sense aprendre res de xarxes ni d'IAM. Estratègia: recreate; amb una sola instància i sense usuaris reals, un tall de trenta segons no importa. Migracions: en arrencar, per simplicitat, o a la release phase si el PaaS l'ofereix. Detall crític: destruir l'aplicació després del ple, perquè el nivell gratuït ja no existeix.
(b) Producció per a 40.000 ciutadans. Allotjament: contenidors gestionats (ECS Fargate + RDS, 08-03). Raó: cal xarxa privada, base de dades amb backups i alta disponibilitat, balancejador amb TLS i autoescalat, però l'equip no té ningú que es pugui dedicar a operar Kubernetes; Fargate elimina la gestió de servidors. Estratègia: rolling update amb dues rèpliques mínimes i minimumHealthyPercent: 100, recolzada en la comprovació d'estat sobre /actuator/health/readiness. Migracions: pas previ, com a tasca puntual amb la mateixa imatge, més disciplina d'expand/contract; amb rèpliques convivint, és l'única opció sensata.
(c) Cinc aplicacions i tres equips. Allotjament: Kubernetes (08-04). Aquí sí que compensa: la complexitat s'amortitza entre cinc serveis, cada equip desplega el seu Deployment sense coordinar-se amb els altres, i els manifestos són portables entre proveïdors, que era el requisit explícit. Estratègia: rolling update per defecte i canary per als serveis de cara al ciutadà, amb mètriques per versió. Migracions: Job de Kubernetes previ al desplegament, un per servei, cadascun amo del seu propi esquema. Advertiment honest: la base de dades continua sent gestionada fora del clúster; córrer PostgreSQL dins de Kubernetes multiplica el risc sense guany clar.
Solució 3
Minut a minut.
- Minut 0. L'orquestrador arrenca una instància amb 2.5.0. En iniciar-se, Flyway aplica
ALTER TABLE estacions RENAME COLUMN capacitat TO places_totals. La migració triga mil·lisegons i té efecte immediat per a tothom. - Minut 0 + 1 segon. Les dues rèpliques de 2.4.0 continuen vives i continuen executant
SELECT ... capacitat ... FROM estacions. PostgreSQL responERROR: column "capacitat" does not exist. Hibernate propaga l'excepció, elGestorGlobalExcepcionsde 03-06 la tradueix i dos terços del trànsit reben500. - Minut 1. La instància nova passa el seu readiness i comença a rebre trànsit: un terç de les peticions funciona. El símptoma que veu el ciutadà és el pitjor possible: l'aplicació falla de manera intermitent, i recarregar «de vegades arregla» el problema.
- Minuts 1-4. El rolling update substitueix les altres dues rèpliques. Quan acaba, tot torna a funcionar. S'han perdut uns minuts de servei parcial.
- Si algú reverteix durant la finestra, el desastre és més gran: les tres rèpliques tornen a 2.4.0, que busca
capacitat, i la columna ja no existeix. El servei passa de fallar en dos terços a fallar en el 100 %.
Per què revertir no arregla res. La reversió retorna el codi, però la migració ja es va aplicar i l'esquema no torna sol. És la lliçó central de l'apartat 8: la base de dades no es reverteix, així que l'aplicació només es pot revertir si l'esquema és compatible amb la versió anterior. Aquí no ho és, i l'únic camí de tornada seria una altra migració que reanomenés la columna una altra vegada, escrita i aplicada sota pressió.
El pla correcte, en tres desplegaments (expand/contract, 04-08).
Desplegament 1 — expand. Migració que afegeix la columna sense treure res:
-- V10__expand_places_totals.sql
ALTER TABLE estacions ADD COLUMN places_totals INTEGER;
UPDATE estacions SET places_totals = capacitat;
ALTER TABLE estacions ALTER COLUMN places_totals SET NOT NULL;El codi 2.5.0 llegeix capacitat i escriu a les dues columnes (o un disparador manté la còpia). Compatible amb 2.4.0, que continua fent servir capacitat amb normalitat. El rolling update és segur: totes dues versions funcionen contra el mateix esquema.
Desplegament 2 — canvi de lectura. Sense migració. La versió 2.6.0 llegeix i escriu només places_totals. Continua sent compatible cap enrere, perquè capacitat continua existint i actualitzada, així que revertir a 2.5.0 funciona. Aquest és el desplegament que cal deixar reposar uns dies: és el punt de no retorn pràctic.
Desplegament 3 — contract. Només quan cap instància de 2.5.0 no quedi viva i hi hagi confiança en 2.6.0:
Reforços del pla. Executar les migracions com a pas previ i no en arrencar, perquè el resultat de cadascuna sigui explícit i no quedi sepultat al log d'arrencada; mantenir ddl-auto: validate, que fa fallar immediatament una instància l'esquema de la qual no coincideix en lloc de deixar-la fallant per consulta; i afegir a la revisió de codi una regla senzilla i verificable: cap migració no pot contenir DROP COLUMN, RENAME ni un canvi de tipus incompatible al mateix desplegament que el codi que l'utilitza.
Conclusió
CicloUrbana encara no està desplegada, però ja saps exactament què significa desplegar-la. Distingeixes construcció, publicació i desplegament, i tens clara la definició operativa: disponible, repetible i reversible. Coneixes els tres artefactes possibles i per què el nostre és una imatge de contenidor —el JAR continua sent vàlid, el WAR només per obligació—, i has interioritzat el principi que governa tot el mòdul: construir una vegada, desplegar a molts llocs, amb la conseqüència inevitable que el binari no pot portar dins la configuració de cap entorn.
Tens els dotze factors aplicats un a un al nostre projecte, amb el diagnòstic honest d'on som: l'aplicació els compleix gairebé tots, i el que falla és el procés que l'envolta. Saps quins models d'allotjament existeixen, quant costen, quant esforç operatiu exigeixen i quin correspon a cada moment de la vida de la xarxa de Ribalta. I tens al cap l'anatomia completa d'un entorn de producció: DNS, balancejador amb terminació TLS, rèpliques llencables, base de dades gestionada en xarxa privada, magatzem de secrets, registre d'imatges i observabilitat.
Sobretot, tens la llista de decisions que cal prendre abans de tocar cap consola: com es desen i es roten els secrets, si Flyway migra en arrencar o en un pas previ —i per què això canvia radicalment amb diverses instàncies—, quines còpies de seguretat hi ha i si alguna vegada s'han restaurat, com es dimensiona la memòria de la JVM amb MaxRAMPercentage per no acabar en OOMKilled, per què cal fixar TZ i desar els instants en UTC, com server.forward-headers-strategy arregla les URL darrere d'un balancejador que termina el TLS, com es calcula el pool d'HikariCP davant del max_connections de PostgreSQL, i per què cal un marge tipus preStop abans de l'aturada ordenada. Coneixes les quatre estratègies de desplegament i, el més important, què exigeix cadascuna de l'aplicació: la convivència de versions obliga a compatibilitat cap enrere a l'esquema —expand/contract— i a l'API. I saps per què CicloUrbana escala horitzontalment sense sessions enganxoses ni Redis: perquè el JWT de 05-04 la va deixar sense estat.
Amb aquesta base, la resta del mòdul són execucions concretes de les mateixes idees. La lliçó següent, Desplegant a Heroku, fa el primer desplegament real: un PaaS que s'encarrega del sistema operatiu, del servidor, del certificat i de la base de dades, i on en menys d'una hora https://ciclourbana-ribalta estarà responent amb les seves estacions. En veurem els conceptes —dynos, buildpacks, Procfile, config vars, release phase—, el detall incòmode d'una DATABASE_URL que no és una URL JDBC vàlida, com hi encaixen els perfils i els secrets, i també on són els límits que abans o després obliguen a sortir d'un PaaS.
Curs de Spring Boot
Mòdul 1: Introducció a Spring Boot
- Què és Spring Boot?
- Configuració del teu entorn de desenvolupament
- Creant la teva primera aplicació Spring Boot
- Entenent l'estructura del projecte
- L'arrencada i el cicle de vida de l'aplicació
Mòdul 2: Conceptes bàsics de Spring Boot
- Anotacions de Spring Boot
- Injecció de dependències a Spring Boot
- Àmbit i cicle de vida dels beans
- Configuració de Spring Boot
- Propietats de Spring Boot
- Autoconfiguració i starters per dins
Mòdul 3: Construint serveis web RESTful
- Introducció als serveis web RESTful
- Creant controladors REST
- Gestió dels mètodes HTTP
- Validació de dades d'entrada
- DTOs i mapatge entre capes
- Gestió d'excepcions a REST
- Documentar l'API amb OpenAPI
Mòdul 4: Accés a dades amb Spring Boot
- Introducció a Spring Data JPA
- Configuració de fonts de dades
- Creació d'entitats JPA
- Relacions entre entitats
- Ús de repositoris de Spring Data
- Mètodes de consulta a Spring Data JPA
- Transaccions i gestió de la persistència
- Migracions d'esquema amb Flyway
Mòdul 5: Seguretat a Spring Boot
- Introducció a Spring Security
- Configuració de Spring Security
- Autenticació i autorització d'usuaris
- Implementació d'autenticació JWT
- Seguretat a nivell de mètode i enduriment de l'API
Mòdul 6: Proves a Spring Boot
- Introducció a les proves
- Proves unitàries amb JUnit
- Simulació amb Mockito
- Proves d'integració
- Proves amb Testcontainers
Mòdul 7: Funcions avançades de Spring Boot
- Spring Boot Actuator
- Perfils de Spring Boot
- Tasques programades i execució asíncrona
- Spring Boot amb Docker
- Spring Boot i microserveis
- Comunicació entre serveis i tolerància a fallades
Mòdul 8: Desplegament d'aplicacions Spring Boot
- Introducció al desplegament
- Desplegant a Heroku
- Desplegant a AWS
- Desplegant a Kubernetes
- Integració i lliurament continus
Mòdul 9: Rendiment i monitoratge
- Ajust de rendiment
- Memòria cau amb Spring Cache
- Monitoratge amb Spring Boot Actuator
- Ús de Prometheus i Grafana
- Gestió de registres i logs
- Traçabilitat distribuïda
