SRE: SLIs, SLOs y Automatizaciones Que Realmente Ayudan
Apoya este blog
Si te resulta util este contenido, considera apoyar el blog.
Introducción
En este artículo vamos a explorar el lado práctico de la Ingeniería de Confiabilidad de Sitio (SRE), específicamente cómo definir Indicadores de Nivel de Servicio (SLIs) y Objetivos de Nivel de Servicio (SLOs) como código, desplegarlos usando ArgoCD, y aprovechar servidores MCP y automatizaciones para hacer todo el proceso menos doloroso.
Si venís haciendo operaciones o ingeniería de plataformas hace un tiempo, probablemente ya sabés que el monitoreo solo no alcanza. Tener un dashboard lleno de luces verdes no significa que tus usuarios estén contentos. Los SLIs y SLOs te dan un marco para medir lo que realmente importa y tomar decisiones informadas sobre confiabilidad vs. velocidad de entrega de features.
Vamos al tema.
¿Qué es SRE?
Site Reliability Engineering es una disciplina que aplica prácticas de ingeniería de software a problemas de operaciones. Google popularizó el concepto, pero la idea central es simple: tratá tu infraestructura y procesos operativos como código, medí lo que importa, y usá presupuestos de error para balancear confiabilidad con la velocidad de entrega de nuevas funcionalidades.
Los componentes clave son:
- SLIs (Indicadores de Nivel de Servicio): Métricas que miden la calidad de tu servicio desde la perspectiva del usuario
- SLOs (Objetivos de Nivel de Servicio): Objetivos que definís para tus SLIs (ej: “99.9% de las requests deben ser exitosas”)
- Presupuestos de Error: La cantidad aceptable de falta de confiabilidad (100% - objetivo del SLO)
- SLAs (Acuerdos de Nivel de Servicio): Contratos comerciales basados en SLOs (no nos vamos a enfocar en estos acá)
Entendiendo los SLIs
Un SLI es una medida cuantitativa cuidadosamente definida de algún aspecto del nivel de servicio proporcionado. Los SLIs más comunes son:
- Disponibilidad: La proporción de requests que son exitosas
- Latencia: La proporción de requests que son más rápidas que un umbral
- Calidad: La proporción de respuestas que no están degradadas
Lo importante acá es la parte de “proporción”. Los SLIs se expresan como ratios:
SLI = eventos_buenos / eventos_totales
Por ejemplo, para un servicio HTTP:
# SLI de Disponibilidad
disponibilidad = (requests_totales - errores_5xx) / requests_totales
# SLI de Latencia
latencia = requests_mas_rapidas_que_300ms / requests_totales
Esto es mucho más útil que métricas crudas porque refleja directamente la experiencia del usuario. Un pico de errores que dura 5 segundos es muy diferente de uno que dura 5 minutos, y el ratio captura esa diferencia sobre una ventana de tiempo.
Entendiendo los SLOs
Un SLO es el valor objetivo para un SLI sobre una ventana de tiempo específica. Por ejemplo:
- “99.9% de las requests HTTP deben devolver una respuesta sin error en una ventana móvil de 30 días”
- “99% de las requests deben completarse en menos de 300ms en una ventana móvil de 30 días”
El SLO te da un presupuesto de error. Si tu SLO es 99.9%, tu presupuesto de error es 0.1%. En 30 días, eso significa que podés permitirte aproximadamente 43 minutos de tiempo de inactividad total. Esto es increíblemente poderoso porque convierte la confiabilidad en un recurso medible que podés gastar. ¿Querés hacer un despliegue riesgoso? Revisá tu presupuesto de error primero.
Llevando los SLIs a código con Prometheus
Ahora vamos a lo práctico. La forma más común de implementar SLIs es con métricas de Prometheus. Si estás corriendo cargas de trabajo en Kubernetes, probablemente ya tenés Prometheus o un sistema compatible recolectando métricas.
Para un servicio web típico, querés exponer un histograma que rastree la duración de las requests y el estado:
# Si tu aplicación usa el cliente de Prometheus, exponé algo como:
# histogram: http_request_duration_seconds (con labels: method, path, status)
# counter: http_requests_total (con labels: method, path, status)
# Para nuestra app Phoenix/Elixir, usamos phoenix_telemetry y peep para exponer estas métricas.
# Pero el concepto aplica a cualquier lenguaje.
Con esas métricas en Prometheus, podés definir recording rules que calculen los ratios del SLI. Acá hay un ejemplo de reglas de Prometheus para un SLI de disponibilidad HTTP:
# prometheus-rules.yaml
apiVersion: monitoring.coreos.com/v1
kind: PrometheusRule
metadata:
name: sli-availability
namespace: monitoring
spec:
groups:
- name: sli.availability
interval: 30s
rules:
# Tasa de requests totales en ventana de 5m
- record: sli:http_requests:rate5m
expr: sum(rate(http_requests_total[5m]))
# Tasa de requests con error en ventana de 5m (respuestas 5xx)
- record: sli:http_errors:rate5m
expr: sum(rate(http_requests_total{status=~"5.."}[5m]))
# SLI de disponibilidad (ratio de requests exitosas)
- record: sli:availability:ratio_rate5m
expr: |
1 - (sli:http_errors:rate5m / sli:http_requests:rate5m)
Y para un SLI de latencia:
# prometheus-rules-latency.yaml
apiVersion: monitoring.coreos.com/v1
kind: PrometheusRule
metadata:
name: sli-latency
namespace: monitoring
spec:
groups:
- name: sli.latency
interval: 30s
rules:
# Requests más rápidas que 300ms
- record: sli:http_request_duration:rate5m
expr: sum(rate(http_request_duration_seconds_bucket{le="0.3"}[5m]))
# Todas las requests
- record: sli:http_request_duration_total:rate5m
expr: sum(rate(http_request_duration_seconds_count[5m]))
# SLI de latencia
- record: sli:latency:ratio_rate5m
expr: |
sli:http_request_duration:rate5m / sli:http_request_duration_total:rate5m
Estas recording rules pre-computan los ratios del SLI para que puedas usarlos en alertas y dashboards sin ejecutar consultas costosas cada vez.
SLOs como código con Sloth
Escribir recording rules y alert rules de Prometheus a mano para cada SLO se vuelve tedioso rápido. Ahí es donde entra Sloth. Sloth es una herramienta que genera todas las reglas de Prometheus que necesitás a partir de una definición simple de SLO.
Acá hay una definición de SLO para nuestro servicio:
# slos/tr-web.yaml
apiVersion: sloth.slok.dev/v1
kind: PrometheusServiceLevel
metadata:
name: tr-web
namespace: default
spec:
service: "tr-web"
labels:
team: "platform"
slos:
- name: "requests-availability"
objective: 99.9
description: "99.9% de las requests HTTP deben ser exitosas"
sli:
events:
error_query: sum(rate(http_requests_total{status=~"5..",service="tr-web"}[{{.window}}]))
total_query: sum(rate(http_requests_total{service="tr-web"}[{{.window}}]))
alerting:
name: TrWebHighErrorRate
labels:
severity: critical
team: platform
annotations:
summary: "Tasa de error alta en tr-web"
page_alert:
labels:
severity: critical
ticket_alert:
labels:
severity: warning
- name: "requests-latency"
objective: 99.0
description: "99% de las requests deben ser más rápidas que 300ms"
sli:
events:
error_query: |
sum(rate(http_request_duration_seconds_count{service="tr-web"}[{{.window}}]))
-
sum(rate(http_request_duration_seconds_bucket{le="0.3",service="tr-web"}[{{.window}}]))
total_query: sum(rate(http_request_duration_seconds_count{service="tr-web"}[{{.window}}]))
alerting:
name: TrWebHighLatency
labels:
severity: warning
team: platform
annotations:
summary: "Latencia alta en tr-web"
page_alert:
labels:
severity: critical
ticket_alert:
labels:
severity: warning
Después generás las reglas de Prometheus:
sloth generate -i slos/tr-web.yaml -o prometheus-rules/tr-web-slo.yaml
Sloth genera alertas multi-ventana y multi-tasa-de-quemado siguiendo las recomendaciones del libro de SRE de Google. Obtenés alertas de quemado rápido (algo está muy mal ahora) y alertas de quemado lento (estás consumiendo presupuesto de error más rápido de lo esperado). Esto es una mejora enorme comparado con definir umbrales de alerta manualmente.
Desplegando SLOs con ArgoCD
Ahora que tenemos nuestras definiciones de SLO y las reglas de Prometheus generadas como archivos YAML, podemos desplegarlos de la manera GitOps usando ArgoCD. Si leíste mi artículo anterior sobre GitOps, esto te va a resultar familiar.
La idea es simple: almacená tus definiciones de SLO y reglas generadas en un repositorio Git, y dejá que ArgoCD las sincronice con tu cluster.
Acá está la estructura del repositorio:
slo-configs/
├── slos/
│ ├── tr-web.yaml # Definiciones de SLO de Sloth
│ └── api-gateway.yaml
├── generated/
│ ├── tr-web-slo.yaml # Recursos PrometheusRule generados
│ └── api-gateway-slo.yaml
├── dashboards/
│ ├── tr-web-slo.json # JSON de dashboard de Grafana
│ └── api-gateway-slo.json
└── argocd/
└── application.yaml # Manifiesto de Application de ArgoCD
El manifiesto de Application de ArgoCD:
# argocd/application.yaml
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: slo-configs
namespace: argocd
spec:
project: default
source:
repoURL: https://github.com/kainlite/slo-configs
targetRevision: HEAD
path: generated
destination:
server: https://kubernetes.default.svc
namespace: monitoring
syncPolicy:
automated:
prune: true
selfHeal: true
syncOptions:
- CreateNamespace=true
Con este setup, cada vez que actualizás una definición de SLO, regenerás las reglas y pusheás a Git, ArgoCD aplica automáticamente los cambios en tu cluster. Sin comandos manuales de kubectl, sin olvidarte de aplicar ese archivo que cambiaste la semana pasada.
También podés configurar un paso de CI para regenerar automáticamente las reglas de Prometheus cuando cambian las definiciones de SLO:
# .github/workflows/generate-slos.yaml
name: Generate SLO Rules
on:
push:
paths:
- 'slos/**'
jobs:
generate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install Sloth
run: |
curl -L https://github.com/slok/sloth/releases/latest/download/sloth-linux-amd64 -o sloth
chmod +x sloth
- name: Generate rules
run: |
for slo in slos/*.yaml; do
name=$(basename "$slo" .yaml)
./sloth generate -i "$slo" -o "generated/${name}-slo.yaml"
done
- name: Commit and push
run: |
git config user.name "github-actions"
git config user.email "[email protected]"
git add generated/
git diff --staged --quiet || git commit -m "chore: regenerate SLO rules"
git push
Ahora tenés un pipeline completamente automatizado: editás una definición de SLO, pusheás, CI genera las reglas, ArgoCD las despliega. Hermoso.
Servidores MCP para automatización de SRE
Acá es donde las cosas se ponen realmente interesantes. Los servidores de Model Context Protocol (MCP) te permiten darle a asistentes de IA como Claude acceso a tus herramientas de infraestructura. Imaginate poder preguntar “¿cuánto presupuesto de error me queda para tr-web?” y obtener una respuesta real de tus datos en vivo de Prometheus.
Un servidor MCP es esencialmente una API que expone herramientas que una IA puede llamar. Podés construir uno que envuelva tus APIs de Prometheus y Kubernetes:
// mcp-sre-server/src/main.rs
// Un ejemplo simplificado de un servidor MCP para consultas de SRE
use mcp_server::{Server, Tool, ToolResult};
#[derive(Tool)]
#[tool(name = "query_error_budget", description = "Consultar presupuesto de error restante")]
struct QueryErrorBudget {
service: String,
slo_name: String,
}
impl QueryErrorBudget {
async fn execute(&self) -> ToolResult {
let query = format!(
r#"1 - (
sli:availability:ratio_rate30d{{service="{}"}}
) / (1 - {}.0/100)"#,
self.service, self.objective
);
let result = prometheus_query(&query).await?;
ToolResult::text(format!(
"Presupuesto de error para {}/{}: {:.2}% restante",
self.service, self.slo_name, result * 100.0
))
}
}
#[derive(Tool)]
#[tool(name = "list_slo_violations", description = "Listar SLOs que están quemando demasiado rápido")]
struct ListSloViolations;
impl ListSloViolations {
async fn execute(&self) -> ToolResult {
let query = r#"ALERTS{alertname=~".*SLO.*", alertstate="firing"}"#;
let alerts = prometheus_query(query).await?;
ToolResult::text(format!("Violaciones de SLO activas:\n{}", alerts))
}
}
#[derive(Tool)]
#[tool(name = "get_deployment_risk", description = "Evaluar riesgo de despliegue basado en presupuesto de error")]
struct GetDeploymentRisk {
service: String,
}
impl GetDeploymentRisk {
async fn execute(&self) -> ToolResult {
let budget = get_error_budget(&self.service).await?;
let recent_deploys = get_recent_deploys(&self.service).await?;
let risk = match budget {
b if b > 0.5 => "BAJO - bastante presupuesto de error disponible",
b if b > 0.2 => "MEDIO - el presupuesto de error se está agotando",
b if b > 0.0 => "ALTO - muy poco presupuesto de error",
_ => "CRÍTICO - presupuesto de error agotado, considerá congelar despliegues",
};
ToolResult::text(format!(
"Riesgo de despliegue para {}: {}\nPresupuesto restante: {:.1}%\nDespliegues recientes: {}",
self.service, risk, budget * 100.0, recent_deploys
))
}
}
Con este servidor MCP corriendo, podés configurar Claude Code o cualquier cliente compatible con MCP para conectarse. Después tenés acceso en lenguaje natural a tus datos de SRE:
- “¿Cuánto presupuesto de error tiene tr-web?” → Consulta Prometheus, devuelve el presupuesto restante
- “¿Es seguro deployar ahora?” → Verifica presupuesto de error + incidentes recientes
- “¿Qué SLOs están en riesgo esta semana?” → Lista SLOs con tasas de quemado altas
- “Mostrá la tendencia de latencia de las últimas 24h” → Consulta Prometheus y resume
También podés construir herramientas MCP que se integren con ArgoCD:
#[derive(Tool)]
#[tool(name = "argocd_sync_status", description = "Verificar estado de sincronización de ArgoCD")]
struct ArgoCDSyncStatus;
impl ArgoCDSyncStatus {
async fn execute(&self) -> ToolResult {
let output = Command::new("argocd")
.args(["app", "get", "slo-configs", "-o", "json"])
.output()
.await?;
let app: ArgoApp = serde_json::from_slice(&output.stdout)?;
ToolResult::text(format!(
"Estado de sincronización de SLO configs: {}\nSalud: {}\nÚltima sincronización: {}",
app.status.sync.status,
app.status.health.status,
app.status.sync.compared_to.revision
))
}
}
#[derive(Tool)]
#[tool(name = "rollback_deployment", description = "Hacer rollback de un despliegue via ArgoCD")]
struct RollbackDeployment {
service: String,
revision: Option<String>,
}
impl RollbackDeployment {
async fn execute(&self) -> ToolResult {
// Esto estaría protegido detrás de confirmación en un setup real
let revision = self.revision.as_deref().unwrap_or("HEAD~1");
let output = Command::new("argocd")
.args(["app", "rollback", &self.service, "--revision", revision])
.output()
.await?;
ToolResult::text(format!("Rollback iniciado para {} a {}", self.service, revision))
}
}
La configuración del servidor MCP en tu configuración de Claude Code se vería algo así:
{
"mcpServers": {
"sre-tools": {
"command": "mcp-sre-server",
"args": ["--prometheus-url", "http://prometheus:9090", "--argocd-url", "https://argocd.example.com"],
"env": {
"ARGOCD_AUTH_TOKEN": "tu-token-aca"
}
}
}
}
Automatizaciones que unen todo
El verdadero poder viene cuando combinás SLOs, ArgoCD y servidores MCP en flujos de trabajo automatizados. Acá hay algunos patrones que funcionan bien en la práctica:
1. Puertas de despliegue automatizadas
Usá presupuestos de error como puertas de despliegue. Si el presupuesto de error está por debajo de un umbral, bloqueá despliegues automáticamente:
# En tu pipeline de CI
- name: Verificar presupuesto de error
run: |
BUDGET=$(curl -s "http://prometheus:9090/api/v1/query?query=error_budget_remaining{service='tr-web'}" \
| jq -r '.data.result[0].value[1]')
if (( $(echo "$BUDGET < 0.1" | bc -l) )); then
echo "Presupuesto de error por debajo del 10%, bloqueando despliegue"
exit 1
fi
2. Creación automática de incidentes
Cuando se rompe un SLO, creá automáticamente un issue o incidente:
# alertmanager-config.yaml
receivers:
- name: slo-breach
webhook_configs:
- url: http://incident-bot:8080/create
send_resolved: true
route:
routes:
- match:
severity: critical
type: slo_breach
receiver: slo-breach
3. Reportes semanales de SLO
Automatizá reportes semanales de SLO para mantener al equipo informado:
# Un CronJob que consulta Prometheus y envía un resumen a Slack
apiVersion: batch/v1
kind: CronJob
metadata:
name: slo-weekly-report
namespace: monitoring
spec:
schedule: "0 9 * * 1" # Todos los lunes a las 9am
jobTemplate:
spec:
template:
spec:
containers:
- name: reporter
image: kainlite/slo-reporter:latest
env:
- name: PROMETHEUS_URL
value: "http://prometheus:9090"
- name: SLACK_WEBHOOK
valueFrom:
secretKeyRef:
name: slack-webhook
key: url
restartPolicy: Never
4. Congelamiento de features basado en presupuesto de error
Este es uno de los patrones más poderosos de SRE. Cuando el presupuesto de error se agota, el equipo debería cambiar el foco de features a trabajo de confiabilidad:
- Presupuesto > 50%: Shippeá features libremente
- Presupuesto 20-50%: Sé cauteloso con cambios riesgosos
- Presupuesto 5-20%: Enfocate en mejoras de confiabilidad
- Presupuesto < 5%: Congelamiento de features, todos a trabajar en confiabilidad
Podés automatizar esto haciendo que tu servidor MCP actualice una página de estado o canal de Slack con el nivel actual de presupuesto, para que todos en el equipo sepan dónde están las cosas sin tener que revisar dashboards.
Juntando todo
Acá hay un resumen de lo que construimos:
- SLIs como métricas de Prometheus: Recording rules que calculan ratios de disponibilidad y latencia
- SLOs con Sloth: Definiciones declarativas de SLO que generan alertas multi-ventana y multi-tasa-de-quemado
- GitOps con ArgoCD: Configuraciones de SLO almacenadas en Git, sincronizadas automáticamente al cluster
- Servidores MCP: Interfaz de lenguaje natural para consultar presupuestos de error, verificar riesgo de despliegue y gestionar ArgoCD
- Automatizaciones: Puertas de despliegue, creación de incidentes, reportes semanales y políticas de presupuesto de error
La belleza de este enfoque es que cada pieza es simple por sí sola, pero juntas crean un sistema donde la confiabilidad es medible, automatizada y parte del flujo de trabajo diario del equipo en lugar de algo que se piensa después.
Notas finales
SRE no tiene que ser complicado. Empezá con un SLI para tu servicio más importante, definí un SLO razonable y construí desde ahí. Las herramientas que cubrimos (Prometheus, Sloth, ArgoCD, servidores MCP) son todas de código abierto y probadas en batalla.
La conclusión clave es esta: medí lo que importa a tus usuarios, definí objetivos, y dejá que la automatización se encargue del resto. Tu yo del futuro durante la próxima guardia te lo va a agradecer.
¡Espero que te haya resultado útil y lo hayas disfrutado! ¡Hasta la próxima!
Errata
Si encontrás algún error o tenés alguna sugerencia, por favor mandame un mensaje para que se corrija.
También podés revisar el código fuente y los cambios en las fuentes acá
$ Comentarios
Online: 0Por favor inicie sesión para poder escribir comentarios.