Ejercicio 1 — El pipeline
- El paralelismo:
lint+unit+integration+buildcorren EN PARALELO al abrir el PR;e2eespera abuild(necesita la imagen);check-deploycorre en paralelo con los test (no depende del código ejecutándose, solo del settings). La ruta crítica: e2e (~4 min con imagen cacheada; sin cache, el build de 90 s + e2e). El pipeline total del PR: ~6-8 min en frío, ~4-5 min en caliente — el número que decide si el equipo pushea varias veces al día o cada dos horas.
- Los caches (segunda corrida):
lint: 31 s → 9 s (pip cache)
unit: 28 s → 11 s (pip cache + .pytest_cache)
integration: 3m12 → 2m40 (services ya pulleulos)
build: 88 s → 14 s (cache-from registry: las capas de deps reutilizadas)
e2e: 4m10 → 3m55 (imagen del cache, el E2E no se cachea: corre SIEMPRE)- El efecto del flaky: 5 PRs × 1/5 de fallo = ~64% de probabilidad de al menos un rojo en el día — la media observada: 3 de 5 PRs necesitaron 1 re-run, 1 necesitó 2. El costo invisible: la confianza — el tercer re-run ya nadie lo mira. La política del proyecto: re-run manual (jamás automático en el gate), issue inmediato, cuarentena con caducidad (35).
Ejercicio 2 — El digest
- La cadena:
docker pushdevuelvesha256:9f2c...; el job lo exporta (::set-output) y el deploy lo consume.docker pull ticketflow@sha256:9f2c...→docker inspectmuestra el mismo digest: la identidad del artefacto es el digest, no el tag (el tag es un alias mutable; el digest, inmutable).
- El scan gate: la base vieja pinneada → trivy
--exit-code 1 --severity CRITICAL→ job ROJO con el reporte de la CVE. Las MEDIUM: al reporte (artifact del job), no al gate — el triage del 40: el CVSS alto sin contexto bloquea el deploy de cosas importantes por ruido; el CRITICAL explotable, jamás pasa.
- El comando del audit: 1 comando:
docker inspect --format '{{index .RepoDigests 0}}' prod-app | xargs -I{} docker image inspect {} --format '{{index .Config.Labels "org.opencontainers.image.revision"}}'Con el label org.opencontainers.image.revision horneado en el build (OCI label estándar): digest → sha del commit. Un comando, cadena completa — la auditoría del 47 ("¿qué código corrió cuando el cliente llamó?") es una línea.
Ejercicio 3 — El despliegue
- El log del switch:
10:14:02 deploy.sh green → green up (imagen a1b2c3d)
10:14:18 wait_healthy green → /healthz/ OK ×3
10:14:22 switch_traffic.sh → nginx upstream → green
10:14:23 smoke.sh: /events 200, /reservations 201, /healthz 200 → GREEN CONFIRMED
0 respuestas 500 en el switch (el nginx drena las conexiones del blue por keepalive)- El rollback medido: el smoke rojo →
rollback.sh→ switch de vuelta a blue → tráfico restablecido en 11 s. El despliegue sin rollback probado es una apuesta: el ejercicio cuesta 20 minutos y compra la tranquilidad de TODOS los futuros deploys.
- La decisión: blue-green (el canary exige el enrutado por porcentaje + métricas por versión que el nginx local no da; el costo del 2× infra en TicketFlow es ~30 €/mes — el rollback de 11 s lo paga). El párrafo: "Blue-green: rollback <15 s probado, infra 2× asumida. Si pasamos a canary: el rollback automático lo dispara
http_5xx_rate > 2%durante 2 min op95 > 2× línea base(46), con el alarm de canary como dueño".
Ejercicio 4 — Las migraciones
- El guard del release: el job corre
python manage.py makemigrations --check(nadie olvida generarlas) + el linter del 11 (django-migration-linter) con--fail-on-danger: una migración que renombra columna sin el expand-contract red-paintea el job con el remedio en el mensaje.
- El estado intermedio demostrado: el test del 11 corre la suite contra el esquema EXPANDIDO con el CÓDIGO VIEJO (el checkout de la app blue): verde — la columna nueva es nullable/sin default que rompa, el código viejo ni la ve. Esa es la prueba de que el expand-contract permite el convivio blue/green.
- Los JAMÁS documentados: (a) app nueva sin esquema:
column "org_id" does not existen el primer request → 500 en cadena (~14 en 3 s hasta el rollback) — el migrate ANTES del deploy es la ley; (b)migrate && runserveren el contenedor: el rollback re-despliega blue... que RE-CORRE el migrate del arranque (de nuevo, con lock, con lentitud) y, peor, el rollback no puede "no migrar": la migración ya está aplicada y el código blue con el esquema nuevo convive — el rollback deja de ser atómico. El runbook del 47 lleva ambas advertencias.
Ejercicio 5 — El círculo
- La anotación: el webhook del pipeline llama al log (45):
{"event": "deployment_completed", "commit": "a1b2c3d", "digest": "sha256:9f2c...", "actor": "buffy", "ts": "..."}La gráfica del p95 con la línea vertical: el "¿qué cambió a las 14:20?" del 47 se responde con hover.
- Las 4 DORA de los últimos 10 deploys del proyecto: lead time PR→prod: 1.8 h (sana); frecuencia: 2.1/día (sana); tasa de fallo del deploy: 20% (2 de 10: el smoke lo cazó — el fallo del deploy DETECTADO es casi gratis, la vergüenza sería el 502 en prod); MTTR: 14 min (el rollback de 11 s + el diagnóstico). La vergüenza del equipo de 1: los deploys de viernes por la tarde siguen ocurriendo — el documento la prohíbe y el calendario no la cumple.
- El
docs/ci-cd.md(esqueleto de la página): ASCII del pipeline con gates → estrategia (blue-green, razón: rollback <15 s) → orden canónico del release (migrate expand → deploy → smoke → switch → contract) → política de rollback → los dos JAMÁS del ejercicio 4 → DORA mensual. Una página que el tú de las 3 a.m. lee de arriba a abajo sin escalas.
Resumen del profesor
- El gate del merge es verde y determinista; lo lento corre después: el pipeline protege el ciclo de feedback tanto como el código.
- El digest es el artefacto del release: commit→imagen→entorno auditable en un comando; el deploy despliega, jamás reconstruye.
- Blue-green + expand-contract + rollback probado: la pareja que convierte el deploy en un evento aburrido — y aburrido es la meta.