Saltar al contenido

Ingeniería del caos: romper cosas a propósito

La ingeniería del caos inyecta fallos deliberados en sistemas de producción para descubrir debilidades antes de que causen interrupciones reales.

4 min de lectura
Flujo de un experimento de ingeniería del caos mostrando las fases de hipótesis, experimento y análisis

Tu sistema parece resistente sobre el papel — reintentos, circuit breakers, bases de datos redundantes. ¿Pero realmente sobrevive a los fallos? La ingeniería del caos responde a esa pregunta inyectando fallos deliberadamente en experimentos controlados. En lugar de esperar a las caídas de las 3 AM para descubrir debilidades, las encuentras a las 2 PM de un martes, cuando todo el equipo está despierto.

El marco del experimento de caos

Cada experimento de caos sigue una estructura científica: formula una hipótesis, define el estado estable, inyecta el fallo, observa y analiza.

tstypescript
// Structure of a chaos experiment
interface ChaosExperiment {
  name: string;
  hypothesis: string;
  steadyState: {
    metric: string;
    threshold: number;
    window: string;
  };
  method: {
    type: "latency" | "failure" | "resource" | "network";
    target: string;
    parameters: Record<string, unknown>;
    duration: string;
  };
  rollback: {
    automatic: boolean;
    trigger: string;
  };
}
 
const experiment: ChaosExperiment = {
  name: "Database failover under load",
  hypothesis: "When the primary database becomes unreachable, the application fails over to the replica within 30 seconds with less than 1% error rate increase",
  steadyState: {
    metric: "error_rate",
    threshold: 0.01,  // 1% error rate
    window: "5m",
  },
  method: {
    type: "network",
    target: "primary-db.internal",
    parameters: { action: "block", port: 5432 },
    duration: "5m",
  },
  rollback: {
    automatic: true,
    trigger: "error_rate > 0.05 for 2m",  // Abort if error rate exceeds 5%
  },
};

Empezar simple: experimentos a nivel de aplicación

No necesitas tráfico de producción ni herramientas sofisticadas para empezar. Comienza con experimentos controlados en un entorno de staging.

tstypescript
// Middleware that simulates downstream service failures
// Only active when CHAOS_MODE environment variable is set
function chaosMiddleware(serviceName: string) {
  return (req: Request, res: Response, next: NextFunction) => {
    if (process.env.CHAOS_MODE !== "true") {
      return next();
    }
 
    const chaosConfig = getChaosConfig(serviceName);
 
    // Simulate latency injection
    if (chaosConfig.latency && Math.random() < chaosConfig.latency.probability) {
      const delay = chaosConfig.latency.minMs +
        Math.random() * (chaosConfig.latency.maxMs - chaosConfig.latency.minMs);
      setTimeout(next, delay);
      return;
    }
 
    // Simulate error injection
    if (chaosConfig.error && Math.random() < chaosConfig.error.probability) {
      res.status(chaosConfig.error.statusCode).json({
        error: "Chaos injection: simulated failure",
        service: serviceName,
      });
      return;
    }
 
    next();
  };
}

Experimentos de partición de red

Los fallos de red entre servicios son el problema más común en producción. Usa tc (traffic control) en Linux para simular condiciones de red reales.

shbash
# ❌ Testing only the happy path
# curl http://api-server:3000/health → 200 OK
# "Ship it, the service works!"
 
# ✅ Simulating real network conditions
 
# Add 200ms latency to traffic going to the database
tc qdisc add dev eth0 root netem delay 200ms 50ms distribution normal
 
# Simulate 10% packet loss to downstream service
tc qdisc add dev eth0 root netem loss 10%
 
# Simulate network partition (complete connectivity loss)
iptables -A OUTPUT -d database.internal -j DROP
 
# Clean up after experiment
tc qdisc del dev eth0 root
iptables -D OUTPUT -d database.internal -j DROP

Validar circuit breakers

Se supone que los circuit breakers previenen fallos en cascada. Los experimentos de caos verifican que realmente funcionen.

tstypescript
// ❌ Assuming the circuit breaker works because the code looks right
const breaker = new CircuitBreaker(callExternalService, {
  timeout: 3000,
  errorThresholdPercentage: 50,
  resetTimeout: 30000,
});
 
// ✅ Chaos experiment to verify circuit breaker behavior
async function verifyCiruitBreaker() {
  const metrics = {
    totalRequests: 0,
    successfulRequests: 0,
    circuitOpenRejections: 0,
    timeouts: 0,
  };
 
  // Phase 1: Normal operation — circuit should be closed
  console.log("Phase 1: Verifying normal operation");
  for (let i = 0; i < 100; i++) {
    try {
      await breaker.fire();
      metrics.successfulRequests++;
    } catch (error) {
      // Should not happen in normal operation
    }
    metrics.totalRequests++;
  }
 
  // Phase 2: Inject failures — circuit should open
  console.log("Phase 2: Injecting failures");
  enableChaosMode({ error: { probability: 1.0, statusCode: 500 } });
 
  for (let i = 0; i < 50; i++) {
    try {
      await breaker.fire();
    } catch (error) {
      if (error.message === "Breaker is open") {
        metrics.circuitOpenRejections++;
      } else {
        metrics.timeouts++;
      }
    }
    metrics.totalRequests++;
  }
 
  // Phase 3: Recovery — circuit should close after reset timeout
  console.log("Phase 3: Verifying recovery");
  disableChaosMode();
  await sleep(35000); // Wait for reset timeout
 
  const recovered = await breaker.fire();
  console.log("Circuit recovered:", recovered !== undefined);
 
  return metrics;
}

Ejercicios de gameday

Un gameday es un ejercicio de equipo programado en el que ejecutas experimentos de caos y practicas la respuesta a incidentes. Combina pruebas técnicas con validación de procesos.

markdownmarkdown
## Gameday Checklist
 
### Before
- [ ] Define 3-5 specific experiments with hypotheses
- [ ] Ensure monitoring dashboards are visible to all participants
- [ ] Verify rollback procedures for each experiment
- [ ] Brief all participants on the plan and abort criteria
- [ ] Confirm the on-call rotation is staffed
 
### During
- [ ] Run one experiment at a time
- [ ] Record observations in a shared document
- [ ] Note any unexpected behaviors or cascading effects
- [ ] Use the incident response process (even if the "incident" is planned)
- [ ] Abort immediately if abort criteria are met
 
### After
- [ ] Document findings for each experiment
- [ ] File tickets for discovered issues (with severity based on blast radius)
- [ ] Update runbooks based on what was learned
- [ ] Schedule follow-up gameday to verify fixes

Control del radio de impacto

Nunca ejecutes experimentos de caos sin límites. Define los límites del alcance y las condiciones de aborto automático.

ymlyaml
# Chaos experiment configuration with safety controls
experiment:
  name: "API latency injection"
  scope:
    environment: "production"
    percentage_of_traffic: 5    # Only affect 5% of requests
    target_service: "order-api"
    excluded_endpoints:         # Never inject chaos on critical paths
      - "/api/payments"
      - "/api/health"
 
  abort_conditions:
    - metric: "error_rate"
      threshold: 0.05           # Abort if errors exceed 5%
      window: "2m"
    - metric: "p99_latency_ms"
      threshold: 5000           # Abort if p99 exceeds 5 seconds
      window: "2m"
 
  duration: "15m"
  auto_rollback: true

Conclusiones clave

  1. La ingeniería del caos se basa en hipótesis — define lo que esperas antes de inyectar el fallo
  2. Empieza en staging con experimentos simples — no necesitas caos en producción desde el día uno
  3. Verifica tus patrones de resiliencia — los circuit breakers, reintentos y failovers solo funcionan si los pruebas
  4. Controla el radio de impacto agresivamente — limita siempre el alcance, establece condiciones de aborto y ten el rollback listo
  5. Los gamedays crean memoria muscular en el equipo — practica la respuesta a incidentes cuando el riesgo es bajo
  6. Corrige lo que encuentres de inmediato — una debilidad descubierta sin corrección es peor que la ignorancia, porque has aceptado el riesgo
Wilfredo Rujel

Wilfredo Rujel

Ingeniero de Software Full Stack

Compartir esta publicaciónX