Plan de Pruebas de Recuperación ante Desastres
Una plantilla para planificar y ejecutar pruebas de recuperación ante desastres incluyendo validación de failover, verificación de integridad de datos y medición del tiempo de recuperación.
Descripción general
Un plan de recuperación ante desastres sin probar es solo una esperanza sin verificar. Cuando llega el desastre de verdad, los huecos aparecen rápido: dependencias que faltan, runbooks desactualizados, tiempos de recuperación que nadie midió nunca. Este plan te da una forma repetible de poner a prueba esos procedimientos — failovers controlados, verificación de integridad de datos y cifras honestas de RTO/RPO. Ejercita el documento que produce la plantilla de plan de recuperación ante desastres.
Cuándo usarla
Usa este plan cuando:
- El cumplimiento exige una prueba de DR en el calendario (SOC2, ISO 27001)
- La infraestructura o los procedimientos de recuperación cambiaron hace poco
- Un incidente pasado expuso brechas en tus capacidades de DR
- Les debes a las partes interesadas una prueba de que las cifras de RTO/RPO son reales
Requisitos previos
Antes de comenzar:
- Runbooks de DR revisados y actualizados en los últimos 90 días
- Entorno de prueba disponible (aislado de producción)
- Snapshots de backup confirmados como recuperables en los últimos 7 días
- Stakeholders notificados de la ventana de prueba e impacto esperado
- Plan de rollback documentado en caso de que la prueba salga mal
Solución
# Plan de Prueba de Recuperación ante Desastres: `<Nombre del Servicio>`
## 1. Objetivos de la Prueba
| Objetivo | Meta | medición |
|----------|------|----------|
| Recovery Time Objective (RTO) | < 4 horas | Tiempo desde declaración de desastre hasta disponibilidad del servicio |
| Recovery Point Objective (RPO) | < 15 minutos | pérdida máxima de datos aceptable en recuperación |
| Integridad de Datos | 100% | Todas las transacciones verificadas contra la fuente |
| Comunicación | < 30 minutos | Todas las partes interesadas notificadas del inicio de la prueba |
## 2. Alcance y Suposiciones
### En Alcance
- Failover de base de datos y restauración desde backup
- Re-despliegue de aplicación a región de DR
- Cambio de DNS y enrutamiento de tráfico
- validación de smoke test de flujos criticos de usuario
### Fuera de Alcance
- Dependencias de servicios de terceros (asumidos disponibles)
- Fallas completas de centro de datos fisico (prueba basada en cloud)
- Escenarios de aislamiento completo de red
### Suposiciones
- El último snapshot de backup es válido y completo
- La región de DR tiene capacidad suficiente
- La conectividad de red entre regiones es funcional
- Los miembros del equipo estan disponibles durante la ventana de prueba
## 3. Escenarios de Prueba
### Escenario A: Falla Completa de región Primaria
**Disparador:** simulación de indisponibilidad completa de la región primaria de AWS.
**Pasos:**
1. **Declarar desastre** (0:00) — Ingeniero líder anuncia prueba de DR en canal de incidente
2. **Restaurar base de datos** (0:30) — Restaurar desde último snapshot en región de DR
3. **Desplegar aplicaciones** (1:00) — Desplegar stack de aplicación a cluster de DR
4. **Actualizar DNS** (1:30) — Cambiar tráfico a balanceador de carga de DR
5. **Validar** (2:00) — Ejecutar smoke tests y verificar integridad de datos
6. **Medir RTO** — Registrar tiempo desde declaración hasta validación exitosa
### Escenario B: corrupción de Base de Datos
**Disparador:** Base de datos primaria tiene datos corruptos requiriendo recuperación point-in-time.
**Pasos:**
1. Identificar punto de corrupción desde alertas de monitoreo
2. Restaurar base de datos a 5 minutos antes del timestamp de corrupción
3. Verificar integridad transaccional con consultas de checksum
4. Promover instancia restaurada a primaria
5. Redirigir tráfico de aplicación
## 4. Ejecución de la Prueba
### Checklist Pre-Prueba
| Item | Estado | Responsable |
|------|--------|-------------|
| Snapshot de backup creado | [ ] | DBA |
| Entorno de DR aprovisionado | [ ] | Plataforma |
| Runbooks impresos / accesibles offline | [ ] | SRE |
| Stakeholders notificados | [ ] | líder de Incidente |
| Plan de rollback confirmado | [ ] | SRE |
| Dashboards de monitoreo listos | [ ] | Observabilidad |
### Línea de Tiempo de Ejecución
| Tiempo | Actividad | Resultado Esperado |
|--------|-----------|-------------------|
| T+0:00 | Declarar inicio de prueba | Canal de incidente notificado |
| T+0:05 | Iniciar restauración de base de datos | Trabajo de restauración RDS iniciado |
| T+1:00 | Base de datos disponible | Prueba de conexión exitosa |
| T+1:30 | Desplegar aplicación | Todos los pods saludables |
| T+2:00 | Cambio de DNS | tráfico llegando a región de DR |
| T+2:30 | Smoke tests | 100% tasa de éxito |
| T+3:00 | verificación de integridad de datos | Conteo de transacciones coincide con fuente |
| T+4:00 | medición de RTO | Registrar actual vs meta |
### Procedimiento de Rollback
Si cualquier paso crítico falla:
1. Pausar prueba inmediatamente
2. Revertir DNS a región primaria
3. NO eliminar recursos de DR hasta revisión post-prueba
4. Escalar a gerente de ingenieria
5. Programar prueba de seguimiento después de correcciones
## 5. Verificación de Integridad de Datos
```sql
-- comparación de conteo de transacciones
SELECT 'primary' as source, COUNT(*) as tx_count FROM orders
UNION ALL
SELECT 'dr', COUNT(*) FROM dr_orders;
-- comparación de checksums
SELECT 'primary', SUM(CHECKSUM(*)) FROM payments
UNION ALL
SELECT 'dr', SUM(CHECKSUM(*)) FROM dr_payments;
-- último timestamp de transacción
SELECT MAX(created_at) FROM orders;
```
| Verificación | Primario | DR | Coincide |
|--------------|----------|-----|----------|
| Conteo de transacciones | ______ | ______ | [ ] |
| Checksum a nivel de fila | ______ | ______ | [ ] |
| último timestamp | ______ | ______ | [ ] |
## 6. Reporte Post-Prueba
### Resumen de Resultados
| Métrica | Meta | Actual | Estado |
|---------|------|--------|--------|
| RTO | < 4 horas | ______ | Aprobado / Fallido |
| RPO | < 15 minutos | ______ | Aprobado / Fallido |
| Integridad de datos | 100% | ______ | Aprobado / Fallido |
| Smoke tests | 100% | ______ | Aprobado / Fallido |
### Problemas Encontrados
| Problema | Severidad | Responsable | Fecha Limite de corrección |
|----------|-----------|-------------|---------------------------|
| ______ | crítica / Alta / Media / Baja | ______ | ______ |
### Lecciones Aprendidas
- Lo que funciono bien:
- Lo que tomo más tiempo de lo esperado:
- Lo que fallo inesperadamente:
- Lo que debería automatizarse:
### Aprobación
| Rol | Nombre | Fecha | Firma |
|-----|--------|-------|-------|
| Líder de Prueba | ______ | ______ | ______ |
| Gerente de Ingeniería | ______ | ______ | ______ |
| Compliance (si aplica) | ______ | ______ | ______ |
Explicación
El plan divide las pruebas de DR en objetivos declarados (metas de RTO/RPO), escenarios controlados (modos específicos de falla) y resultados medibles (criterios de aprobado/fallido). El checklist pre-prueba existe para que un simulacro no falle porque a alguien se le olvidó un snapshot — eso es un problema de preparación, no una brecha de DR. Y como las pruebas salen mal a veces, el procedimiento de rollback evita que un mal simulacro se convierta en una caída real.
Cada marca de tiempo es un checkpoint, no una suposición — si la restauración de la base de datos pasa de T+1:00, la prueba ya sabe que falló antes de llegar al final.
Variantes
| Contexto | Enfoque | Notas |
|---|---|---|
| Prueba de cumplimiento anual | Escenario completo con auditores | Documentar todo, mantener firmas |
| Ejercicio interno trimestral | Escenario abreviado, sin auditor | Enfocarse en coordinación del equipo y tiempos |
| después de cambio de infraestructura | Escenario dirigido al componente cambiado | Validar solo lo que cambio |
| Game day / chaos engineering | Prueba no anunciada | más realista, requiere automatización madura |
Lo que funciona
- Programar pruebas durante horario laboral — las personas que necesitan aprender deben participar
- Medir, no estimar — el RTO real casi siempre es mayor que la estimación documentada
- Probar desde backup, no solo desde replicación — la replicación puede estar saludable mientras los backups estan corruptos
- Documentar toda desviación — incluso diferencias menores de tiempo indican brechas de proceso
- Automatizar smoke tests — la verificación manual es demasiado lenta durante un desastre real
Errores comunes
- Probar solo failover, no restauración desde backup — la replicación puede estar saludable mientras los backups estan corruptos
- No probar con volúmenes de datos realistas — restaurar 1TB toma más que restaurar 1GB
- Saltar la prueba del procedimiento de rollback — descubres que el rollback no funciona cuando más lo necesitas
- Probar solo durante períodos de bajo tráfico — no valida las suposiciones de capacidad
- No actualizar los runbooks después de los hallazgos — las mismas brechas aparecen en la prueba del próximo año
Soluciones Avanzadas
Ejecución automatizada de pruebas de DR con Terraform y AWS
Aprovisiona el entorno de DR, restaura desde backup y ejecuta pruebas de validación automáticamente:
import boto3
import subprocess
import time
from dataclasses import dataclass, field
from typing import List, Optional
from datetime import datetime, timedelta
@dataclass
class DRTestResult:
test_name: str
start_time: datetime
end_time: Optional[datetime] = None
rto_seconds: Optional[int] = None
rpo_seconds: Optional[int] = None
steps_completed: List[str] = field(default_factory=list)
steps_failed: List[str] = field(default_factory=list)
data_integrity_ok: bool = False
class DRTestRunner:
def __init__(self, region: str, dr_region: str):
self.region = region
self.dr_region = dr_region
self.rds = boto3.client("rds", region_name=dr_region)
self.ec2 = boto3.client("ec2", region_name=dr_region)
self.s3 = boto3.client("s3", region_name=region)
def restore_database_from_snapshot(
self, snapshot_id: str, db_instance_id: str
) -> str:
"""Restore RDS instance from snapshot in DR region."""
print(f"Restoring {db_instance_id} from snapshot {snapshot_id}...")
response = self.rds.restore_db_instance_from_db_snapshot(
DBInstanceIdentifier=db_instance_id,
DBSnapshotIdentifier=snapshot_id,
AvailabilityZone=f"{self.dr_region}a",
)
# Wait for instance to be available
waiter = self.rds.get_waiter("db_instance_available")
waiter.wait(DBInstanceIdentifier=db_instance_id)
endpoint = response["DBInstance"]["Endpoint"]["Address"]
print(f"Database available at: {endpoint}")
return endpoint
def run_smoke_tests(self, endpoint: str) -> bool:
"""Run smoke tests against the restored environment."""
tests = [
("health_check", f"curl -sf http://{endpoint}/health"),
("write_test", f"curl -sf -X POST http://{endpoint}/api/test -d '{{\"test\":\"dr\"}}'"),
("read_test", f"curl -sf http://{endpoint}/api/test/dr"),
]
all_passed = True
for name, cmd in tests:
result = subprocess.run(cmd, shell=True, capture_output=True, timeout=30)
if result.returncode == 0:
print(f" PASS: {name}")
else:
print(f" FAIL: {name}: {result.stderr.decode()}")
all_passed = False
return all_passed
def verify_data_integrity(
self, primary_conn: str, dr_conn: str
) -> bool:
"""Compare transaction counts between primary and DR."""
primary_count = self._query_count(primary_conn)
dr_count = self._query_count(dr_conn)
match = primary_count == dr_count
print(f"Primary: {primary_count} rows, DR: {dr_count} rows, Match: {match}")
return match
def _query_count(self, conn_str: str) -> int:
"""Execute a count query and return the result."""
cmd = f'psql "{conn_str}" -t -c "SELECT COUNT(*) FROM orders;"'
result = subprocess.run(cmd, shell=True, capture_output=True, timeout=60)
return int(result.stdout.decode().strip()) if result.returncode == 0 else -1
def execute_full_test(self, snapshot_id: str) -> DRTestResult:
"""Execute a full DR test and return results."""
result = DRTestResult(
test_name="full_region_failure",
start_time=datetime.now(),
)
try:
# Step 1: Restore database
endpoint = self.restore_database_from_snapshot(
snapshot_id, "dr-test-instance"
)
result.steps_completed.append("database_restore")
# Step 2: Run smoke tests
if self.run_smoke_tests(endpoint):
result.steps_completed.append("smoke_tests")
else:
result.steps_failed.append("smoke_tests")
# Step 3: Verify data integrity
if self.verify_data_integrity(
"postgresql://primary.db.internal/appdb",
f"postgresql://{endpoint}/appdb",
):
result.data_integrity_ok = True
result.steps_completed.append("data_integrity")
else:
result.steps_failed.append("data_integrity")
except Exception as e:
print(f"Test failed: {e}")
result.steps_failed.append(f"exception: {str(e)}")
result.end_time = datetime.now()
result.rto_seconds = int((result.end_time - result.start_time).total_seconds())
return result
# Example usage
runner = DRTestRunner("us-east-1", "us-west-2")
result = runner.execute_full_test("rds-snapshot-2026-07-01")
print(f"\nRTO: {result.rto_seconds}s")
print(f"Steps completed: {result.steps_completed}")
print(f"Steps failed: {result.steps_failed}")
print(f"Data integrity: {result.data_integrity_ok}")
Script de game day de chaos engineering
Inyecta fallas controladas para probar la preparación de DR sin aviso previo. Advertencia: el escenario full hace cordon y drain de todos los nodos con la etiqueta de la región primaria — ejecútalo solo en clusters que puedas permitirte tumbar, y nunca como primer simulacro.
#!/bin/bash
# game-day.sh - Chaos engineering DR test
# Usage: ./game-day.sh --scenario <network|disk|cpu|full>
set -euo pipefail
SCENARIO="${1:---scenario}"
shift || true
case "$SCENARIO" in
--scenario) SCENARIO="$1" ;;
esac
NAMESPACE="production"
SERVICE="api"
LOG_FILE="/tmp/game-day-$(date +%s).log"
log() { echo "[$(date -u +%H:%M:%S)] $1" | tee -a "$LOG_FILE"; }
case "$SCENARIO" in
network)
log "Injecting network latency to ${SERVICE} pods..."
kubectl exec -n "$NAMESPACE" deployment/"$SERVICE" -- \
tc qdisc add dev eth0 root netem delay 500ms 2>/dev/null || true
log "Monitoring for 5 minutes..."
sleep 300
kubectl exec -n "$NAMESPACE" deployment/"$SERVICE" -- \
tc qdisc del dev eth0 root 2>/dev/null || true
log "Network latency removed."
;;
disk)
log "Filling disk on ${SERVICE} pods to 90%..."
kubectl exec -n "$NAMESPACE" deployment/"$SERVICE" -- \
fallocate -l $(df --output=avail -BG / | tail -1 | tr -d 'G ')M /tmp/fill 2>/dev/null || true
sleep 120
kubectl exec -n "$NAMESPACE" deployment/"$SERVICE" -- rm -f /tmp/fill 2>/dev/null || true
log "Disk space restored."
;;
cpu)
log "Spiking CPU on ${SERVICE} pods..."
kubectl exec -n "$NAMESPACE" deployment/"$SERVICE" -- \
sh -c 'yes > /dev/null & yes > /dev/null & yes > /dev/null &' 2>/dev/null || true
sleep 120
kubectl delete pod -n "$NAMESPACE" -l app="$SERVICE" --grace-period=0 --force 2>/dev/null || true
log "CPU spike ended (pods restarted)."
;;
full)
log "Starting full region failure simulation..."
log "1. Cordoning all nodes in primary region..."
kubectl cordon --selector=topology.kubernetes.io/region=us-east-1
log "2. Draining workloads..."
kubectl drain --selector=topology.kubernetes.io/region=us-east-1 \
--ignore-daemonsets --delete-emptydir-data --timeout=300s || true
log "3. Waiting 5 minutes for failover..."
sleep 300
log "4. Checking DR region health..."
kubectl get pods -n production --context=dr-cluster
log "5. Uncordoning primary region..."
kubectl uncordon --selector=topology.kubernetes.io/region=us-east-1
log "Full region simulation complete."
;;
*)
echo "Usage: $0 --scenario <network|disk|cpu|full>"
exit 1
;;
esac
log "Game day results saved to $LOG_FILE"
Query de dashboard de monitoreo RTO/RPO
Rastrea métricas de RTO y RPO a lo largo del tiempo para identificar tendencias y degradación:
-- Prometheus query for RPO tracking (replication lag over time)
-- Use in Grafana dashboard
SELECT
date_trunc('day', timestamp) as day,
avg(value) as avg_lag_seconds,
max(value) as max_lag_seconds,
percentile_cont(0.99) WITHIN GROUP (ORDER BY value) as p99_lag_seconds
FROM prometheus_metrics
WHERE metric_name = 'pg_replication_lag_seconds'
AND timestamp > now() - interval '90 days'
GROUP BY 1
ORDER BY 1;
-- DR test results over time
SELECT
test_date,
rto_target_seconds,
rto_actual_seconds,
rpo_target_seconds,
rpo_actual_seconds,
CASE
WHEN rto_actual_seconds <= rto_target_seconds THEN 'PASS'
ELSE 'FAIL'
END as rto_status,
CASE
WHEN rpo_actual_seconds <= rpo_target_seconds THEN 'PASS'
ELSE 'FAIL'
END as rpo_status
FROM dr_test_results
ORDER BY test_date DESC
LIMIT 12;
Calendario de pruebas de DR
| Trimestre | Escenario | Responsable | RTO objetivo | Última prueba |
|---|---|---|---|---|
| Q1 2026 | Fallo de región primaria | Equipo de plataforma | 4 horas | 2026-01-15 |
| Q2 2026 | Corrupción de base de datos | Equipo DBA | 2 horas | 2026-04-20 |
| Q3 2026 | Partición de red | Equipo de red | 1 hora | Pendiente |
| Q4 2026 | Región completa + restauración desde backup | Equipo SRE | 8 horas | Pendiente |
Un calendario por sí solo no hace real el plan:
- Recorre la línea de tiempo en papel primero. Haz un pase de mesa sobre el cronograma de ejecución antes de tocar infraestructura — los huecos en responsables y accesos salen gratis.
- Incluye pruebas de dependencias de terceros. Proveedores de DNS, CDNs, APIs externas — tu plan de DR depende de todos ellos, así que su failover también necesita un simulacro:
# Prueba de failover de DNS
dig @8.8.8.8 app.example.com +short
# Debería devolver la IP de la región de DR tras el corte
# Prueba de failover de CDN
curl -sI https://app.example.com | grep "x-served-by"
# Debería mostrar el nodo de caché de DR tras el corte
# Prueba del circuit breaker de dependencia de API externa
curl -X POST http://app.internal/admin/test-circuit-breaker \
-d '{"dependency":"payment-gateway","action":"open"}'
Ver también
- AWS Whitepaper: Disaster Recovery of Workloads on AWS: las estrategias de recuperación que este plan pone a prueba.
- Guía de recuperación ante desastres: RTO, RPO y los runbooks que se ejercitan aquí.
- Plantilla de plan de recuperación ante desastres: el documento bajo prueba.
- Guía de chaos engineering: si quieres la versión de game day sin avisar.
- Recursos ejecutables del companion:
dr_test_runner.py,game-day.sh, el SQL de verificación de integridad y un calendario de pruebas.
Preguntas frecuentes
¿Con qué frecuencia deberíamos ejecutar pruebas de DR?
Lo anual es el suelo de cumplimiento; lo trimestral mantiene al equipo honesto. Cada cambio de infraestructura que toque una ruta de recuperación se gana su propio simulacro. Los equipos con automatización madura van más allá y corren ejercicios de caos mensuales.
¿Cómo medimos el RTO con honestidad?
Desde la declaración de desastre hasta el servicio validado, no desde que arranca el script. El tiempo de detección cuenta: si el monitoreo tardó 4 minutos en avisar a alguien, esos 4 minutos son parte de tu RTO. Mide la versión que viven tus usuarios.
¿Por qué probar restauraciones desde backup y no solo failover?
La replicación puede estar perfectamente sana mientras tus backups están corruptos en silencio. Una prueba de failover demuestra que la réplica funciona; una de restauración demuestra que sobrevives al día en que la réplica no existe. Ejecuta ambas, en calendarios distintos.
¿Qué pasa si la prueba de DR falla catastróficamente?
Esa es información valiosa. La prueba ha revelado que tu plan de DR no funciona — mejor descubrirlo en una ventana controlada que durante un desastre real. Pausa la prueba, restaura producción, corrige los problemas y reprograma.
¿Necesitamos notificar a clientes sobre pruebas de DR?
Si la prueba toca algo que los clientes pueden ver, sí — prográmala dentro de una ventana de mantenimiento y avisa con antelación. Si es solo interna, no hay nadie fuera del equipo a quien avisar.
¿Quién debería ser responsable de una prueba de DR?
Un líder de prueba con nombre y con autoridad para dar por terminado el simulacro. Rotar ese rol entre trimestres funciona bien; lo que no funciona es un simulacro sin un único dueño — esos terminan en un informe que nadie abre.
Recursos Relacionados
Runbook de Failover de Base de Datos
Un runbook paso a paso para ejecutar procedimientos de failover de base de datos de forma segura con tiempo de inactividad y pérdida de datos mínimos.
DocRunbook de Rollback de Despliegues
Runbook para revertir despliegues fallidos: triggers, comandos de kubectl/Helm/ArgoCD, rollback de migraciones de base de datos y checklists de verificación.
DocPlantilla de Runbook de Migración de Datos con Rollback
Usa esta plantilla de runbook para migrar datos de forma segura. Incluye pre-chequeos, pasos de ejecución, rollback y validación post-migración.
DocPlantilla de Comunicacion de Incidentes
Una plantilla para notificar a stakeholders durante interrupciones de produccion con mensajes pre-redactados para cada nivel de severidad y tipo de audiencia.
DocPlaybook de Respuesta a Violaciones de Datos
Playbook paso a paso para responder a incidentes de seguridad con acceso no autorizado a datos, desde la detección hasta la notificación y remediación.
DocPlantilla de Gestión de Vulnerabilidades
Plantilla repetible para rastrear vulnerabilidades, asignar responsables de remediación, definir plazos de parches y reportar riesgos de seguridad a los stakeholders.