Módulo 4: Alerting On Error Budget Burn Rate
1. Introducción: por qué un umbral estático no alcanza
Descripción
El Módulo 2 dejó SLO.md con una cifra incómoda: 4,23 de 43,2 minutos de presupuesto restantes, un mes que estuvo a un solo día malo más de agotar su error budget completo. El Módulo 3 conectó esa fórmula a datos reales —CloudWatch, Prometheus, logs, trazas—, pero ninguno de los dos módulos construyó todavía la pieza que convierte "el presupuesto está en riesgo" en "alguien se entera, ahora, de que está en riesgo". Ese es el trabajo de este módulo: alerting que dispara por la razón correcta —consumo acelerado del error budget, no un umbral arbitrario— con la lógica ejecutada de verdad y el contraste honesto contra el producto gestionado que AWS ya vende para resolver exactamente este mismo problema.
Conexión con el módulo
Este módulo no inventa datos nuevos. Reutiliza, sin cambiarlos, el dataset "mala semana" del Módulo 2, lección 7 (BAD_WEEK, la semana de un despliegue roto que consumió 43,4 veces el presupuesto mensual) y el escenario sano del Módulo 2, lección 4 (TRAFFIC_30_DAYS, el mes de referencia con 90,2% de presupuesto consumido pero sin ningún día catastrófico). Las ocho lecciones de este módulo toman esos dos escenarios ya conocidos y les hacen la misma pregunta, con tres motores distintos: ¿esto debería disparar una alerta, o no?
La analogía de este módulo: el tanque de combustible, dos formas de vigilarlo
Imagina dos formas de vigilar el tanque de combustible de un camión de Andes Cargo. La primera es una luz que se enciende cuando el tanque llega a, digamos, un cuarto lleno — un umbral estático: un número fijo, sin memoria de qué tan rápido se llegó ahí. La segunda es un medidor que no solo mira cuánto queda, sino qué tan rápido se está vaciando ahora mismo — si el tanque baja de la mitad a un cuarto en cinco minutos, algo está mal (una fuga), aunque el nivel absoluto todavía no cruce ningún umbral fijo; si baja al mismo ritmo lento de siempre, un cuarto lleno es simplemente "hora de cargar combustible pronto", no una emergencia.
La primera luz —el umbral estático— tiene dos fallas que cualquier conductor real reconocería de inmediato. Puede encenderse demasiado tarde: si el tanque tiene una fuga rápida, para cuando la luz de "un cuarto" se enciende, ya quedan minutos, no horas, para quedarse varado. Y puede encenderse con demasiada frecuencia sin necesidad real: un camión que hace rutas largas cruza el umbral de "un cuarto lleno" en cada viaje, todos los días, sin que eso sea nunca una emergencia — la luz se vuelve ruido que el conductor aprende a ignorar. Burn rate —el tema central de este módulo— es la segunda forma de vigilar: no cuánto queda, sino qué tan rápido se está gastando, comparado contra la velocidad que el plan de viaje permite.
El problema real de un umbral estático, con números de este mismo proyecto
Un umbral estático de alerting, aplicado al error budget de process-shipment-manifest, se vería más o menos así: "avisa si el SLI cae por debajo de 99,9% en cualquier momento". Suena razonable. No lo es, por dos razones que el propio dataset de este proyecto ya demuestra.
Demasiado tarde. El día 17 del dataset del Módulo 2 (TRAFFIC_30_DAYS) tuvo un burn rate diario de 13,27x —a un paso del umbral más urgente de Google SRE (14,4x)—. Un umbral estático evaluado sobre el SLI acumulado del mes completo ni siquiera lo habría notado: el SLI de todo el mes, incluyendo ese día, terminó en 99,9098% — por encima del 99,9% que el umbral estaría vigilando. Un solo día casi catastrófico, invisible para un umbral que solo mira el promedio acumulado.
Demasiado seguido, o nunca lo suficiente, dependiendo de la ventana. Si el umbral se evaluara sobre una ventana corta —por ejemplo, cada hora—, un pico breve y sin importancia (un cliente que sube un manifiesto malformado por error, corregido al minuto siguiente) dispararía una alerta que nadie necesita atender a las 3 AM. Si se evaluara sobre una ventana larga —el mes completo, como en el ejemplo anterior—, un incidente real y agudo, como los días 17-18, queda diluido dentro del promedio y nunca cruza el umbral. No hay una sola ventana que resuelva ambos problemas a la vez — y esa es, exactamente, la razón por la que existe el patrón que la lección 2 de este módulo formaliza.
UMBRAL ESTATICO SOBRE EL SLI ACUMULADO — LOS DOS FALLOS
Ventana LARGA (ej. el mes completo)
────────────────────────────────────────────────────
Dia 17: burn rate 13.27x ──┐
Dia 18: burn rate 6.85x ──┼── diluidos en el promedio
Resto del mes: casi 0x ──┘ mensual -> SLI = 99.91%
-> el umbral estatico NUNCA se cruza
Ventana CORTA (ej. cada hora)
────────────────────────────────────────────────────
Un pico breve, sin importancia real
-> cruza el umbral -> alerta -> nadie la necesita a las 3 AM
-> el equipo aprende a ignorarla ("alert fatigue")
El mapa de este módulo: las 8 lecciones
MODULO 4 — ALERTING SOBRE BURN RATE DEL ERROR BUDGET
de "un umbral no alcanza" a tres motores reales que disparan por la razon correcta
M4.1 Introduccion (esta leccion) el problema del umbral estatico
M4.2 Multi-window, multi-burn-rate el patron real de Google SRE, citado
M4.3 El evaluador de burn rate EJECUTADO: burn_rate_evaluator.py
M4.4 La regla real en Alertmanager EJECUTADO: Prometheus + Alertmanager
M4.5 Una alarma real de CloudWatch EJECUTADO (validate) + REPRESENTATIVO
M4.6 Lo que AWS ya automatiza REPRESENTATIVO, con fecha exacta
M4.7 Enrutando la alerta EJECUTADO (validate) + REPRESENTATIVO
M4.8 Proyecto: la politica de alerting de Andes Cargo EJECUTADO: simulacro forzado
| # | Lección | Qué construye |
|---|---|---|
| 1 | Introducción (esta) | Por qué un umbral estático dispara tarde o dispara de más |
| 2 | Burn rate multi-ventana, multi-tasa | El patrón exacto de sre.google/workbook/alerting-on-slos/, citado |
| 3 | Manos a la obra: el evaluador de burn rate | Ejecutado: scripts/burn_rate_evaluator.py, salida literal |
| 4 | Manos a la obra: la regla real en Alertmanager | Ejecutado: Prometheus + Alertmanager, corriendo de verdad |
| 5 | Manos a la obra: una alarma real de CloudWatch | Ejecutado (validate/plan) + representativo (apply/awslocal) |
| 6 | Lo que AWS ya automatiza | Representativo, con la fecha exacta verificada |
| 7 | Manos a la obra: enrutando la alerta | Ejecutado (validate) + representativo (PagerDuty/Opsgenie, nombrados) |
| 8 | Proyecto: la política de alerting de Andes Cargo | Ejecutado: simulacro forzado, mala semana dispara, normal no dispara |
El hilo de este módulo: dos escenarios fijos, tres motores
A diferencia del Módulo 2 —donde cada lección corría sobre un dataset ligeramente distinto— y del Módulo 3 —donde un único batch se leía con tres instrumentos—, este módulo tiene una estructura más precisa todavía: dos escenarios, ya conocidos, evaluados por tres motores distintos, cada uno construido sobre el anterior.
- Escenario "normal" — el mes de 30 días del Módulo 2, lección 4 (
TRAFFIC_30_DAYS): sano en conjunto, con margen ajustado pero sin ningún incidente que deba disparar una alerta seria. - Escenario "mala semana" — la semana de 7 días del Módulo 2, lección 7 (
BAD_WEEK): un despliegue roto que consumió 43,4 veces el presupuesto mensual completo en solo siete días.
Los tres motores de este módulo, en el orden en que se construyen:
- Python puro (lección 3) —
scripts/burn_rate_evaluator.py, el prototipo de la decisión de alerta, sin ninguna infraestructura detrás. - Prometheus + Alertmanager (lección 4) — la misma decisión, ahora evaluada de verdad contra métricas reales scrapeadas, con una alerta que de verdad se enruta a un receptor.
- CloudWatch Alarm (lección 5) — la misma decisión, esta vez expresada como infraestructura declarada en Terraform, sobre el Lambda real de Andes Cargo.
Los tres motores, sobre los mismos dos escenarios, deberían llegar a la misma conclusión: "mala semana" dispara, "normal" no dispara. La lección 8 de este módulo lo demuestra con un simulacro forzado, corriendo los tres motores uno al lado del otro.
Honestidad de este módulo: qué corre de verdad, qué queda representativo
| Herramienta | Estado en este módulo | Razón técnica exacta |
|---|---|---|
scripts/burn_rate_evaluator.py (lección 3) | Real | Python puro, sin dependencias externas — corrió con python3 para escribir esta lección, salida literal. |
Prometheus v3.13.2 + Alertmanager 0.33.1 (lección 4) | Real | docker compose up levantado de verdad, con una regla de alerta real evaluada contra un exportador Python real, enrutada a un receptor webhook real. |
terraform validate / terraform plan (lección 5, 7) | Real | Corridos de verdad contra el HCL de observability.tf — salida literal. |
terraform apply / awslocal cloudwatch (lección 5) | Representativo | Sin LOCALSTACK_AUTH_TOKEN exportado en este entorno de escritura, el contenedor de LocalStack no arranca — el mismo límite circunstancial de todo este ecosistema desde finops-and-cost-guardrails-guide. CloudWatch está confirmado en el plan Hobby; la salida representativa está reconstruida campo por campo a partir del HCL real, nunca inventada. |
| CloudWatch Application Signals SLO (lección 6) | Representativo, nombrado, no ejecutado | Depende de infraestructura tipo APM sin cobertura confirmada en LocalStack de ningún tier — se nombra por contraste, con su fecha de lanzamiento verificada. |
| PagerDuty / Opsgenie (lección 7) | Representativo, nombrado, no ejecutado | SaaS de pago, sin capa $0 equivalente al resto de este laboratorio. |
La regla que gobierna toda esta guía se mantiene sin excepción: si un comando aparece en una lección, corrió para escribirla. Lo que no corrió de verdad queda etiquetado en el momento exacto en que aparece, con la razón técnica precisa.
Errores comunes
Pensar que "burn rate" es solo un nombre más elegante para "porcentaje de presupuesto consumido" (de confundir dos preguntas distintas, ya resuelto en el Módulo 2 pero fácil de olvidar aquí). Qué pasa: alguien, al empezar este módulo, asume que burn rate es simplemente el consumed_pct que la calculadora del Módulo 2 ya imprime. Cómo detectarlo: si no puedes explicar por qué un mes con 90,2% de presupuesto consumido acumulado (Módulo 2, lección 4) tuvo, en un solo día, un burn rate de 13,27x. Cómo corregirlo: consumed_pct es un acumulado sobre toda la ventana; burn rate es una velocidad instantánea, medida sobre una ventana mucho más corta que la ventana del SLO — la distinción exacta que el Módulo 2, lección 6 ya estableció, y que este módulo convierte en una alerta real.
Esperar que este módulo construya una sola alerta "definitiva" (de subestimar por qué hay tres motores). Qué pasa: alguien llega a la lección 4 esperando que, una vez construida la regla de Alertmanager, las lecciones 5 y 7 sean redundantes. Cómo detectarlo: si tu pregunta al terminar la lección 4 es "¿para qué necesito CloudWatch si ya tengo Prometheus?". Cómo corregirlo: un sistema de producción real casi nunca depende de un solo motor de alerting — la lección 5 existe precisamente porque process-shipment-manifest corre en AWS, y una alarma nativa de CloudWatch es una segunda capa de defensa, no una redundancia inútil, con sus propias fortalezas y límites (la lección 6 nombra exactamente cuáles) frente a Prometheus/Alertmanager.
Asumir que "representativo" en la lección 5 significa que el recurso de Terraform no es real (de confundir la capa de infraestructura con la capa de ejecución contra LocalStack). Qué pasa: alguien lee "representativo" junto a awslocal cloudwatch describe-alarms y concluye que todo el bloque HCL de esa lección es hipotético. Cómo detectarlo: si tratas el HCL de observability.tf como pseudocódigo en vez de como el archivo real que terraform validate ya validó contra el esquema real del provider de AWS. Cómo corregirlo: terraform validate y terraform plan corrieron de verdad, contra el HCL real, con la versión real del provider (hashicorp/aws ~> 6.0) — lo representativo es únicamente el paso siguiente, apply contra un LocalStack que en este entorno específico no arranca por falta de token, exactamente el mismo límite ya documentado desde finops-and-cost-guardrails-guide.
Ejercicios
Ejercicio 1 — Explica, sin usar la palabra "burn rate", por qué un umbral estático evaluado sobre el SLI mensual acumulado no habría detectado el incidente de los días 17-18 del Módulo 2. Usa los números exactos: SLI del mes completo 99,9098%, umbral hipotético 99,9%.
Ver solución
El SLI del mes completo (99,9098%) queda, por muy poco, por encima del umbral hipotético de 99,9% — un umbral estático evaluado sobre el acumulado del mes nunca se cruza, así que nunca dispara ninguna alerta, sin importar qué tan mal estuvo un día específico dentro de ese mes. El promedio mensual absorbe y diluye un evento agudo de dos días (días 17-18) entre 28 días mayormente sanos — el mismo problema que la sección "demasiado tarde, o nunca" de esta lección describe: una ventana lo suficientemente larga para calcular un promedio estable es, casi por definición, demasiado larga para detectar un evento corto dentro de ella.
Ejercicio 2 — Predice, sin ejecutar nada todavía, qué motor de este módulo (Python, Prometheus/Alertmanager, o CloudWatch) sería el más simple de correr en la laptop de un ingeniero sin ninguna cuenta de AWS ni ningún contenedor Docker corriendo. Justifica tu respuesta con lo que ya sabes de cada herramienta.
Ver solución
scripts/burn_rate_evaluator.py (lección 3) — Python puro, sin ninguna dependencia externa, sin Docker, sin cuenta de AWS, sin ningún servicio corriendo en segundo plano. Prometheus/Alertmanager (lección 4) ya requiere Docker corriendo dos contenedores; CloudWatch (lección 5) requiere, como mínimo, LocalStack corriendo (o una cuenta real de AWS). Esta progresión —de la lógica más simple posible hacia la infraestructura real— es intencional: la lección 3 existe para que entiendas la decisión de alerta sin ninguna complejidad operativa alrededor, antes de ver esa misma decisión implementada dentro de dos sistemas reales de producción.
Ejercicio 3 — Un colega propone resolver el problema del umbral estático simplemente evaluándolo con más frecuencia (cada minuto en vez de cada hora). Explica, usando la analogía del tanque de combustible de esta lección, por qué eso no resuelve el problema de fondo.
Ver solución
Evaluar un umbral estático con más frecuencia cambia cuándo se evalúa, pero no cambia qué se evalúa — sigue siendo un nivel absoluto (¿el SLI está por debajo de X ahora mismo?), sin ninguna noción de velocidad. En la analogía del tanque: revisar el nivel del combustible cada minuto en vez de cada hora no le dice al conductor si el tanque se está vaciando rápido (una fuga) o lento (uso normal) — solo le dice el nivel actual, con más frecuencia. El problema de fondo —que un umbral de nivel no distingue una fuga de un uso normal en el mismo nivel— sigue exactamente igual, sin importar qué tan seguido se mire. Lo que sí resuelve el problema es medir la tasa de cambio (cuánto bajó el nivel en los últimos cinco minutos, comparado contra cuánto debería bajar en un uso normal) — exactamente la definición de burn rate que la lección 2 de este módulo formaliza.
Resumen y siguiente paso
Esta lección instaló el problema central que gobierna las siete lecciones que siguen: un umbral estático sobre el error budget dispara demasiado tarde (diluido en una ventana larga) o con demasiada frecuencia sin necesidad real (ruidoso en una ventana corta), y no hay una sola ventana que resuelva ambos problemas a la vez. Viste el mapa de las 8 lecciones, el hilo que las conecta —dos escenarios fijos ya conocidos (TRAFFIC_30_DAYS, BAD_WEEK), evaluados por tres motores construidos en progresión (Python, Prometheus/Alertmanager, CloudWatch)—, y la tabla completa de honestidad de este módulo.
Antes de avanzar deberías poder: explicar, con los números exactos del día 17 del Módulo 2, por qué un umbral estático sobre el SLI acumulado no lo habría detectado; nombrar los tres motores de este módulo en el orden en que se construyen; y explicar la diferencia entre evaluar un umbral con más frecuencia y medir una velocidad de consumo.
La lección 2 formaliza la solución: el patrón multi-window, multi-burn-rate exacto de Google SRE, citado directamente de la fuente, con el resultado ya conocido del Módulo 2, lección 6 como primer ejemplo trabajado.
Recursos
- Este mismo repositorio, Módulo 2, lecciones 4, 6 y 7 — los dos datasets fijos (
TRAFFIC_30_DAYS,BAD_WEEK) y la primera implementación de burn rate que este módulo extiende. - Google SRE Workbook — Alerting on SLOs — la fuente exacta del patrón multi-window, multi-burn-rate que la lección 2 de este módulo cita completa.
finops-and-cost-guardrails-guide, Módulo 5 — el precedente directo de una alarma real de CloudWatch en este ecosistema, y el mismo patrón de honestidad (validate/planreales,applyrepresentativo) que la lección 5 de este módulo sigue.