Ejercicio 1 — La decisión documentada
- El ADR (extracto): "Plataforma de TicketFlow: PaaS de contenedores (Cloud Run). Contexto: equipo de 1, 1 app monolito modular (53), ~50k reservas/mes. Razones: 0 nodos que parchear, autoscaling por uso (~45 €/mes vs ~90 € de un K8s mínimo), el pipeline del 41 despliega digests sin conocer la plataforma. Descartado K8s: el costo operativo (updates, RBAC, networking) no lo paga el producto hoy. Trigger de revisión: segundo servicio con redes complejas, o exigencia on-prem del 56".
- Los triggers reales (no moda): (1) un segundo sistema que debe hablar con el primero por red privada con políticas por servicio (el multi-servicio del 53): el PaaS simple se queda corto de networking; (2) multi-tenancy estricto con aislamiento de red por cliente (el 56): los Network Policies de K8s lo dan, el PaaS no; (3) el requerimiento on-prem/edge del cliente institucional (56): K8s portable es el estándar del despliegue. El más probable del roadmap: (1) — y llegaría con la saga de pagos del 32 separándose del monolito, no antes.
- Los 6 objetos y su traducción: Deployment (las réplicas del compose
--scalecon probes), Service (elports:del compose como balanceo interno), ConfigMap/Secret (el.envdel 27 desdoblado en no-secreto/secreto), HPA (el autoscaling que el PaaS da gratis), CronJob (el beat/31 por tarea), Ingress (el proxy del 39 como objeto declarado).
Ejercicio 2 — El cluster local
- El ciclo normal:
kind load docker-image+kubectl apply -f deploy/k8s/→kubectl get pods:
NAME READY STATUS RESTARTS AGE
ticketflow-web-7d4f9c-x2k8l 1/1 Running 0 12s
ticketflow-web-7d4f9c-b9m3p 1/1 Running 0 12s
ticketflow-web-7d4f9c-t4w7q 1/1 Running 0 12sEl matiz del local: la imagen vive en tu docker local — el kind load la mete al containerd del cluster (el pull del registry es la vía real del 41).
- La resurrección: kill del proceso gunicorn dentro del pod → el liveness falla en ≤10 s → kubelet reinicia el contenedor (el mismo pod,
RESTARTS 1) → Ready en ~15-20 s totales. El MTTR del pod (42) ahora lo ejecuta kubelet sin humano.
- El contraste clave: BD congelada → el readiness falla (el /ready toca la BD) → el pod sale de los Endpoints del Service (sin tráfico) PERO NO se reinicia (el liveness local sigue OK). Cuando la BD vuelve: el readiness pasa y el pod re-entra al balanceo solo. Sin la separación: el liveness tocando la BD reiniciaría TODOS los pods (cascada) justo cuando la BD vuelve — el thundering herd del §2 documentado por experimento.
Ejercicio 3 — El HPA y el CronJob
- El finding del HPA: pico de 400 VUs → el HPA escala de 2 a 7 pods en ~25-40 s (metrics-server + decisión + pull + readiness): el usuario del pico VE la latencia del scale-out (los 2 pods iniciales absorben 400 VUs durante medio minuto: el p95 del pico sube antes de bajar). La cura del 55: minReplicas que cubra el pico previsible (la preventa anunciada se escala ANTES — el HPA reacciona, el operador anticipa).
- La aritmética con HPA: 10 pods × pool 10 = 100 + worker 8 + beat 2 = 110 > 100 del gestionado. El ajuste razonable: pool max_size 6 (total 70) + maxReplicas 8 (56+10=66: margen 34%) — o PgBouncer del 42 si el negocio exige maxReplicas alto. La regla: el HPA maxReplicas es parte del presupuesto de conexiones — sin esa línea en la tabla del 39, el scale-out es un DoS propio.
- El CronJob con el lock: dos
kubectl create job --from=cronjob/expirar-reservassolapados → el log del segundo:run-lock expirar ocupado: skip(la 31) y termina 0 en 2 s. La lógica estable (28) hace seguro el transporte nuevo — el K8s no necesita "asegurarse" de nada: el lock vive en el servicio.
Ejercicio 4 — La BD fuera
- La advertencia documentada: Postgres en Deployment + emptyDir →
kubectl delete pod→ los datos se van con el pod (emptyDir vive y muere con él): la BD arranca VACÍA. El hallazgo como advertencia del runbook: "un Deployment de BD sin PV no es una BD, es una caché de arranque con 500 usuarios dentro".
- La versión con PV: los datos sobreviven al restart del pod (el PV persiste). Aun así, las 3 razones del NO en prod: (1) el failover es artesanal (la replicación de PG a mano vs el failover automático del gestionado con PITR); (2) los backups: nadie prueba el restore del PV (42: el drill es del proveedor); (3) el drain/mantenimiento del nodo puede patear el StatefulSet en el peor momento (el evacuado del nodo con la BD = el incidente del 47 con pasos de más).
- La tabla final de conexiones:
| Fuente | Conexiones |
|---|---|
| web (HPA máx 8 × pool 6) | 48 |
| worker Celery | 8 |
| beat | 2 |
| margen operativo (20%) | ~15 |
| Total vs gestionado (100) | 73 |
Ejercicio 5 — El despliegue completo
- El rolling:
maxSurge 1, maxUnavailable 0→ un pod nuevo sube, pasa readiness, entra al Service; uno viejo se drena y muere; ×3. Tiempo total: ~45 s. Los viejos conviven con los nuevos durante el rollout (el convivio que el expand-contract del 11 permite).
- El rollback medido: versión rota (healthcheck 500) → los pods nuevos NUNCA pasan readiness → el rollout se atasca (el Service jamás les manda tráfico: cero 5xx en el mal rollout — el readiness ES el guard) →
kubectl rollout undo→ 20 s a la versión buena. El contraste con el blue-green del 41: K8s protege MEJOR al usuario durante el despliegue roto (el tráfico jamás cambia), pero el rollback del blue-green es más atómico post-switch. La respuesta del equipo: rolling para el día a día, y el blue-green del 41 cuando el release lo exija.
- Los YAML en el repo (
deploy/k8s/): el README del directorio con elkubectl apply -f deploy/k8s/y el principio del 44: los manifiestos se PR-ean como el código — elkubectl editde las 3 a.m. es la deriva que el IaC de la 44 declara ilegal (elterraform plan/kubectl diffla caza al día siguiente).
Resumen del profesor
- La plataforma se elige por el costo que el producto paga: PaaS hoy, K8s cuando el networking/aislamiento lo exija — con el trigger escrito en el ADR.
- readiness (¿recibo tráfico?) ≠ liveness (¿estoy vivo?): la BD congelada debe sacar el pod del balanceo, no reiniciarlo en cascada.
- El HPA maxReplicas entra en la aritmética de conexiones del 39; la BD vive gestionada fuera del cluster; los YAML se revisan en PR.