Fins ara, cada peça de Quilòmetre Zero ha corregut en màquines concretes: els tres nodes de Cassandra que Ansible va configurar a 07-05, els brokers de Kafka que es reinicien d'un en un respectant les ISR, el clúster de Patroni amb el seu etcd, el MinIO amb els seus discos, el clúster de Kubernetes sobre el qual es despleguen els sis serveis. Algú ha comprat aquestes màquines, les ha instal·lat en un centre de dades, els ha posat discos i xarxa, i les substitueix quan fallen. I quan a 07-03 es va decidir un pla de recuperació davant de desastres en pilot light amb una "regió secundària", es va deixar oberta la pregunta d'on és aquesta regió i qui l'opera. Aquesta lliçó hi respon canviant la premissa: la infraestructura passa a ser una API que un proveïdor ofereix sota demanda, amb regions i zones de disponibilitat, xarxes virtuals, balancejadors, autoescalat i, sobretot, serveis gestionats que fan per nosaltres el que al curs hem muntat a mà. Veurem els models de servei i de desplegament, els conceptes que canvien el disseny (regions i zones, VPC, elasticitat), la correspondència entre cada component de Quilòmetre Zero i el seu equivalent gestionat a AWS i GCP, els avantatges i els desafiaments (inclòs el que exigeix una revisió legal), la infraestructura com a codi amb Terraform, els pilars d'una arquitectura ben dissenyada, i una introducció a FinOps amb el cost mensual aproximat de la plataforma. La pràctica desplega Quilòmetre Zero a AWS amb Terraform i els manifestos k8s/ de 07-05. Serverless, edge i CDN queden per a 08-04.

Contingut

  1. Models de servei: IaaS, PaaS, SaaS i la responsabilitat compartida
  2. Models de desplegament: pública, privada, híbrida i multinúvol
  3. Regions, zones de disponibilitat i recuperació davant de desastres
  4. Xarxes al núvol: VPC, subxarxes, balancejadors i autoescalat
  5. Serveis gestionats: què substitueix cada peça de Quilòmetre Zero
  6. Avantatges i desafiaments: elasticitat, pagament per ús, lock-in, egress i compliment
  7. Infraestructura com a codi amb Terraform
  8. Arquitectura ben dissenyada: els pilars
  9. FinOps: etiquetes, reserves, spot, pressupostos i el cost de Quilòmetre Zero
  10. Pràctica: Quilòmetre Zero a AWS amb Terraform i EKS
  11. Errors Comuns i Consells
  12. Exercicis
  13. Conclusió

  1. Models de servei: IaaS, PaaS, SaaS i la responsabilitat compartida

El núvol es defineix pel que el proveïdor assumeix i el que continua sent responsabilitat del client. Aquesta línia es mou segons el model de servei, i saber on és en cada cas és el que evita tant pagar pel que no s'usa com suposar que "el núvol ho fa" quan no ho fa.

Capa Centre de dades propi (el que Quilòmetre Zero té avui) IaaS (màquines virtuals: EC2, Compute Engine) PaaS (plataforma: RDS, EKS, App Engine, Cloud Run) SaaS (aplicació: Keycloak gestionat, Confluent Cloud, Datadog) FaaS (funcions: Lambda; és 08-04)
Edifici, energia, xarxa física Client Proveïdor Proveïdor Proveïdor Proveïdor
Maquinari, hipervisor Client Proveïdor Proveïdor Proveïdor Proveïdor
Sistema operatiu, pedaços Client Client Proveïdor Proveïdor Proveïdor
Runtime, middleware (PostgreSQL, Kafka, Kubernetes) Client Client Proveïdor (instal·lació, pedaços, failover) Proveïdor Proveïdor
Configuració del servei (paràmetres, rèpliques, esquemes) Client Client Client Parcial Client
Aplicació i el seu codi Client Client Client Proveïdor Client (només la funció)
Dades: classificació, xifratge, accés, backups lògics Client Client Client Client Client
Identitats i permisos (IAM) Client Client Client Client Client

Dues files no canvien mai de columna: les dades i les identitats són sempre del client. Aquest és el model de responsabilitat compartida que tots els proveïdors publiquen: el proveïdor respon de la seguretat del núvol (que ningú no accedeixi físicament al disc, que l'hipervisor aïlli), el client de la seguretat al núvol (que el bucket no sigui públic, que el rol IAM no tingui *, que les dades personals estiguin xifrades i a la regió correcta). Un bucket km0-factures accessible sense autenticació és responsabilitat del client en qualsevol model.

Per a Quilòmetre Zero, el repartiment natural és: PaaS per a tot el que té equivalent gestionat (bases de dades, Kafka, Kubernetes, memòria cau, objectes), IaaS només on no n'hi ha o on el control compensa (els nodes de treball de Kubernetes són IaaS per sota, però gestionats com a grup), i SaaS per al que no aporta diferència (identitat, observabilitat si es decideix externalitzar-la).

  1. Models de desplegament: pública, privada, híbrida i multinúvol

Model Què és Quan té sentit Cost ocult
Núvol públic Recursos d'un proveïdor (AWS, GCP, Azure...) compartits entre clients, aïllats lògicament Càrregues variables, equips petits d'operació, necessitat de serveis gestionats, creixement incert Lock-in, egress, cost per ús que creix amb l'èxit
Núvol privat La mateixa abstracció (API, autoservei, elasticitat) sobre maquinari propi: OpenStack, VMware, Kubernetes on-premise Regulació estricta, latència cap a sistemes propis, volum estable que amortitza el maquinari Cal operar el núvol a més de l'aplicació; sense elasticitat real més enllà del maquinari comprat
Híbrid Part en privat, part en públic, connectats (VPN o enllaç dedicat) Migració gradual (l'estrangulament de 08-01 aplicat a la infraestructura); dades que s'han de quedar a dins amb còmput elàstic a fora Latència i cost de l'enllaç; dos models de seguretat i observabilitat
Multinúvol Càrregues repartides entre diversos proveïdors públics Evitar la dependència d'un de sol, aprofitar serveis únics de cadascun, exigències de clients El més car d'operar: dos IAM, dues xarxes, dos conjunts de serveis gestionats; l'egress entre núvols; la portabilitat obliga a renunciar al que és gestionat

Quilòmetre Zero, que avui té el seu propi maquinari, passarà a núvol públic amb un període híbrid durant la migració (l'enllaç VPN entre el centre de dades i la VPC permet que Kafka repliqui amb MirrorMaker i que Patroni tingui una rèplica al núvol abans del tall). El multinúvol es descarta explícitament: amb quatre equips, operar dos proveïdors costaria més que el risc que mitiga, i la portabilitat es manté d'una altra manera, amb Kubernetes com a capa de desplegament i amb contenidors per al que no és gestionat (apartat 6).

  1. Regions, zones de disponibilitat i recuperació davant de desastres

Un proveïdor organitza la seva infraestructura en regions (una zona geogràfica: Irlanda, Frankfurt, París...) i, dins de cada regió, en diverses zones de disponibilitat (availability zones, AZ): centres de dades físicament separats (quilòmetres de distància, energia i xarxa independents) però connectats amb latència d'1-2 ms. Aquesta estructura es correspon directament amb els dominis de fallada de 07-03:

  • Una AZ és un domini de fallada de centre de dades: incendi, tall d'energia, fallada de xarxa de l'edifici. Distribuir les rèpliques entre AZ (els tres nodes de Cassandra en tres AZ; el primari de Patroni i la seva rèplica síncrona en AZ diferents; els brokers de Kafka amb broker.rack = AZ perquè les rèpliques de cada partició es reparteixin) fa que la pèrdua d'una AZ no sigui un desastre, només un failover. És el que a 07-05 feia topologySpreadConstraints amb l'etiqueta de zona.
  • Una regió és un domini de fallada geogràfic (i regulatori): un desastre natural, una fallada d'un servei regional del proveïdor, o un canvi legal. La regió secundària de 07-03 és una altra regió del mateix proveïdor, a centenars o milers de quilòmetres, amb latència de 10-80 ms, cosa que descarta la replicació síncrona entre elles.

Amb això, les tres estratègies de DR de 07-03 es concreten al núvol:

Estratègia Què hi ha a la regió secundària RPO RTO Cost relatiu Com es fa a AWS
Pilot light Dades replicades (rèplica de lectura d'RDS entre regions, replicació S3, MirrorMaker d'MSK); clúster EKS mínim o inexistent; Terraform a punt per crear la resta Minuts (replicació asíncrona) 30-60 min (crear nodes, promoure rèplica, canviar DNS) ~15-20 % de la primària RDS cross-region read replica; S3 CRR; Route 53 amb failover manual
Warm standby Tot desplegat a escala reduïda (EKS amb pocs nodes, serveis amb 1 rèplica) rebent dades Minuts Minuts (escalar i canviar DNS) ~40-50 % Igual + EKS actiu amb HPA a punt; Route 53 amb health checks
Activa-activa multiregió Tot a escala completa a totes dues, servint trànsit alhora Segons o zero (segons la dada) Segons (el DNS o el balancejador global deixa d'enviar) ~200 % + complexitat de dades Aurora Global Database o Cassandra multi-DC; Route 53 latency routing; disseny multilíder de 03-04

Quilòmetre Zero manté la decisió de 07-03 (pilot light) per cost, amb una diferència important respecte del centre de dades propi: al núvol, la regió secundària no costa res mentre està buida. Terraform pot crear la infraestructura de la regió secundària en 20 minuts a partir del mateix codi; l'única cosa que ha d'existir per endavant són les dades replicades. L'activa-activa es deixa com a pas següent a 08-05, perquè implica resoldre conflictes d'escriptura entre regions (03-04) i canviaria el model de consistència de l'estoc.

flowchart TB
    subgraph R1[Regió eu-west-1, primària]
        direction TB
        ALB[Balancejador d'aplicació<br/>ALB, públic]
        subgraph AZa[AZ a]
            EKSa[Nodes EKS<br/>comandes, cataleg...]
            RDSa[(RDS primari<br/>km0_inventari)]
            MSKa[Broker MSK 1]
            CASa[Cassandra 1]
        end
        subgraph AZb[AZ b]
            EKSb[Nodes EKS]
            RDSb[(RDS standby<br/>Multi-AZ, síncron)]
            MSKb[Broker MSK 2]
            CASb[Cassandra 2]
        end
        subgraph AZc[AZ c]
            EKSc[Nodes EKS]
            MSKc[Broker MSK 3]
            CASc[Cassandra 3]
            REDc[(ElastiCache<br/>rèplica)]
        end
        S3[(S3 km0-fotos<br/>regional, 3+ AZ)]
        ALB --> EKSa & EKSb & EKSc
    end
    subgraph R2[Regió eu-central-1, pilot light]
        RDSr[(Rèplica de lectura<br/>asíncrona)]
        S3r[(S3 replicat)]
        MSKr[MirrorMaker → MSK mínim]
        EKSr[EKS: només definit<br/>a Terraform]
    end
    RDSa -. "replicació asíncrona" .-> RDSr
    S3 -. "CRR" .-> S3r
    MSKa -. "MirrorMaker" .-> MSKr
    DNS[Route 53<br/>km0.example] --> ALB
    DNS -. "failover manual" .-> R2

  1. Xarxes al núvol: VPC, subxarxes, balancejadors i autoescalat

Una VPC (Virtual Private Cloud) és una xarxa privada aïllada dins de la regió, amb el seu rang d'adreces (10.0.0.0/16), dividida en subxarxes que pertanyen cadascuna a una AZ. La convenció que Quilòmetre Zero adopta, i que és l'habitual, és de tres capes per AZ:

Subxarxa Què conté Accés des d'Internet Sortida a Internet
Pública Balancejadors, NAT gateway, bastió (si n'hi ha) Sí (tenen IP pública i ruta a l'Internet Gateway) Directa
Privada d'aplicació Nodes d'EKS amb els sis serveis, Kong No A través del NAT gateway (per cridar la passarel·la de pagaments, descarregar imatges)
Privada de dades RDS, MSK, ElastiCache, nodes de Cassandra No Cap (ni entrada ni sortida a Internet)

Els grups de seguretat són tallafocs amb estat per recurs, i expressen les mateixes regles que les polítiques de xarxa de Kubernetes de 07-05 però a la capa de la VPC: RDS només accepta 5432 des del grup de seguretat dels nodes d'EKS; MSK només 9094 (TLS) des d'EKS; res des de fora. Els serveis gestionats com S3 s'assoleixen per endpoints de VPC, que eviten que el trànsit cap a km0-fotos surti a Internet (i eviten el cost del NAT).

Els balancejadors gestionats substitueixen l'Ingress + Kong de 07-05 en la seva capa externa: un balancejador d'aplicació (ALB a AWS, HTTP(S) Load Balancer a GCP) termina TLS amb certificats gestionats i renovats automàticament, reparteix entre les AZ, fa health checks i exposa una IP a diverses AZ. Kong continua existint al darrere, com a gateway amb els seus plugins (06-05); el balancejador és només la porta de la regió. El WebSocket de 08-02 exigeix que el balancejador suporti Upgrade i tingui un timeout d'inactivitat llarg (l'ALB ho permet; un balancejador de xarxa de capa 4 ho passa sense mirar).

L'autoescalat opera en dos nivells que cal distingir: l'HPA de 07-05 afegeix Pods segons CPU o lag de Kafka, però els Pods necessiten nodes; l'autoescalador del clúster (Cluster Autoscaler o Karpenter a EKS, l'autoescalat de node pools a GKE) afegeix i treu màquines virtuals quan hi ha Pods pendents o nodes buits. Per sota, un grup d'autoescalat de màquines virtuals (Auto Scaling Group) les reemplaça si fallen un health check i les reparteix entre AZ. La Setmana de la Verema amb k6 (07-06) es converteix així en: l'HPA puja comandes de 3 a 12 Pods → 6 Pods pendents per falta de CPU → Karpenter arrenca 2 nodes en 90 s → els Pods es programen. Aquests 90 s són la raó de mantenir marge (nodes amb capacitat lliure) abans d'una campanya anunciada, o d'escalar per calendari.

  1. Serveis gestionats: què substitueix cada peça de Quilòmetre Zero

Aquesta taula és la correspondència entre el que s'ha construït al curs i el que el proveïdor ofereix gestionat. A cada fila, el que es guanya és el que el proveïdor assumeix (instal·lació, pedaços, failover, backups, escalat) i el que es perd és control i portabilitat.

Peça de Quilòmetre Zero Lliçó AWS GCP Què gestiona el proveïdor Què continua sent nostre
PostgreSQL + Patroni (km0_inventari, km0_analitica) 03-04, 07-03 RDS PostgreSQL Multi-AZ (o Aurora) Cloud SQL amb alta disponibilitat Failover entre AZ (~60 s), backups automàtics, PITR, pedaços, rèpliques de lectura Esquema, índexs, paràmetres, pooling de connexions, restauracions de prova
Kafka (comandes.esdeveniments, repartiment.*) 02-04 MSK (o Confluent Cloud) Confluent Cloud o Pub/Sub (model diferent) Brokers, ZooKeeper/KRaft, pedaços, rèpliques entre AZ, escalat d'emmagatzematge Tòpics, particions, retenció, esquemes, consumidors
Cassandra (km0_comandes) 04-04 Keyspaces (API compatible, serverless) o Cassandra a EC2 Cassandra a GCE o Astra (DataStax) Nodes, compactacions, reparacions, escalat Model de dades, nivells de consistència (Keyspaces no suporta tot CQL: les transaccions lleugeres i alguns tipus necessiten revisió)
Redis (memòria cau, limitador, pub/sub) 04-05 ElastiCache for Redis (rèpliques entre AZ) Memorystore Failover, pedaços, rèpliques Estratègia de memòria cau, TTL, claus
MinIO (km0-fotos, km0-factures, km0-backups, km0-auditoria) 04-03 S3 GCS Durabilitat (11 nous), versionat, cicle de vida, replicació entre regions Polítiques d'accés, xifratge amb claus pròpies, estructura de claus
HDFS + Spark + Airflow 04-02, 05-03, 05-05 EMR (Spark sobre S3, sense HDFS persistent) + MWAA (Airflow) Dataproc + Cloud Composer Clústers efímers, versions d'Spark, Airflow gestionat Els treballs (vendes_diaries.py), els DAG (km0_vendes_diaries), el particionament del llac a S3
Flink (alertes_estoc.py) 05-04 Managed Service for Apache Flink Dataflow (model Beam) o Flink a GKE Checkpoints, escalat, versions El codi del treball i el seu estat
Kubernetes 07-05 EKS GKE Pla de control (API server, etcd) amb SLA, actualitzacions Nodes (grups gestionats), manifestos, operadors, mesh
Vault 06-04 Secrets Manager + KMS Secret Manager + Cloud KMS Emmagatzematge, rotació programada, auditoria Quins secrets, qui els llegeix (IAM), integració als Pods
Keycloak (realm km0) 06-03 Cognito Identity Platform Alta disponibilitat, escalat, federació Realm, clients, rols, fluxos (amb menys flexibilitat que Keycloak)
Prometheus + Grafana + Loki + Tempo 07-01, 07-02 CloudWatch (mètriques, logs) + X-Ray, o Amazon Managed Prometheus/Grafana Cloud Monitoring + Cloud Logging + Cloud Trace Emmagatzematge, retenció, alertes Instrumentació (serveis/comu/), dashboards, SLOs
Kong 06-05 Kong a EKS (o API Gateway gestionat, amb menys plugins) Kong a GKE (o Apigee) Si és gestionat: escalat i disponibilitat Rutes, plugins
Mosquitto (08-02) 08-02 IoT Core (retirat en alguns proveïdors) o EMQX a EKS EMQX/HiveMQ a GKE — Tot, si s'autogestiona
Certificats km0-ca, mTLS 06-04 ACM (públics) + mesh per a mTLS Certificate Manager + mesh Emissió i renovació dels públics El mTLS intern continua sent del mesh

Tres notes sobre aquesta taula. Primera: Pub/Sub de GCP no és Kafka: no té particions ni offsets a l'estil Kafka (encara que ofereix ordenació per clau i reproducció), i el codi de consumidors amb grups i commits canvia; si es vol Kafka a GCP, és Confluent Cloud. Segona: Keyspaces és compatible amb CQL però no és Cassandra: el nivell de consistència i les transaccions lleugeres tenen diferències que cal verificar amb el repositori de 04-04 abans de migrar. Tercera: el que és gestionat no elimina l'operació, la canvia: ja no s'apedaça PostgreSQL, però continua calent dimensionar la instància, triar la finestra de manteniment (RDS reinicia per aplicar pedaços majors), provar les restauracions i vigilar les connexions.

  1. Avantatges i desafiaments: elasticitat, pagament per ús, lock-in, egress i compliment

Avantatge Què aporta a Quilòmetre Zero Condició perquè sigui real
Elasticitat La Setmana de la Verema (×15) s'absorbeix amb nodes que existeixen només aquella setmana; la resta de l'any es paga per 1/15 Que l'aplicació escali horitzontalment (els Mòduls 4 i 7 ho han fet possible) i que l'autoescalat estigui provat (07-06)
Pagament per ús Sense inversió inicial; una regió secundària buida no costa Disciplina d'apagar el que no s'usa (apartat 9)
Serveis gestionats Patroni, les reparacions de Cassandra, els pedaços de Kafka deixen de ser guàrdies de l'equip de plataforma Acceptar menys control i algunes diferències funcionals
Abast global Regions a prop de nous mercats; DR en una altra regió amb la mateixa API Disseny conscient de la latència entre regions
Seguretat de base Centre de dades certificat, xifratge en repòs per defecte, IAM granular, auditoria (CloudTrail) integrada Configurar-la: la responsabilitat compartida de l'apartat 1
Desafiament En què consisteix Com el tracta Quilòmetre Zero
Lock-in Com més gestionat, més específic del proveïdor: Keyspaces en lloc de Cassandra, Cognito en lloc de Keycloak, Step Functions (08-04) en lloc de saga_comanda.py. Migrar de proveïdor passa a ser un projecte de mesos Portabilitat selectiva: Kubernetes i contenidors per als serveis; APIs estàndard (PostgreSQL, Kafka, S3, Redis, CQL) per a les dades, de manera que el gestionat sigui substituïble per l'autogestionat; acceptar lock-in on el benefici és gran (RDS, S3) i evitar-lo on és petit (Cognito davant de Keycloak a EKS)
Cost d'egress Les dades que surten de la regió es cobren per GB (les que entren solen ser gratuïtes): les fotos servides a l'Anna, la rèplica a la regió secundària, el trànsit entre AZ (també es cobra, encara que menys) CDN davant de km0-fotos (04-03, 08-04); consumidors de Kafka a la mateixa AZ que el seu broker líder quan sigui possible (client.rack); llac i còmput a la mateixa regió
Cost que creix amb l'èxit A volum estable i alt, el núvol pot costar més que el maquinari propi amortitzat Reserves i spot (apartat 9); revisar cada any amb números
Compliment i residència de dades Les dades personals (nom, adreça i posició de l'Anna; dades de pagament) estan subjectes a normativa (a la UE, el RGPD) que condiciona on es poden emmagatzemar i processar, qui hi pot accedir (inclòs el proveïdor) i quins contractes calen. Triar una regió fora de la UE, o un servei que replica metadades a una altra regió, pot incomplir-la Advertència: aquesta decisió exigeix revisió legal, no només tècnica. Quilòmetre Zero tria regions de la UE (eu-west-1, eu-central-1), xifra les dades personals amb claus pròpies a KMS (06-02) i documenta els subencarregats; però quines dades poden sortir de la UE, amb quines clàusules contractuals i amb quina avaluació d'impacte ho decideix el responsable de protecció de dades, no l'arquitecte
Complexitat d'IAM Centenars de rols i polítiques; un s3:* sobre * en un rol de servei és la fuita de dades més comuna Mínim privilegi per servei (IAM Roles for Service Accounts a la pràctica); revisió automàtica (Access Analyzer)
Dependència d'un tercer Les regions fallen (passa diverses vegades l'any a cada proveïdor) Multi-AZ per al quotidià, DR entre regions per a l'excepcional (apartat 3)

  1. Infraestructura com a codi amb Terraform

A 07-05 es van distingir les eines de configuració (Ansible: deixar una màquina existent en un estat) de les d'aprovisionament (Terraform: crear la infraestructura mateixa), i es va remetre aquí. Quan la infraestructura és una API, descriure-la com a codi és el que permet revisar-la en pull request, reproduir-la en una altra regió i saber què existeix. Terraform (i la seva bifurcació oberta OpenTofu) és declaratiu: es descriu l'estat desitjat en fitxers .tf (llenguatge HCL) i l'eina calcula i executa les crides a l'API necessàries per arribar-hi, en l'ordre que dicten les dependències.

Quatre conceptes són suficients per llegir i escriure la pràctica de l'apartat 10:

  1. Proveïdors (providers): plugins que tradueixen recursos a crides d'API (aws, google, kubernetes, helm, kafka...). Es declaren amb versió fixada.
  2. Recursos i dades: resource "aws_db_instance" "inventari" { ... } crea; data "aws_availability_zones" "disponibles" {} consulta. Es referencien entre ells (aws_vpc.km0.id), i aquestes referències són el graf de dependències.
  3. Estat remot: Terraform desa en un fitxer d'estat la correspondència entre el que s'ha declarat i el que existeix (ids reals). Aquest fitxer és crític i compartit: es desa en un backend remot (S3 amb versionat, i una taula de DynamoDB com a cadenat perquè dues persones no apliquin alhora; és el mateix problema d'exclusió mútua de 03-03 resolt amb un servei gestionat), es xifra i mai no es puja a Git. Conté secrets (contrasenyes inicials d'RDS), cosa que reforça l'anterior.
  4. Mòduls: carpetes de .tf reutilitzables amb variables d'entrada i sortides (module "vpc" { source = "terraform-aws-modules/vpc/aws" ... }). Quilòmetre Zero usa mòduls de la comunitat per a VPC i EKS (centenars de recursos que no val la pena escriure a mà) i mòduls propis per a cada entorn.

El cicle és terraform init (descarrega proveïdors i mòduls, connecta amb l'estat), terraform plan (calcula el diff entre el que s'ha declarat i l'estat i el mostra: + crear, ~ modificar, - destruir, -/+ reemplaçar), i terraform apply (l'executa després de confirmar). El plan és la peça més valuosa: s'executa al pipeline (07-05) i s'enganxa a la pull request; un -/+ sobre aws_db_instance.inventari (reemplaçar la base de dades) és el que una revisió ha d'aturar. En GitOps, apply només l'executa el pipeline després del merge, mai una persona des del seu portàtil.

  1. Arquitectura ben dissenyada: els pilars

Els proveïdors publiquen marcs d'"arquitectura ben dissenyada" (well-architected) amb pilars que serveixen com a llista de comprovació. Els sis d'AWS (GCP té una llista equivalent) coincideixen gairebé un a un amb mòduls del curs:

Pilar Pregunta que fa On ho ha treballat Quilòmetre Zero
Excel·lència operativa S'opera amb codi, s'observa, s'aprèn dels incidents? IaC (07-05, aquesta lliçó), observabilitat (07-01/07-02), postmortems i runbooks (07-03), GitOps
Seguretat Identitat, mínim privilegi, xifratge, auditoria, defensa en profunditat? Mòdul 6 complet; IAM i grups de seguretat d'aquesta lliçó
Fiabilitat Sobreviu a fallades de component, AZ i regió? S'ha provat? Redundància i failover (07-03), resiliència (07-04), caos (07-06), multi-AZ i DR (apartat 3)
Eficiència de rendiment S'usen els recursos adequats a cada càrrega i es mesuren? Emmagatzematge per càrrega (Mòdul 4), memòria cau (04-05), autoescalat, SLO p99 (07-01)
Optimització de costos Es paga pel que s'usa, es mesura i es revisa? FinOps (apartat 9)
Sostenibilitat Es minimitza el consum: apagar el que està ociós, regions amb energia neta, eficiència del codi? Autoescalat a la baixa, clústers efímers d'Spark, retenció de dades (04-02)

La seva utilitat pràctica és la revisió periòdica: cada semestre, recórrer les preguntes del marc sobre la plataforma i anotar què falta. És un exercici del projecte final (08-05).

  1. FinOps: etiquetes, reserves, spot, pressupostos i el cost de Quilòmetre Zero

Al núvol, el cost és una mètrica més del sistema, i com qualsevol mètrica cal mesurar-la, atribuir-la i posar-hi alertes. La disciplina s'anomena FinOps i, en el seu nivell bàsic, es redueix a cinc pràctiques:

  1. Etiquetatge (tagging): tot recurs porta etiquetes equip, servei, entorn, centre_cost. Sense elles, la factura és un número; amb elles, "l'equip de repartiment va gastar 1 840 € al setembre, el 60 % en MSK". Terraform les aplica per defecte a tot (default_tags).
  2. Reserves i plans d'estalvi: comprometre's a un ús durant 1 o 3 anys a canvi d'un 30-60 % de descompte. S'aplica a la base que sempre està encesa (els nodes mínims d'EKS, RDS, MSK, ElastiCache), mai al pic.
  3. Instàncies spot: capacitat sobrant del proveïdor amb un 60-90 % de descompte, que pot ser retirada amb 2 minuts d'avís. Ideal per al que tolera interrupcions i es reinicia sol: els executors d'Spark (05-03 ja tolerava la pèrdua d'executors recomputant particions), els nodes d'EKS per a càrregues sense estat amb diverses rèpliques. Mai per a RDS, Cassandra, Kafka ni el pla de control.
  4. Pressupostos i alertes: un pressupost per equip i entorn, amb alerta al 80 % i al 100 % previstos, i detecció d'anomalies (un NAT gateway que de sobte mou 2 TB perquè algú ha servit les fotos sense CDN). Són alertes com les de 07-01, cap al mateix canal.
  5. Apagar el que està ociós: entorns de staging apagats de nit i el cap de setmana (amb Terraform o amb programacions), clústers d'Spark efímers (EMR arrenca per al DAG km0_vendes_diaries i es destrueix en acabar), volums i snapshots orfes.

Cost mensual aproximat de Quilòmetre Zero a AWS

Els preus següents són ficticis i orientatius, de l'ordre de magnitud correcte per a una regió europea, i serveixen per al raonament, no com a pressupost. L'exercici útil és veure on és el cost, no la xifra exacta.

Component Dimensionament Cost base mensual (€) Amb reserves / spot (€) Observació
EKS pla de control 1 clúster 70 70 Fix
Nodes EKS (serveis, Kong, mesh, observabilitat) 6 × 4 vCPU/16 GB permanents + fins a 12 en campanya 900 + 300 (mitjana de pics) 550 + 120 (reserva base, spot als pics) Partida més gran de còmput
RDS PostgreSQL Multi-AZ (km0_inventari) 2 vCPU/8 GB × 2 (standby), 200 GB 380 250 Multi-AZ duplica
RDS PostgreSQL (km0_analitica) 2 vCPU/8 GB, 500 GB 210 140 Sense Multi-AZ: tolera RTO d'hores
MSK 3 brokers × 2 vCPU/8 GB, 1 TB total 650 450 Cost per broker-hora + emmagatzematge
Cassandra a EC2 (3 nodes) 3 × 4 vCPU/16 GB + 3 × 500 GB SSD 480 320 Keyspaces seria per lectura/escriptura; amb 1,2 M comandes/mes surt similar
ElastiCache Redis 2 nodes 2 GB (primari + rèplica) 90 60
S3 (km0-fotos 30 GB, km0-factures, km0-backups 800 GB, km0-auditoria) ~1 TB + peticions 35 35 L'emmagatzematge és barat
Egress a Internet (fotos, API) 3 TB/mes sense CDN 240 240 → ~40 amb CDN (08-04) La partida que la CDN redueix
Trànsit entre AZ ~2 TB/mes (rèpliques, consumidors) 40 40 S'oblida sempre
NAT gateway 2 (un per AZ activa) + 500 GB 110 110 Els endpoints de VPC per a S3 el redueixen
ALB 1 + trànsit 45 45
EMR (Spark) efímer 2 h/dia × 6 nodes spot 180 60 Spot: −70 %
MWAA (Airflow) Entorn petit 320 320 Car per al que fa: candidat a Airflow a EKS (~40)
Flink gestionat 4 KPU 420 420 Alternativa: Flink a EKS
Secrets Manager + KMS 60 secrets, 3 claus 45 45
CloudWatch (logs 200 GB/mes, mètriques) 180 180 O Loki/Tempo/Prometheus a EKS (cost en nodes)
Regió secundària (pilot light) Rèplica RDS, S3 CRR, MSK mínim 350 350 ~10 % de la primària
Suport del proveïdor Pla de negoci 250 250
Total aproximat ≈ 5 300 ≈ 3 900

El que la taula ensenya: el còmput (EKS + bases de dades + Kafka) és dos terços del total; les reserves estalvien un 25-30 % sobre la base; l'egress és la partida més sensible al disseny (la CDN de 08-04 la divideix per sis); i dos serveis gestionats (MWAA, Flink gestionat) costen més que els seus equivalents autogestionats al clúster, cosa que il·lustra que "gestionat" no sempre guanya: la decisió depèn de quant costa el temps de l'equip que l'operaria.

  1. Pràctica: Quilòmetre Zero a AWS amb Terraform i EKS

L'estructura del codi d'infraestructura, que s'afegeix al projecte km0/:

km0/infra/
├── aws/
│   ├── main.tf            # proveïdors, backend d'estat, VPC, EKS, RDS, MSK, ElastiCache, S3, IAM
│   ├── variables.tf       # region, entorn, mides
│   ├── outputs.tf         # endpoints que necessiten els manifestos k8s/
│   └── entorns/
│       ├── prod.tfvars
│       └── staging.tfvars
└── gcp/                   # l'equivalent a GCP (només taula en aquesta lliçó)

El que segueix és main.tf explicat bloc a bloc. Està simplificat (hi falten alguns paràmetres de seguretat i de xarxa que un mòdul real inclou), però cada bloc és real i correspon a una decisió de les seccions anteriors.

Bloc 1: proveïdors i estat remot

# km0/infra/aws/main.tf
terraform {
  required_version = ">= 1.6"
  required_providers {
    aws        = { source = "hashicorp/aws",        version = "~> 5.40" }
    kubernetes = { source = "hashicorp/kubernetes", version = "~> 2.27" }
    helm       = { source = "hashicorp/helm",       version = "~> 2.12" }
  }
  # Estat remot: S3 amb versionat (per recuperar un estat anterior) i
  # DynamoDB com a cadenat (dos 'apply' simultanis s'exclouen).
  backend "s3" {
    bucket         = "km0-terraform-estat"
    key            = "prod/infra.tfstate"
    region         = "eu-west-1"
    dynamodb_table = "km0-terraform-cadenat"
    encrypt        = true
  }
}

provider "aws" {
  region = var.region                       # "eu-west-1": regió de la UE (compliment, apartat 6)
  default_tags {                            # FinOps: tota peça porta aquestes etiquetes
    tags = { projecte = "km0", entorn = var.entorn, gestionat_per = "terraform" }
  }
}

data "aws_availability_zones" "disponibles" { state = "available" }
locals {
  azs = slice(data.aws_availability_zones.disponibles.names, 0, 3)   # tres AZ: a, b, c
}

Bloc 2: la VPC amb tres capes per AZ

module "vpc" {
  source  = "terraform-aws-modules/vpc/aws"
  version = "~> 5.5"

  name = "km0-${var.entorn}"
  cidr = "10.0.0.0/16"
  azs  = local.azs

  public_subnets   = ["10.0.0.0/24",  "10.0.1.0/24",  "10.0.2.0/24"]    # ALB, NAT
  private_subnets  = ["10.0.10.0/23", "10.0.12.0/23", "10.0.14.0/23"]   # nodes EKS (512 IPs per AZ: els Pods consumeixen IPs)
  database_subnets = ["10.0.20.0/24", "10.0.21.0/24", "10.0.22.0/24"]   # RDS, MSK, ElastiCache, Cassandra

  enable_nat_gateway     = true
  one_nat_gateway_per_az = true          # sense això, un NAT en una sola AZ és un punt únic de fallada per a la sortida
  create_database_subnet_group = true

  # Etiquetes que EKS necessita per descobrir en quines subxarxes crear balancejadors
  public_subnet_tags  = { "kubernetes.io/role/elb" = 1 }
  private_subnet_tags = { "kubernetes.io/role/internal-elb" = 1 }
}

# Endpoint de VPC per a S3: el trànsit a km0-fotos no surt a Internet ni passa pel NAT (cost i seguretat)
resource "aws_vpc_endpoint" "s3" {
  vpc_id          = module.vpc.vpc_id
  service_name    = "com.amazonaws.${var.region}.s3"
  route_table_ids = module.vpc.private_route_table_ids
}

Bloc 3: EKS amb grups de nodes permanents i spot

module "eks" {
  source  = "terraform-aws-modules/eks/aws"
  version = "~> 20.8"

  cluster_name    = "km0-${var.entorn}"
  cluster_version = "1.29"
  vpc_id          = module.vpc.vpc_id
  subnet_ids      = module.vpc.private_subnets       # els nodes mai en subxarxes públiques

  cluster_endpoint_public_access = true              # kubectl des del pipeline (restringir per CIDR a prod)
  enable_irsa                    = true              # IAM Roles for Service Accounts (bloc 6)

  eks_managed_node_groups = {
    base = {                                         # capacitat permanent: aquí van les reserves
      instance_types = ["m6i.xlarge"]                # 4 vCPU / 16 GB
      min_size = 6, desired_size = 6, max_size = 9
      labels = { "km0/pool" = "base" }
    }
    campanya = {                                     # capacitat elàstica en spot: només Pods sense estat
      instance_types = ["m6i.xlarge", "m5.xlarge", "m6a.xlarge"]  # diversos tipus: més probabilitat de spot
      capacity_type  = "SPOT"
      min_size = 0, desired_size = 0, max_size = 12
      labels = { "km0/pool" = "spot" }
      taints = [{ key = "km0/spot", value = "true", effect = "NO_SCHEDULE" }]  # només qui ho toleri explícitament
    }
  }
}

Els Pods de comandes i cataleg porten a k8s/comandes-deployment.yaml una toleration per a km0/spot i una affinity que prefereix base, de manera que en campanya desborden a spot; Cassandra, Kafka (si fos autogestionat) i les bases de dades no toleren el taint i mai no cauen en spot. L'autoescalador del clúster (Karpenter, instal·lat amb el proveïdor helm) és qui passa el grup campanya de 0 a 12 segons els Pods pendents.

Bloc 4: RDS PostgreSQL Multi-AZ per a km0_inventari

resource "aws_db_subnet_group" "dades" {
  name       = "km0-dades"
  subnet_ids = module.vpc.database_subnets
}

resource "aws_security_group" "rds" {
  name   = "km0-rds"
  vpc_id = module.vpc.vpc_id
  ingress {                                          # només 5432 i només des dels nodes d'EKS
    from_port = 5432, to_port = 5432, protocol = "tcp"
    security_groups = [module.eks.node_security_group_id]
  }
}

resource "aws_db_instance" "inventari" {
  identifier        = "km0-inventari-${var.entorn}"
  engine            = "postgres"
  engine_version    = "16"
  instance_class    = var.entorn == "prod" ? "db.m6g.large" : "db.t4g.medium"
  allocated_storage = 200
  storage_encrypted = true
  kms_key_id        = aws_kms_key.dades.arn           # clau pròpia (06-02): la rotació i l'accés els controlem nosaltres

  db_name  = "km0_inventari"
  username = "km0_admin"
  manage_master_user_password = true                 # la contrasenya la genera i desa Secrets Manager

  multi_az               = var.entorn == "prod"      # standby síncron en una altra AZ: substitueix Patroni (07-03)
  backup_retention_period = 14                       # backups diaris + WAL continu: PITR de 14 dies
  backup_window          = "02:00-03:00"
  maintenance_window     = "sun:03:30-sun:04:30"     # RDS reinicia per a pedaços majors: finestra coneguda
  deletion_protection    = var.entorn == "prod"      # un 'terraform destroy' accidental no esborra producció
  performance_insights_enabled = true

  db_subnet_group_name   = aws_db_subnet_group.dades.name
  vpc_security_group_ids = [aws_security_group.rds.id]
}

# Rèplica de lectura a la regió secundària: la base del pilot light (07-03, apartat 3)
resource "aws_db_instance" "inventari_dr" {
  provider            = aws.secundaria                # un segon proveïdor amb region = "eu-central-1"
  identifier          = "km0-inventari-dr"
  replicate_source_db = aws_db_instance.inventari.arn
  instance_class      = "db.t4g.medium"               # petita: es redimensiona en promoure
  kms_key_id          = aws_kms_key.dades_dr.arn
  skip_final_snapshot = true
}

El que aquí substitueix el que s'havia construït a mà: multi_az = true és Patroni + etcd + rèplica síncrona + failover automàtic (RDS canvia el DNS de l'endpoint a l'standby en ~60 s; l'aplicació només necessita reconnectar, que és el que el pool amb reintents de 07-04 ja fa); backup_retention_period és el pgBackRest i l'arxivat de WAL de 07-03. El que no substitueix: la prova de restauració setmanal, que continua sent un treball d'Airflow que restaura l'últim snapshot en una instància temporal i executa comprovacions.

Bloc 5: MSK, ElastiCache i S3

resource "aws_msk_cluster" "esdeveniments" {
  cluster_name           = "km0-esdeveniments-${var.entorn}"
  kafka_version          = "3.6.0"
  number_of_broker_nodes = 3                                  # un per AZ: rèpliques de partició repartides
  broker_node_group_info {
    instance_type   = "kafka.m5.large"
    client_subnets  = module.vpc.database_subnets
    security_groups = [aws_security_group.msk.id]
    storage_info { ebs_storage_info { volume_size = 350 } }   # 3 × 350 GB ≈ 1 TB: retenció de 02-04
  }
  encryption_info {
    encryption_in_transit { client_broker = "TLS", in_cluster = true }   # TLS obligatori (06-02)
  }
  configuration_info {
    arn      = aws_msk_configuration.esdeveniments.arn
    revision = aws_msk_configuration.esdeveniments.latest_revision
  }
}

resource "aws_msk_configuration" "esdeveniments" {
  name              = "km0-esdeveniments"
  server_properties = <<-EOT
    default.replication.factor=3
    min.insync.replicas=2
    auto.create.topics.enable=false
  EOT                                                          # els mateixos valors de 02-04 i 07-03
}

resource "aws_elasticache_replication_group" "cache" {
  replication_group_id = "km0-cache-${var.entorn}"
  description          = "Memòria cau de catàleg, limitador, pub/sub de repartiment"
  engine               = "redis"
  node_type            = "cache.t4g.small"
  num_cache_clusters   = 2                                     # primari + rèplica en una altra AZ
  automatic_failover_enabled = true
  multi_az_enabled     = true
  at_rest_encryption_enabled = true
  transit_encryption_enabled = true
  subnet_group_name    = aws_elasticache_subnet_group.dades.name
  security_group_ids   = [aws_security_group.redis.id]
}

resource "aws_s3_bucket" "fotos" {
  bucket = "km0-fotos-${var.entorn}"
}
resource "aws_s3_bucket_versioning" "fotos" {                  # versionat, com a MinIO (04-03)
  bucket = aws_s3_bucket.fotos.id
  versioning_configuration { status = "Enabled" }
}
resource "aws_s3_bucket_public_access_block" "fotos" {         # mai públic: se serveix per URL presignada o CDN
  bucket = aws_s3_bucket.fotos.id
  block_public_acls = true, block_public_policy = true, ignore_public_acls = true, restrict_public_buckets = true
}
resource "aws_s3_bucket_lifecycle_configuration" "fotos" {
  bucket = aws_s3_bucket.fotos.id
  rule {
    id     = "versions-antigues"
    status = "Enabled"
    noncurrent_version_expiration { noncurrent_days = 30 }     # la regla de cicle de vida de 04-03
  }
}
resource "aws_s3_bucket_replication_configuration" "fotos_dr" {  # CRR a la regió secundària (pilot light)
  bucket = aws_s3_bucket.fotos.id
  role   = aws_iam_role.replicacio_s3.arn
  rule {
    id     = "dr"
    status = "Enabled"
    destination { bucket = aws_s3_bucket.fotos_dr.arn, storage_class = "STANDARD_IA" }
  }
}

Bloc 6: IAM roles per a service accounts (IRSA)

Els Pods de cataleg necessiten escriure a km0-fotos; els de comandes, llegir els seus secrets. En lloc de credencials de llarga durada en un Secret de Kubernetes (el que 06-04 evitava amb Vault), EKS permet associar un rol IAM a una service account de Kubernetes: el Pod obté credencials temporals automàticament, amb el mínim privilegi, i CloudTrail registra qui ha fet què. És la identitat de càrrega de treball de 06-04 (SPIFFE) amb l'IAM del proveïdor.

data "aws_iam_policy_document" "cataleg_fotos" {
  statement {
    actions   = ["s3:PutObject", "s3:GetObject", "s3:DeleteObject"]   # sense s3:* ni ListAllMyBuckets
    resources = ["${aws_s3_bucket.fotos.arn}/fotos/*"]                 # només el prefix de fotos
  }
}

module "irsa_cataleg" {
  source  = "terraform-aws-modules/iam/aws//modules/iam-role-for-service-accounts-eks"
  version = "~> 5.39"
  role_name = "km0-cataleg-${var.entorn}"
  role_policy_arns = { fotos = aws_iam_policy.cataleg_fotos.arn }
  oidc_providers = {
    eks = {
      provider_arn               = module.eks.oidc_provider_arn
      namespace_service_accounts = ["km0-prod:cataleg"]          # NOMÉS aquesta service account pot assumir el rol
    }
  }
}

Al manifest k8s/cataleg-deployment.yaml n'hi ha prou amb serviceAccountName: cataleg i l'anotació eks.amazonaws.com/role-arn: <arn del rol> a la ServiceAccount; l'SDK d'AWS al Pod (boto3, el mateix fotos.py de 04-03 apuntant a S3 en lloc de a MinIO) troba les credencials sol.

Bloc 7: sortides per als manifestos

# km0/infra/aws/outputs.tf
output "rds_inventari_endpoint" { value = aws_db_instance.inventari.address }
output "msk_bootstrap_tls"      { value = aws_msk_cluster.esdeveniments.bootstrap_brokers_tls }
output "redis_endpoint"         { value = aws_elasticache_replication_group.cache.primary_endpoint_address }
output "bucket_fotos"           { value = aws_s3_bucket.fotos.bucket }
output "eks_cluster_name"       { value = module.eks.cluster_name }

Desplegar els manifestos k8s/ de 07-05 a EKS

Els manifestos no canvien de forma: canvien els valors de configuració que apunten a les dependències. A 07-05, comandes-config tenia KAFKA_BOOTSTRAP=kafka-0.kafka:9092; ara el genera el pipeline a partir de les sortides de Terraform:

# km0/infra/aws/desplegar.sh — executat pel pipeline després de 'terraform apply'
set -euo pipefail
cd km0/infra/aws
terraform init -input=false
terraform plan -var-file=entorns/prod.tfvars -out=plan.bin           # el pla s'adjunta a la PR
terraform apply -input=false plan.bin

aws eks update-kubeconfig --name "$(terraform output -raw eks_cluster_name)" --region eu-west-1

# ConfigMaps amb els endpoints reals (els manifestos de 07-05 els referencien pel nom)
kubectl -n km0-prod create configmap plataforma-endpoints \
  --from-literal=KAFKA_BOOTSTRAP="$(terraform output -raw msk_bootstrap_tls)" \
  --from-literal=PG_INVENTARI_HOST="$(terraform output -raw rds_inventari_endpoint)" \
  --from-literal=REDIS_URL="rediss://$(terraform output -raw redis_endpoint):6379" \
  --from-literal=S3_BUCKET_FOTOS="$(terraform output -raw bucket_fotos)" \
  --dry-run=client -o yaml | kubectl apply -f -

# Els mateixos manifestos de 07-05: Deployments, Services, HPA, PDB, NetworkPolicies, canary
kubectl apply -k km0/k8s/overlays/aws-prod       # kustomize: la base de 07-05 + pedaços (serviceAccountName, tolerations spot)
kubectl -n km0-prod rollout status deployment/comandes --timeout=300s

El que desapareix de k8s/ en passar a serveis gestionats: els StatefulSets de PostgreSQL amb Patroni, de Kafka i de Redis, i els seus operadors. El que es queda: els sis serveis, Kong, el mesh, Cassandra (si no s'adopta Keyspaces), Prometheus/Loki/Tempo (si no s'adopta CloudWatch) i Mosquitto. L'HPA de 07-05 funciona igual; el que és nou és Karpenter a sota. I les proves de 07-06 es repeteixen contra l'entorn staging desplegat amb staging.tfvars (instàncies petites, sense Multi-AZ, sense DR), que s'apaga a les nits.

L'equivalent a GCP

Recurs a AWS (main.tf) Equivalent a GCP (proveïdor google) Diferència rellevant
VPC amb subxarxes per AZ google_compute_network + google_compute_subnetwork (les subxarxes són regionals, abasten totes les zones) Menys subxarxes; la distribució per zona la fa GKE
EKS + grups de nodes GKE (google_container_cluster regional + google_container_node_pool, amb Autopilot com a opció sense nodes) GKE regional reparteix el pla de control i els nodes en 3 zones per defecte
RDS Multi-AZ Cloud SQL amb availability_type = "REGIONAL" Failover similar (~60 s); PITR amb point_in_time_recovery_enabled
MSK Confluent Cloud (Kafka) o Pub/Sub (model diferent: sense particions ni offsets a l'estil Kafka) Pub/Sub exigeix reescriure consumidors; Confluent conserva el codi
ElastiCache Memorystore for Redis (tier = "STANDARD_HA") Sense mode clúster als nivells bàsics
S3 + CRR GCS amb bucket dual-region o multi-region La replicació entre regions és una propietat del bucket, no una regla
IRSA Workload Identity (service account de Kubernetes ↔ service account de Google) Mateix concepte
Secrets Manager + KMS Secret Manager + Cloud KMS Equivalent
EMR / MWAA / Flink gestionat Dataproc / Cloud Composer / Dataflow Dataflow usa el model Beam, no l'API de Flink
CloudWatch Cloud Monitoring + Cloud Logging + Cloud Trace Equivalent
Estat remot a S3 + DynamoDB Backend gcs (el cadenat el dona el mateix bucket) Més simple

Errors Comuns i Consells

  • Suposar que "gestionat" significa "sense operar". RDS continua reiniciant a la finestra de manteniment, MSK continua necessitant dimensionar particions, i ningú no prova les restauracions per nosaltres. El treball canvia, no desapareix.
  • Una sola AZ "per estalviar". Un NAT, una subxarxa, RDS sense Multi-AZ: la primera incidència d'AZ del proveïdor és una caiguda total. Multi-AZ per a tot el que sigui al camí d'una comanda.
  • Oblidar l'egress i el trànsit entre AZ al pressupost. Són les dues partides que sorprenen a la primera factura. CDN per al que és públic, endpoints de VPC per a S3, client.rack als consumidors de Kafka.
  • Rols IAM amb *. Un rol de servei amb s3:* sobre tots els buckets converteix un Pod compromès en una fuita total. Una política per service account, amb accions i recursos concrets (bloc 6).
  • L'estat de Terraform a Git o en un portàtil. Conté secrets i és l'únic mapa del que existeix. Backend remot xifrat amb versionat i cadenat, sempre.
  • Aplicar sense llegir el pla. Un -/+ sobre una base de dades és una destrucció amb recreació. El pla va a la pull request i deletion_protection a producció.
  • Triar la regió només per preu o latència. Les dades personals tenen restriccions de residència; la regió és una decisió amb revisió legal, i documentada.
  • Comprar reserves per al pic. Les reserves cobreixen la base que sempre està encesa; el pic va a spot o a sota demanda. Comprar de més és pagar per capacitat que no s'usa.
  • Consell: desplega primer staging amb la mateixa configuració de Terraform i mides petites; el 90 % dels errors de xarxa, IAM i grups de seguretat apareixen allà a cost mínim.
  • Consell: posa el cost a Grafana al costat de la resta de mètriques (els proveïdors exporten la despesa diària per etiqueta). Un gràfic d'euros per comanda és la mètrica de negoci que més converses útils genera.

Exercicis

Exercici 1: dominis de fallada a la VPC

Revisa el main.tf de l'apartat 10 i respon: (a) si l'AZ b desapareix sencera, quins components de Quilòmetre Zero es veuen afectats, quin mecanisme recupera cadascun i en quant de temps aproximat; (b) què hauria passat amb one_nat_gateway_per_az = false; (c) quin component del main.tf no sobreviuria a la pèrdua de la regió i què caldria executar per recuperar-lo.

Exercici 2: la factura del canvi de decisió

Usant la taula de costos de l'apartat 9 (preus ficticis), calcula l'estalvi o el sobrecost mensual aproximat d'aquestes tres decisions i raona si compensen: (a) substituir MWAA per Airflow a EKS (estimat: 40 € de nodes); (b) passar km0_analitica d'RDS a Multi-AZ; (c) moure els nodes permanents d'EKS de sota demanda a una reserva de 3 anys (descompte estimat 55 % en lloc de 40 %) sabent que l'equip planeja reduir a 4 nodes permanents d'aquí a un any.

Exercici 3: lock-in selectiu

Per a cadascun d'aquests components, decideix si Quilòmetre Zero hauria d'usar el servei gestionat del proveïdor o desplegar-lo a EKS, aplicant el criteri de "lock-in selectiu" de l'apartat 6 (benefici del gestionat davant del cost de la dependència): PostgreSQL, Kafka, Cassandra, Keycloak, Vault, Prometheus/Grafana. Justifica cadascuna en dues frases.

Solucions

Exercici 1.

(a) Amb l'AZ b caiguda: RDS km0_inventari: l'standby era a b (o el primari; RDS ho decideix); si era l'standby, no passa res visible i RDS en crea un de nou en una altra AZ; si era el primari, failover automàtic a l'standby en ~60 s, durant els quals les reserves d'estoc fallen i la saga reintenta (07-04). MSK: el broker 2 cau; les particions el líder de les quals hi era trien nou líder entre les ISR (03-03) en segons; amb min.insync.replicas=2 i RF=3 es continua escrivint. Cassandra: un node de tres; amb QUORUM (2 de 3) es continua llegint i escrivint. ElastiCache: si el primari era a b, failover automàtic a la rèplica (~30 s) i la memòria cau es reomple; si era la rèplica, res. EKS: els nodes de b desapareixen; Kubernetes reprograma els seus Pods a a i c (07-05: topologySpreadConstraints garantia que cap servei no tingués totes les seves rèpliques a b) en 1-3 minuts, i Karpenter afegeix nodes si falta capacitat. ALB: deixa d'enviar a b després de fallar els health checks (segons). NAT: el de b cau, però els nodes de b també, així que ningú no el necessita.

(b) Un sol NAT a, per exemple, l'AZ a: si cau a, els nodes de b i c continuen vius però perden la sortida a Internet: pagaments no assoleix la passarel·la, el pipeline no descarrega imatges. Un component sa queda inutilitzat per un domini de fallada aliè: exactament el que 07-03 anomenava redundància no independent.

(c) Tot el de la regió primària. El que hi ha a la secundària: la rèplica d'RDS (inventari_dr), el bucket replicat i l'MSK mínim amb MirrorMaker. Per recuperar: terraform apply del mateix codi amb region = "eu-central-1" (uns 20-30 min per a VPC + EKS + ElastiCache), promoure la rèplica d'RDS (aws rds promote-read-replica, ~5 min), desplegar els manifestos apuntant als nous endpoints, restaurar Cassandra des dels snapshots replicats a S3 (el pas més llarg i el que fixa l'RPO de comandes), i canviar el registre de Route 53. És el runbook del pilot light de 07-03, i l'RTO real és el que surti al game day, no el de la taula.

Exercici 2.

(a) MWAA 320 € → Airflow a EKS 40 €: estalvi ≈ 280 €/mes (3 360 €/any). Compensa si l'equip de plataforma assumeix operar Airflow (actualitzacions, base de metadades, guàrdia); amb l'operador de Kubernetes i una instància petita d'RDS per a les metadades (~30 € més), continua compensant, i a més evita una dependència específica del proveïdor. És l'exemple que el gestionat no sempre guanya.

(b) 210 € → aproximadament el doble, ≈ 420 €: sobrecost ≈ 210 €/mes. Per a km0_analitica, la pèrdua temporal de la qual no impedeix vendre (RTO d'hores a 07-03, es reconstrueix des del llac), no compensa; la decisió de 07-03 continua sent vàlida.

(c) Base de nodes: 900 € sota demanda; amb reserva d'1 any al 40 %: 540 €; amb reserva de 3 anys al 55 %: 405 €. Diferència a favor de 3 anys: 135 €/mes. Però la reserva de 3 anys cobreix 6 nodes i d'aquí a un any només se'n faran servir 4: es pagarien 2 nodes reservats sense usar durant 2 anys (≈ 2 × 67 € × 24 mesos ≈ 3 200 €), davant d'un estalvi de 135 × 12 = 1 620 € el primer any. No compensa: reserva d'1 any, renegociable quan el dimensionament canviï. Les reserves es compren per a la base estable, i "estable" inclou el futur previsible.

Exercici 3.

  • PostgreSQL → RDS (gestionat). Benefici alt (Multi-AZ, PITR, pedaços substitueixen el clúster de Patroni complet) i lock-in baix: és PostgreSQL estàndard, un pg_dump el mou a qualsevol lloc.
  • Kafka → MSK (gestionat). Benefici alt (el rolling de brokers de 07-05 i la gestió de discos desapareixen) i lock-in baix: protocol Kafka estàndard, MirrorMaker el replica a qualsevol Kafka. A GCP, Confluent abans que Pub/Sub per la mateixa raó.
  • Cassandra → a EKS (o EC2), no Keyspaces. Keyspaces canvia la semàntica (transaccions lleugeres, alguns tipus, model de cost per petició) i el repositori de 04-04 necessitaria revisió: lock-in alt per a un benefici mitjà. Es manté autogestionat amb l'operador de 07-05, i es revisa si l'equip de plataforma no dona l'abast.
  • Keycloak → a EKS, no Cognito. Cognito és més limitat en fluxos i personalització, i migrar usuaris i clients d'un proveïdor d'identitat és dolorós: lock-in alt. Keycloak en dues rèpliques amb RDS al darrere és fàcil d'operar.
  • Vault → Secrets Manager (gestionat), amb matís. Per als secrets estàtics i la seva rotació, Secrets Manager amb IRSA és més simple i s'integra amb IAM. Les credencials dinàmiques de base de dades i la PKI per a mTLS que Vault donava a 06-04 no tenen equivalent directe: si el mesh assumeix el mTLS (08-01), Vault es pot retirar; si no, es manté Vault a EKS només per a això.
  • Prometheus/Grafana → a EKS. La instrumentació de serveis/comu/ és estàndard (OpenTelemetry, exposició Prometheus) i els dashboards i alertes de 07-01 ja existeixen; CloudWatch costaria més i canviaria el llenguatge de consulta (PromQL). Lock-in evitable a cost baix. Els serveis gestionats de Prometheus/Grafana del proveïdor són un punt mitjà acceptable si l'emmagatzematge de mètriques creix.

Conclusió

El núvol converteix la infraestructura en una API, i amb això canvia tres coses en el disseny de Quilòmetre Zero. La primera és la responsabilitat: IaaS, PaaS, SaaS i FaaS mouen la línia entre el que fa el proveïdor i el que fa el client, però les dades i les identitats queden sempre en aquest costat. La segona és la geografia: regions i zones de disponibilitat són dominis de fallada amb nom, les AZ absorbeixen les fallades quotidianes amb Multi-AZ en tot el que toca una comanda, i la regió secundària del pilot light de 07-03 passa a ser codi de Terraform que no costa res mentre no s'executa. La tercera és la substitució: RDS per Patroni, MSK pels brokers d'Ansible, S3 per MinIO, EKS pel clúster propi, amb una taula que diu què es guanya i què es perd a cada fila, i un criteri de lock-in selectiu (gestionat on el benefici és alt i l'API estàndard, propi on la dependència costaria més que l'operació). Terraform descriu la VPC de tres capes per AZ, EKS amb nodes base i spot, RDS Multi-AZ amb la seva rèplica en una altra regió, MSK, ElastiCache, S3 amb versionat i replicació, i IRSA perquè cada Pod tingui només els permisos que necessita; els manifestos de 07-05 es despleguen gairebé sense canvis, i el pla de Terraform es revisa a la pull request com qualsevol codi. Els pilars de l'arquitectura ben dissenyada resumeixen el curs com a llista de comprovació, i FinOps hi afegeix el cost com a mètrica: etiquetes, reserves per a la base, spot per a Spark i els pics, pressupostos amb alerta, i una taula que mostra que el còmput és dos terços de la factura, que dos serveis gestionats costen més que els seus equivalents al clúster, i que l'egress és la partida que el disseny més pot reduir.

Precisament aquesta partida, i dues preguntes més, obren la lliçó següent. Cal un servei a Kubernetes, amb les seves rèpliques, les seves sondes i la seva guàrdia, per generar una miniatura cada vegada que la Formatgeria Montblanc puja una foto, o per produir un PDF quan arriba un pagament.confirmat? I per què les fotos de formatge-curat han de viatjar des d'Irlanda fins al mòbil de l'Anna a València a cada visita, quan podrien ser a vint quilòmetres d'ella? Funcions que existeixen només mentre hi ha un esdeveniment per processar, i còmput i memòria cau a la vora de la xarxa: Arquitectures Serverless i Edge Computing.

Curs d'Arquitectures Distribuïdes

Mòdul 1: Introducció als Sistemes Distribuïts

Mòdul 2: Comunicació en Sistemes Distribuïts

Mòdul 3: Consistència i Replicació

Mòdul 4: Emmagatzematge Distribuït

Mòdul 5: Computació Distribuïda

Mòdul 6: Seguretat en Sistemes Distribuïts

Mòdul 7: Monitoratge i Manteniment

Mòdul 8: Casos d'Estudi i Aplicacions

© Copyright 2026. Tots els drets reservats