# Chaos Engineering — Guía de Confiabilidad Proactiva

El chaos engineering es la disciplina de experimentar en un sistema con el
objetivo de descubrir debilidades antes de que se manifiesten en incidentes
reales. No es romper cosas al azar — es ciencia aplicada con hipótesis,
métricas y criterios de parada.

---

## Principios fundamentales

### 1. Steady State Hypothesis (hipótesis de estado estable)

Antes de inyectar fallo, debes definir cómo se ve el sistema funcionando
correctamente. Sin esta definición, no puedes saber si el experimento causó
un problema o si el problema ya existía.

```yaml
# Ejemplo de steady state hypothesis
steady_state:
  nombre: "API de pedidos en operación normal"
  metricas:
    - nombre: disponibilidad
      query: "sum(rate(http_requests_total{code!~'5..'}[5m])) / sum(rate(http_requests_total[5m]))"
      umbral_minimo: 0.999   # 99.9%
    - nombre: latencia_p99
      query: "histogram_quantile(0.99, rate(http_request_duration_seconds_bucket[5m]))"
      umbral_maximo: 0.5     # 500ms
    - nombre: tasa_pedidos_por_minuto
      query: "rate(pedidos_creados_total[5m]) * 60"
      umbral_minimo: 50      # al menos 50 pedidos/min en horario laboral
```

Si cualquiera de estas métricas no está en el umbral esperado **antes** de
empezar, cancelar el experimento. El sistema no está en estado estable.

### 2. Gradualidad: staging antes que producción

El path correcto es siempre progresivo:

```
Desarrollo → Staging con tráfico simulado → Producción en horas valle
           → Producción en horario completo → Chaos continuo automatizado
```

Nunca saltar pasos. Un experimento en producción sin haberlo ejecutado primero
en staging es un incidente disfrazado de experimento.

### 3. Radio de explosión mínimo (blast radius)

Empieza con el experimento de menor impacto posible. Si quieres probar resiliencia
ante fallo de base de datos, comienza con una instancia de réplica, no con el
primario.

---

## Herramientas por capa del sistema

### Kubernetes / contenedores

| Herramienta | Uso | Instalación |
|-------------|-----|-------------|
| **Chaos Monkey** (Netflix) | Terminar instancias aleatoriamente | `brew install chaos-monkey` |
| **Litmus** | Experimentos nativos en K8s (pods, nodos, red) | `kubectl apply -f litmus-operator.yaml` |
| **Chaos Mesh** | Chaos nativo K8s con UI web | `helm install chaos-mesh chaos-mesh/chaos-mesh` |

```yaml
# Litmus: experimento de fallo de pod
apiVersion: litmuschaos.io/v1alpha1
kind: ChaosEngine
metadata:
  name: pod-delete-experiment
spec:
  appinfo:
    appns: produccion
    applabel: "app=api-pedidos"
  experiments:
    - name: pod-delete
      spec:
        components:
          env:
            - name: TOTAL_CHAOS_DURATION
              value: "60"           # 60 segundos de caos
            - name: CHAOS_INTERVAL
              value: "10"           # eliminar pod cada 10 segundos
            - name: FORCE
              value: "false"        # graceful shutdown, no kill -9
```

### Red / latencia

| Herramienta | Uso | Instalación |
|-------------|-----|-------------|
| **Toxiproxy** (Shopify) | Simular latencia, pérdida de paquetes, corte de conexión | `go install github.com/Shopify/toxiproxy/v2/cmd/toxiproxy-server@latest` |
| **tc (Linux traffic control)** | Control de red a nivel de kernel | Incluido en Linux |

```bash
# Toxiproxy: agregar latencia de 100ms al 10% de las conexiones a la BD
toxiproxy-cli toxic add mi-postgres \
  --type latency \
  --attribute latency=100 \
  --attribute jitter=50 \
  --toxicity 0.10   # afectar 10% de las conexiones

# Limpiar después del experimento
toxiproxy-cli toxic remove mi-postgres --toxicName latency_downstream
```

### Recursos del sistema

| Herramienta | Uso |
|-------------|-----|
| **Gremlin** | Platform SaaS completa: CPU, memoria, disco, red, proceso |
| **stress-ng** | Stress testing de CPU y memoria (open source) |
| `dd` + `fallocate` | Simular disco lleno |

```bash
# stress-ng: consumir 80% de CPU por 60 segundos
stress-ng --cpu 4 --cpu-load 80 --timeout 60s

# Simular disco casi lleno (dejar solo 100MB libres)
fallocate -l $(df / | tail -1 | awk '{print $4 - 100000}')k /tmp/disco-lleno.tmp
# Limpiar inmediatamente después
rm /tmp/disco-lleno.tmp
```

---

## Experimentos básicos por tipo

### Experimento 1: Fallo de pod/instancia

**Hipótesis**: el servicio sigue disponible cuando un pod falla porque Kubernetes
lo reinicia y el load balancer redirige el tráfico.

```bash
# Verificar steady state
kubectl top pods -n produccion

# Inyectar fallo
kubectl delete pod <nombre-pod> -n produccion

# Observar recuperación
kubectl get pods -n produccion -w

# Verificar que el SLO no se violó
# (revisar en Grafana: disponibilidad durante los 2 minutos del experimento)
```

**Criterio de parada**: si la disponibilidad cae por debajo del SLO, detener
y analizar antes de continuar.

### Experimento 2: Latencia de red hacia dependencias

**Hipótesis**: el servicio degrada graciosamente cuando la base de datos tiene
latencia alta, sin causar errores 5xx.

```bash
# Configurar toxiproxy frente a PostgreSQL
toxiproxy-cli create mi-postgres --listen 0.0.0.0:5433 --upstream postgres:5432

# Inyectar 300ms de latencia
toxiproxy-cli toxic add mi-postgres --type latency --attribute latency=300

# Observar comportamiento (debe ver latencia en p99 del servicio, no errores)
# Medir: ¿el circuit breaker se activa? ¿los timeouts de conexión están bien configurados?

# Limpiar
toxiproxy-cli toxic remove mi-postgres --toxicName latency_downstream
toxiproxy-cli delete mi-postgres
```

### Experimento 3: Agotamiento de recursos

**Hipótesis**: el sistema tiene circuit breakers en el pool de conexiones de BD
que previenen cascada de fallos cuando las conexiones se agotan.

```bash
# Saturar el pool de conexiones (con una herramienta de pruebas de carga)
pgbench -c 100 -j 10 -T 60 postgresql://user@localhost/db

# Observar: ¿el servicio retorna 503 en lugar de colgar?
# ¿Las métricas de saturación alertaron antes de que se violara el SLO?
```

---

## Safety: reglas de parada automática

Todo experimento de chaos **DEBE** tener un kill switch claro:

```yaml
# Criterios de parada automática (Litmus ChaosEngine)
spec:
  jobCleanUpPolicy: retain
  components:
    # Detener si disponibilidad < 99.5% durante el experimento
    statusCheckURLs:
      - "http://api-pedidos/health/ready"
    # Revertir automáticamente después de N segundos
    experimentTimeout: 300  # 5 minutos máximo
```

### Checklist de safety antes de ejecutar

- [ ] Steady state definido con queries y umbrales.
- [ ] Error budget > 50% disponible.
- [ ] El experimento se ejecutó primero en staging.
- [ ] Kill switch configurado y probado.
- [ ] Primario y secundario de on-call disponibles.
- [ ] Runbooks actualizados para los tipos de fallo que se inyectarán.
- [ ] Ventana de mantenimiento comunicada a stakeholders (para producción).
- [ ] Experimento programado fuera del período de mayor tráfico.

---

## Cuándo NO hacer chaos engineering

Detener y no ejecutar si:

- El error budget está por debajo del 50% del período actual.
- Hay un incidente activo o abierto en el servicio objetivo.
- El equipo no tiene runbooks para los tipos de fallo que se van a inyectar.
- El servicio no tiene métricas de SLI funcionando correctamente.
- Es la primera semana después de un deploy mayor o una migración de base de datos.
- No hay un ingeniero responsable disponible durante todo el experimento.

---

## Madurez del programa de chaos

| Nivel | Descripción | Prerequisito |
|-------|-------------|-------------|
| 0 | Sin chaos engineering | — |
| 1 | Experimentos manuales en staging | SLOs definidos y medidos |
| 2 | Experimentos manuales en producción (horario laboral) | Nivel 1 + runbooks completos |
| 3 | Chaos automatizado programado (staging) | Nivel 2 + CI integrado |
| 4 | Chaos continuo en producción (GameDays regulares) | Nivel 3 + cultura SRE madura |

La mayoría de los equipos operan bien en Nivel 2. No existe ninguna razón para
apresurarse a Nivel 4.
