El pipeline de 05-03 acaba en kubectl rollout status i en un commit que Argo CD aplica. Entre "aplicar" i "desplegat" passa una cosa que fins ara hem donat per feta: les rèpliques de servei-comandes:1.0.0 han de deixar pas a les de 1.0.1 sense que ni un sol POST /v1/comandes rebi un error de connexió. Quan es desplegava cada dues setmanes de matinada, un tall de 30 s es tolerava; quan cada servei desplega diverses vegades al dia en horari comercial (que és l'objectiu DORA de 05-03), qualsevol tall multiplicat per sis serveis i per N desplegaments és inacceptable. Aquesta lliçó explica les quatre estratègies (recreate, rolling, blue-green, canary), la seva implementació a Kubernetes amb els manifestos de 05-02, la condició que totes comparteixen (dues versions conviuen, així que contractes i esquemes de base de dades han de ser compatibles cap enrere), els feature flags com a manera de separar desplegament de release, i l'estratègia triada per TechCorp per a cada servei. Com el mesh fa el canary per pes exacte queda per a 05-05.

Contingut

  1. Desplegar sense talls com a requisit
  2. Recreate davant de RollingUpdate
  3. Compatibilitat cap enrere durant el rolling: contractes i expand/contract
  4. Blue-green
  5. Canary
  6. Feature flags: desplegament no és release
  7. Taula comparativa
  8. L'estratègia de TechCorp per servei
  9. Desplegar canvis en consumidors d'esdeveniments
  10. Rollback, forward-fix i automatització progressiva

  1. Desplegar sense talls com a requisit

Un desplegament "sense talls" (zero-downtime) exigeix tres coses que ja tenim i una de nova:

  • Que les rèpliques noves no rebin trànsit fins que estiguin llestes: readinessProbe a /health/ready (05-02).
  • Que les rèpliques velles acabin el que tenen entre mans abans de morir: SIGTERM → /health/ready a 503 → servidor.close() (04-02, 05-01) dins de terminationGracePeriodSeconds: 30 (05-02).
  • Que hi hagi més d'una rèplica, perquè sempre en quedi alguna servint.
  • I la nova: que la versió vella i la nova puguin conviure durant segons, minuts o dies, parlant amb els mateixos clients, la mateixa base de dades i les mateixes cues (apartats 3 i 9).

Sense la quarta condició, cap estratègia no funciona: no hi ha manera de canviar de versió "instantàniament" en un sistema distribuït; sempre hi ha una finestra amb totes dues.

  1. Recreate davant de RollingUpdate

Recreate atura totes les rèpliques velles i després arrenca les noves: hi ha un forat de segons sense servei. Només té sentit quan dues versions no poden conviure (un canvi d'esquema destructiu, un consumidor exclusiu d'una cua) i s'accepta el tall. RollingUpdate, el valor per defecte d'un Deployment, substitueix rèpliques a poc a poc:

# k8s/servei-comandes/base/deployment.yaml (fragment afegit a spec)
spec:
  replicas: 3
  strategy:
    type: RollingUpdate               # (Recreate seria l'alternativa)
    rollingUpdate:
      maxSurge: 1                     # quants pods PER SOBRE de replicas hi pot haver durant el desplegament (nombre o %)
      maxUnavailable: 0               # quants pods per sota de replicas es toleren: 0 = mai menys de 3 llestos
  minReadySeconds: 10                 # un pod nou ha de portar 10 s llest abans de comptar com a disponible (filtra arrencades que moren de seguida)
  progressDeadlineSeconds: 300        # si en 5 min no progressa, el rollout es marca com a fallit (i rollout status retorna error al pipeline)

Amb replicas: 3, maxSurge: 1, maxUnavailable: 0, la seqüència és:

sequenceDiagram
    participant D as Deployment
    participant V1 as ReplicaSet v1 (3 pods)
    participant V2 as ReplicaSet v2
    participant S as Service (Endpoints)
    D->>V2: crear pod v2-a (ara 4 pods: 3+1 surge)
    V2->>S: v2-a passa readinessProbe → entra a Endpoints
    Note over S: 4 pods llestos: 3 v1 + 1 v2
    D->>V1: esborrar pod v1-a
    S-->>V1: v1-a surt d'Endpoints; rep SIGTERM
    V1->>V1: /health/ready 503, servidor.close(), acaba peticions en curs, exit
    D->>V2: crear pod v2-b … (es repeteix fins a 3 v2 i 0 v1)

Els quatre paràmetres es llegeixen així:

Paràmetre Efecte Valor de TechCorp Per què
maxSurge: 1 Necessita capacitat per a un pod extra 1 (o 25 % si hi ha moltes rèpliques) Desplegament ràpid sense duplicar recursos
maxUnavailable: 0 Mai per sota de les rèpliques desitjades 0 La capacitat no baixa durant el desplegament; amb replicas: 2 i maxUnavailable: 1 hi hauria moments amb un sol pod
minReadySeconds Evita que un pod que mor als 3 s compti com a èxit 10 Les fallades d'arrencada tardana (connexió al broker) es detecten abans de continuar
readinessProbe (05-02) Marca el ritme: no s'esborra un v1 fins que un v2 està llest /health/ready Sense ella, Kubernetes consideraria llest un pod tan bon punt arrenca el contenidor

La relació amb terminationGracePeriodSeconds: quan el Deployment esborra un pod v1, el kubelet li envia SIGTERM i, en paral·lel, el controlador d'Endpoints el treu del Service. Hi ha una cursa de mil·lisegons en què encara pot arribar una petició; per això /health/ready passa a 503 primer i servidor.close() continua atenent les connexions obertes. Un preStop: { exec: { command: ["sleep", "5"] } } al contenidor dona marge extra si s'observen errors en aquesta finestra.

I el rollback:

kubectl rollout history deploy/servei-comandes            # REVISION 3: image 1.0.1; REVISION 2: image 1.0.0 ...
kubectl rollout undo deploy/servei-comandes               # torna a la revisió anterior amb un altre RollingUpdate (mateixos maxSurge/maxUnavailable)
kubectl rollout undo deploy/servei-comandes --to-revision=2
kubectl rollout pause deploy/servei-comandes              # congela un rollout a mitges per investigar; resume per continuar

rollout undo és immediat i segur si la 1.0.0 pot conviure amb el que la 1.0.1 va deixar a la base de dades: apartat 3. Amb GitOps (05-03), el rollback canònic és revertir el commit de plataforma; rollout undo és la via d'emergència.

  1. Compatibilitat cap enrere durant el rolling: contractes i expand/contract

Durant el rolling (i durant hores o dies en canary), la 1.0.0 i la 1.0.1 de Comandes atenen peticions alhora, publiquen esdeveniments alhora i escriuen a la mateixa taula. Dues conseqüències:

  1. Contractes: l'API i els esdeveniments de la 1.0.1 han de ser compatibles cap enrere amb els consumidors actuals (03-06: afegir camps sí, treure o canviar tipus no; /v2/ per a la resta). Un client que parli amb la 1.0.0 en una petició i amb la 1.0.1 a la següent (el Service balanceja per connexió) no ha de notar diferències incompatibles.
  2. Esquema de base de dades: la migració de la 1.0.1 s'aplica abans que arrenquin els seus pods (el Job de 05-02) i mentre la 1.0.0 continua escrivint. Per tant la migració ha de ser compatible amb la 1.0.0.

El patró per aconseguir-ho és expand/contract: un canvi d'esquema es fa en dos (o tres) desplegaments, mai en un. Amb l'exemple de 03-06, afegir moneda a les línies de comanda:

Fase Migració Codi desplegat Compatible amb
Expand (v1.1.0) ALTER TABLE linies_comanda ADD COLUMN moneda CHAR(3) NOT NULL DEFAULT 'EUR'; v1.1.0 escriu moneda; v1.0.x continua sense conèixer la columna Totes dues: el DEFAULT cobreix les files que insereix v1.0.x
Migrar dades UPDATE linies_comanda SET moneda = 'EUR' WHERE moneda IS NULL (aquí no cal gràcies al DEFAULT; en altres casos, per lots) v1.1.0 a totes les rèpliques v1.1.0
Contract (v1.2.0) ALTER TABLE linies_comanda ALTER COLUMN moneda DROP DEFAULT; (o treure la columna vella si era un reanomenament) v1.2.0 exigeix moneda sempre Només v1.1.0+: per això només s'aplica quan v1.0.x ja no existeix
-- migracions/005-linies-moneda-expand.sql   (desplegament v1.1.0; migrar.js de 04-04 l'aplica al Job)
ALTER TABLE linies_comanda ADD COLUMN moneda CHAR(3) NOT NULL DEFAULT 'EUR';
-- migracions/006-linies-moneda-contract.sql (desplegament v1.2.0, dies després, quan cap pod v1.0.x no pot tornar)
ALTER TABLE linies_comanda ALTER COLUMN moneda DROP DEFAULT;

El que no es fa mai en un sol pas: reanomenar una columna (preu_unitaripreu), canviar-ne el tipus, esborrar-la, o afegir un NOT NULL sense DEFAULT. Qualsevol d'aquestes trenca la versió vella a l'instant en què el Job acaba, abans que la nova estigui llesta, i fa impossible el rollout undo. Regla pràctica del Luis: "una migració només pot afegir; treure és un altre desplegament, i s'ha de poder desfer el desplegament sense desfer la migració".

  1. Blue-green

Dos entorns complets i idèntics, blue (actiu) i green (nou). Es desplega la versió nova a green, es prova amb trànsit intern, i es canvia l'encaminament de cop. Si alguna cosa falla, es torna a blue en un segon. A Kubernetes, dos Deployment i un Service el selector del qual decideix quin rep trànsit:

# Dos Deployments idèntics tret del nom, l'etiqueta version i la imatge (base Kustomize amb nameSuffix i patch)
apiVersion: apps/v1
kind: Deployment
metadata: { name: servei-comandes-blue, namespace: techcorp }
spec:
  replicas: 3
  selector: { matchLabels: { app: servei-comandes, version: blue } }
  template:
    metadata: { labels: { app: servei-comandes, version: blue } }
    spec: { containers: [ { name: servei-comandes, image: ghcr.io/techcorp/servei-comandes:1.0.0 } ] }   # la resta igual que 05-02
---
apiVersion: apps/v1
kind: Deployment
metadata: { name: servei-comandes-green, namespace: techcorp }
spec:
  replicas: 3
  selector: { matchLabels: { app: servei-comandes, version: green } }
  template:
    metadata: { labels: { app: servei-comandes, version: green } }
    spec: { containers: [ { name: servei-comandes, image: ghcr.io/techcorp/servei-comandes:1.0.1 } ] }
---
apiVersion: v1
kind: Service
metadata: { name: servei-comandes, namespace: techcorp }
spec:
  selector:
    app: servei-comandes
    version: blue                     # ← l'interruptor: canviar a green mou TOT el trànsit
  ports: [ { name: http, port: 3002, targetPort: http } ]
---
apiVersion: v1                        # Service auxiliar per provar green abans del canvi (port-forward, E2E interna)
kind: Service
metadata: { name: servei-comandes-green, namespace: techcorp }
spec:
  selector: { app: servei-comandes, version: green }
  ports: [ { name: http, port: 3002, targetPort: http } ]
kubectl apply -f green.yaml && kubectl rollout status deploy/servei-comandes-green
kubectl port-forward svc/servei-comandes-green 3002:3002 &       # proves sobre green sense trànsit real:
curl -s localhost:3002/health/ready && GATEWAY_URL=http://localhost:3002 npm run test:e2e
kubectl patch svc servei-comandes -p '{"spec":{"selector":{"app":"servei-comandes","version":"green"}}}'   # el canvi
# problema? tornada enrere igual de ràpida:
kubectl patch svc servei-comandes -p '{"spec":{"selector":{"app":"servei-comandes","version":"blue"}}}'

Avantatges: canvi i rollback instantanis i sense versions barrejades al Service (tot i que les peticions en curs a blue acaben a blue). Inconvenients: doble cost mentre conviuen, i les mateixes exigències de compatibilitat de dades de l'apartat 3, perquè green ja va escriure a la base de dades abans de tornar a blue. Quan el que s'exposa és un Ingress, l'interruptor pot ser el backend.service.name de l'Ingress en lloc del selector.

  1. Canary

Un canary envia un percentatge petit de trànsit a la versió nova, se n'observen les mètriques (aquí n'hi ha prou amb dues: taxa d'errors 5xx i latència; el detall de què mesurar i com alertar és de 06-01 i 06-05), i si són iguals o millors que les de la versió actual s'augmenta el percentatge fins al 100 %; si no, es retira sense que la majoria d'usuaris ho hagi notat. Tres maneres de fer-ho, de més bàsica a més precisa:

(a) Per rèpliques. El Service selecciona per app: servei-comandes sense mirar version (com a 05-02); un segon Deployment amb una rèplica de la versió nova entra al mateix grup:

apiVersion: apps/v1
kind: Deployment
metadata: { name: servei-comandes-canary, namespace: techcorp }
spec:
  replicas: 1                                        # 1 canary de 10 pods en total (9 estables) ≈ 10 % del trànsit
  selector: { matchLabels: { app: servei-comandes, version: v2 } }
  template:
    metadata: { labels: { app: servei-comandes, version: v2 } }
    spec: { containers: [ { name: servei-comandes, image: ghcr.io/techcorp/servei-comandes:1.1.0 } ] }

Senzill i sense cap peça nova; però el percentatge és aproximat (kube-proxy reparteix per connexió, no per petició) i la granularitat és "una rèplica": amb 3 rèpliques estables, el canary mínim és el 25 %.

(b) Per pes a l'Ingress NGINX. Per al que entra per l'Ingress (a TechCorp, el gateway; o un servei si s'exposés directament), un segon Ingress amb anotacions canary:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: gateway-canary
  namespace: techcorp
  annotations:
    nginx.ingress.kubernetes.io/canary: "true"
    nginx.ingress.kubernetes.io/canary-weight: "10"                # 10 % de les peticions a gateway-v2
    # alternativa/addició: canary per capçalera, com a l'exercici 1 de 03-04
    nginx.ingress.kubernetes.io/canary-by-header: "X-Canary"
    nginx.ingress.kubernetes.io/canary-by-header-value: "comandes" # X-Canary: comandes → sempre a la nova; "never" → mai
spec:
  ingressClassName: nginx
  rules:
    - host: api.techcorp.example
      http:
        paths:
          - path: /
            pathType: Prefix
            backend: { service: { name: gateway-v2, port: { number: 8080 } } }

Pes exacte per petició i canary per capçalera (X-Canary), que permet a l'equip provar la versió nova en producció abans d'obrir el percentatge: la mateixa idea que el router del gateway Express de 03-04, ara declarativa. Limitació: només al perímetre; entre serveis interns l'Ingress no hi intervé.

(c) Per pes entre serveis interns. Que el 10 % de les crides del gateway a servei-comandes vagin a v2, amb pes exacte i sense importar quantes rèpliques hi ha, és el que dona un service mesh amb VirtualService (05-05, on tanquem aquest fil).

En qualsevol de les tres, la part difícil no és el YAML sinó la decisió d'avançar: comparar errors i latència de v2 amb v1 durant N minuts, amb prou trànsit perquè sigui significatiu (a les 4 de la matinada un 10 % de gairebé res no diu res). Al principi TechCorp ho farà a mà mirant Grafana (06-01); l'automatització és de l'apartat 10.

  1. Feature flags: desplegament no és release

Les estratègies anteriors controlen quin codi corre. Un feature flag (04-03: PAGAMENT_NOU_PROVEIDOR, CATALEG_REMOT) controla quin codi s'executa d'entre el que corre. Amb flags, el desplegament de la 1.1.0 es pot fer amb la funcionalitat apagada (dark launch): el codi nou arriba a producció amb un rolling normal, sense risc, i la release (encendre-la per a l'1 %, el 10 %, tothom) és un canvi de configuració, en segons, sense desplegar i sense rollout undo. Comparat amb el canary:

Canary Feature flag
Granularitat Per petició o per rèplica Per usuari, client, país... (el que el codi decideixi)
Requereix Dues versions desplegades Una versió amb dos camins
Revertir Redirigir trànsit Canviar un valor
Cost Infraestructura Complexitat al codi; flags que després cal retirar
Combinables Sí: canary per al risc tècnic del desplegament, flag per al risc funcional

Regla: un flag té data de caducitat; quan la funcionalitat és l'única, s'esborra l'if.

  1. Taula comparativa

Estratègia Tall Cost extra Risc Rollback Complexitat Quan
Recreate Sí (segons-minuts) Cap Alt Redesplegar (lent) Mínima Canvis no compatibles; entorns de dev; tasques
Rolling No +maxSurge pods Mitjà: si la nova falla, ja rep trànsit proporcional rollout undo (segons-minuts) Baixa: per defecte a Kubernetes Per defecte per a tot
Blue-green No Doble mentre conviuen Baix-mitjà: canvi total de cop Instantani (selector) Mitjana: dos Deployments, dos Services, coordinació Canvis grans que es volen provar a prod abans; gateway
Canary No +1 rèplica o similar Baix: exposició limitada i controlada Ràpid (retirar el canary) Mitjana-alta: pesos, mètriques, decisió Serveis crítics amb prou trànsit per mesurar

  1. L'estratègia de TechCorp per servei

Servei Estratègia Motiu
servei-cataleg, servei-inventari, servei-notificacions, servei-clients Rolling (maxSurge: 1, maxUnavailable: 0) Canvis freqüents i petits; el readinessProbe i l'aturada ordenada n'hi ha prou; flags per al que és funcional
servei-comandes, servei-pagaments Canary (rèpliques al principi; pes exacte quan hi hagi mesh, 05-05) És on hi ha els diners: un 10 % durant 30 minuts amb la taxa d'errors vigilada abans del 100 %; promoció a prod amb revisió (05-03)
gateway Blue-green (Ingress apuntant a gateway-blue/gateway-green) Únic punt d'entrada; una fallada afecta tot; el canvi i la tornada enrere han de ser instantanis i provables abans
Job de migracions, CronJob Recreate (no aplica: no serveixen trànsit)

Totes comparteixen els quatre requisits de l'apartat 1 i la disciplina expand/contract.

  1. Desplegar canvis en consumidors d'esdeveniments

Amb un rolling de Comandes, durant una estona hi ha pods v1 i v2 consumint la mateixa cua comandes.saga (04-04). RabbitMQ reparteix els missatges entre tots els consumidors connectats, sense saber de versions. Conseqüències i regles:

  • Tots dos han d'entendre els mateixos esdeveniments: la tolerància a camps nous i el versionat d'esdeveniments de 03-06 no són teoria, són el que fa possible que un estoc.reservat publicat per l'Inventari nou el processi un Comandes vell.
  • Redistribució en morir un pod: quan v1-a rep SIGTERM a mig processar un pagament.confirmat, el missatge no confirmat (ack pendent) torna a la cua i el rep un altre pod, potser v2. La idempotència de processarUnCop (04-04, taula esdeveniments_processats) és el que evita confirmar dues vegades la comanda. L'aturada ordenada del consumidor ha de deixar de prendre missatges (channel.cancel), acabar els que té, i només llavors tancar; el prefetch baix (10) acota quants en queden a mitges.
  • Si v2 publica un esdeveniment nou (comanda.confirmada amb un camp més), els consumidors d'altres serveis en la seva versió actual l'han de tolerar: expand/contract també s'aplica als esdeveniments.
  • Canvi de topologia (nova cua, nou binding): es declara de manera idempotent en arrencar (03-02) i abans de publicar-hi; mai no s'esborra un binding que la versió anterior encara fa servir.

Un consumidor d'una cua no pot fer-se blue-green "pur" (green ja consumeix tan bon punt connecta): o es desplega green sense arrencar el consumidor (un flag CONSUMIDOR_ACTIU) o s'accepta que consumeixi des del començament, que és el que fa TechCorp perquè el consum ja és idempotent.

  1. Rollback, forward-fix i automatització progressiva

  • Rollback: kubectl rollout undo, canvi de selector, retirar el canary o revertir el commit de plataforma. És l'opció quan la fallada és evident i la versió anterior és compatible amb l'estat actual (per això les migracions només afegeixen). Objectiu: minuts, i és el que abaixa el MTTR de 05-03.
  • Forward-fix: desplegar una versió nova que corregeix, en lloc de tornar enrere. Necessari quan la tornada enrere és impossible (una fase contract ja aplicada, dades ja escrites en un format nou) i acceptable quan el pipeline triga menys de 15 minuts: és més fàcil justificar el forward-fix com millor és el CI/CD.
  • Automatització progressiva: Argo Rollouts (recurs Rollout que substitueix el Deployment, amb strategy: canary: steps: [setWeight: 10, pause: 10m, analysis: ...]) o Flagger (opera sobre el Deployment normal i crea el canary sol) consulten Prometheus, avancen el pes si les mètriques compleixen i reverteixen si no. Tots dos necessiten un mecanisme de pes (Ingress NGINX, Istio, Linkerd) i les mètriques de 06-01. És el destí natural del canary manual de Comandes i Pagaments, quan l'equip de Plataforma tingui el mesh o l'Ingress amb pesos i les mètriques llestes.

Errors Comuns i Consells

  • maxUnavailable per defecte (25 %) amb dues rèpliques: durant el desplegament en queda una; i amb replicas: 1 el rolling és un recreate amb un altre nom. maxUnavailable: 0 i com a mínim dues rèpliques a qualsevol servei que rebi trànsit.
  • Migració destructiva al mateix desplegament que el codi: el Job reanomena la columna, els pods v1 comencen a fallar abans que v2 estigui llesta, i rollout undo no arregla res. Expand/contract sempre.
  • Canary sense mètriques ni trànsit: "l'hem tingut 10 minuts i no ha passat res" a les 3 de la matinada. Definir què es mira, quant de temps i quant trànsit mínim abans de començar.
  • Blue-green amb estat al pod (sessions, memòria cau local): green arrenca "fred". Els serveis de TechCorp són stateless precisament per això.
  • Oblidar el readinessProbe o fer-la trivial (/health/live a totes dues): Kubernetes esborra pods vells tan bon punt els nous "arrenquen", no quan poden atendre.
  • Flags eterns: cada if (flags.estaActiu(...)) és una branca per provar. Data de retirada a la targeta que el crea.
  • Consell: assaja el rollback a staging com a part del pipeline (rollout undo + E2E) com a mínim una vegada per versió major; un rollback que no s'ha provat mai no és cap pla.

Exercicis

Exercici 1. servei-comandesreplicas: 2 i la configuració per defecte de RollingUpdate. El Luis observa que durant cada desplegament la latència p95 es duplica i hi ha alguns 502 al gateway. Explica les dues causes més probables amb el que s'ha vist i escriu el fragment d'spec que les corregeix.

Exercici 2. L'equip de Comandes vol reanomenar la columna preu_unitari de linies_comanda a preu (03-06 ho va deixar com a exemple de canvi incompatible). Dissenya la seqüència expand/contract completa: migracions (numerades a partir de 007-...), què escriu i llegeix el codi a cada versió (1.3.0, 1.4.0, 1.5.0) i en quin moment és segur aplicar cada pas.

Exercici 3. La Marta pregunta per què el gateway va en blue-green i no en canary "com Comandes, que és més modern". Redacta la resposta en cinc línies, i afegeix què caldria perquè el gateway pogués anar en canary amb seguretat.

Solucions

Solució 1. (1) Amb replicas: 2 i maxUnavailable: 25 % (arrodonit a 1 pod), en algun moment hi ha un sol pod atenent tot el trànsit: la latència es duplica. (2) Els 502 venen de la finestra entre "el pod surt d'Endpoints" i "el pod deixa d'acceptar": si el readinessProbe triga 5 s a fallar tres vegades (15 s) però el procés ja ha tancat, o si el gateway manté connexions keep-alive al pod que mor; l'aturada ordenada de 04-02 (503 immediat a /health/ready i servidor.close()) redueix la finestra, i un preStop amb sleep 5 la cobreix. Correcció:

spec:
  replicas: 3
  strategy: { type: RollingUpdate, rollingUpdate: { maxSurge: 1, maxUnavailable: 0 } }
  minReadySeconds: 10
  template:
    spec:
      containers:
        - name: servei-comandes
          lifecycle: { preStop: { exec: { command: ["sleep", "5"] } } }   # el kubelet espera aquesta ordre abans d'enviar SIGTERM

terminationGracePeriodSeconds: 30 continua sent suficient: 5 s de preStop + menys de 10 s d'aturada.

Solució 2. v1.3.0 (expand): 007-linies-preu-expand.sql: ALTER TABLE linies_comanda ADD COLUMN preu NUMERIC(10,2); (nullable); el codi escriu les dues columnes i continua llegint preu_unitari. Compatible amb v1.2.x (ignora preu, que admet NULL). Migració de dades (Job a part o dins de 007 si la taula és petita; per lots si no): UPDATE linies_comanda SET preu = preu_unitari WHERE preu IS NULL;. v1.4.0 (canvi de lectura): el codi llegeix preu i continua escrivint totes dues; es pot afegir ALTER COLUMN preu SET NOT NULL a 008-... un cop emplenada. Compatible amb v1.3.0 (que escriu totes dues). v1.5.0 (contract): 009-linies-preu-contract.sql: ALTER TABLE linies_comanda DROP COLUMN preu_unitari;; el codi ja no l'escriu. Només és segur quan cap rèplica anterior a v1.4.0 no pot tornar (dies després, un cop confirmat que no hi haurà rollout undo a v1.3.0). Tres desplegaments en lloc d'un, i cadascun es pot desfer.

Solució 3. El gateway és l'únic punt d'entrada: qualsevol fallada afecta el 100 % de les peticions de tots els serveis, i el seu canvi típic és d'encaminament o de llibreria, difícil de "provar amb el 10 %" perquè un error de configuració acostuma a ser tot o res, no estadístic. Blue-green permet desplegar green complet, executar l'E2E i proves de rutes contra gateway-green abans de rebre trànsit, i tornar a blue en un segon amb un canvi d'Ingress, sense dependre de mètriques. Per passar a canary amb seguretat caldria: pes exacte per petició (Ingress NGINX canary-weight, ja vist), mètriques RED per ruta del gateway (06-01) amb un llindar definit d'errors i latència, prou trànsit a la finestra d'anàlisi, i preferiblement l'automatització d'Argo Rollouts/Flagger per no dependre que algú miri Grafana. Fins llavors, blue-green és l'opció més segura per a aquesta peça.

Conclusió

Desplegar sense talls descansa en quatre pilars (readinessProbe, aturada ordenada dins de terminationGracePeriodSeconds, més d'una rèplica i convivència de versions) i es materialitza en quatre estratègies: recreate només quan la convivència és impossible; rolling com a valor per defecte (maxSurge: 1, maxUnavailable: 0, minReadySeconds, rollout undo); blue-green amb dos Deployment (-blue/-green) i el selector.version del Service (o l'Ingress) com a interruptor; i canary per rèpliques, per pes i capçalera X-Canary a l'Ingress NGINX, o per pes exacte entre serveis interns amb un mesh. La convivència imposa contractes compatibles (03-06) i migracions expand/contract (la columna moneda de linies_comanda en dos desplegaments), també per als consumidors d'esdeveniments que comparteixen cua durant el rolling (idempotència de 04-04). Els feature flags separen desplegament de release; la decisió de TechCorp és rolling per a Catàleg, Inventari, Notificacions i Clients, canary per a Comandes i Pagaments, blue-green per al gateway; i Argo Rollouts o Flagger automatitzaran el canary quan hi hagi mètriques i pesos exactes. Aquest pes exacte entre serveis interns, juntament amb timeouts, reintents i mTLS que avui cada servei resoldria al seu codi, és el que promet un service mesh; la lliçó següent examina què dona Istio (i Linkerd), què costa, i si TechCorp ja ho necessita.

Curs de Microserveis

Mòdul 1: Introducció als Microserveis

Mòdul 2: Disseny de Microserveis

Mòdul 3: Comunicació entre Microserveis

Mòdul 4: Implementació de Microserveis

Mòdul 5: Desplegament i Orquestració

Mòdul 6: Monitoratge i Manteniment

Mòdul 7: Seguretat en Microserveis

Mòdul 8: Casos d'Estudi i Exemples Pràctics

© Copyright 2026. Tots els drets reservats