TL;DR

  • Usa AWS Backup restore testing para validar automáticamente que los backups cumplen objetivos RTO/RPO—backups no probados no son backups
  • Automatiza testing DR con Terraform: levanta infraestructura de recuperación, valida funcionalidad, destruye—paga solo por la duración del test
  • Prueba procedimientos de recuperación trimestralmente como mínimo; documenta cada paso y haz que personal que no escribió la documentación realice la restauración

Ideal para: Equipos con cargas de trabajo en producción que requieren capacidades de recuperación documentadas y requisitos de compliance No recomendado si: Ejecutas aplicaciones stateless sin datos persistentes (solo redespliega desde IaC) Tiempo de lectura: 15 minutos

Los backups que no se prueban minan la confianza en la recuperabilidad de datos. Estudios muestran que 34% de las empresas no prueban sus backups, y de las que lo hacen, 77% han encontrado fallos durante pruebas de restauración. Tu plan DR es tan bueno como tu última prueba exitosa.

Para testing de infraestructura relacionado, consulta Estrategias de Testing de Terraform y Testing de Infraestructura AWS.

Enfoques Asistidos por IA

Las herramientas de IA destacan generando planes de pruebas DR y analizando procedimientos de recuperación en busca de brechas.

Generando planes comprehensivos de pruebas DR:

Crea un plan de pruebas de disaster recovery para una aplicación web de tres capas:

- Capa web: Auto Scaling group con ALB
- Capa app: Servicios ECS Fargate
- Capa datos: RDS PostgreSQL Multi-AZ con Read Replicas

Incluye pruebas para:

1. Fallo de una sola AZ (verificar failover automático)
2. Fallo completo de región (verificar DR cross-region)
3. Corrupción de datos (verificar recuperación point-in-time)
4. Escenario de ransomware (verificar recuperación de backup aislado)

Para cada escenario, especifica: método de activación, RTO/RPO esperado,
pasos de validación, procedimiento de rollback y criterios de éxito.

Creando infraestructura DR con Terraform:

Escribe módulos Terraform para disaster recovery en AWS con:

1. Replicación RDS cross-region con failover automatizado
2. Replicación S3 cross-region con versionado
3. Health checks de Route 53 con failover DNS
4. Función Lambda activada por alarma CloudWatch para iniciar DR

Incluye variables para objetivos RTO/RPO y outputs para monitoreo.
Muestra cómo probar el failover sin afectar producción.

Analizando brechas de cobertura de backup:

Revisa esta configuración de backup AWS en busca de brechas:

Plan de Backup:

- RDS: Snapshots diarios, retención 7 días
- EBS: Snapshots semanales, retención 30 días
- S3: Sin backup (solo replicación cross-region)
- DynamoDB: Backups on-demand antes de deployments

Requisitos de aplicación:

- RPO: 1 hora para base de datos, 4 horas para almacenamiento de archivos
- RTO: 2 horas para recuperación completa
- Compliance: SOC 2, requiere retención de 90 días

Identifica brechas, problemas de compliance y recomendaciones.

Cuándo Usar Diferentes Enfoques de Testing

Framework de Decisión de Estrategia de Testing

Tipo de TestFrecuenciaQué ValidaImpacto en Producción
Validación de backupDiario (automatizado)Backups son restaurablesNinguno
Failover de componentesMensualRecuperación de servicio individualMínimo (usa réplicas)
Ejercicio DR completoTrimestralProcedimiento completo de recuperaciónVentana de mantenimiento programada
Chaos engineeringContinuoResiliencia del sistemaRadio de impacto controlado
Ejercicio de mesaAnualProcedimientos humanos y comunicaciónNinguno

Matriz de Estrategia RTO/RPO

EstrategiaRTORPOCostoCaso de Uso
Backup & RestoreHorasHoras$Sistemas no críticos
Pilot Light10-30 minMinutos$$Sistemas de negocio core
Warm StandbyMinutosCasi cero$$$Aplicaciones críticas
Multi-Site ActiveCasi ceroCasi cero$$$$Misión crítica

AWS Backup Restore Testing

Validación Automatizada de Backups

# Crear plan de testing de restauración
aws backup create-restore-testing-plan \
  --restore-testing-plan-name "daily-validation" \
  --schedule-expression "cron(0 6 * * ? *)" \
  --start-window-hours 1 \
  --recovery-point-selection '{
    "Algorithm": "LATEST_WITHIN_WINDOW",
    "RecoveryPointTypes": ["SNAPSHOT"],
    "SelectionWindowDays": 1
  }'

# Agregar RDS al plan de testing
aws backup create-restore-testing-selection \
  --restore-testing-plan-name "daily-validation" \
  --restore-testing-selection-name "rds-validation" \
  --protected-resource-type "RDS" \
  --iam-role-arn "arn:aws:iam::123456789012:role/BackupRestoreRole" \
  --protected-resource-conditions '{
    "StringEquals": [
      {"Key": "aws:ResourceTag/Environment", "Value": "production"}
    ]
  }'

Terraform para AWS Backup

# modules/backup-testing/main.tf

resource "aws_backup_plan" "production" {
  name = "production-backup-plan"

  rule {
    rule_name         = "daily-backups"
    target_vault_name = aws_backup_vault.production.name
    schedule          = "cron(0 5 * * ? *)"

    lifecycle {
      delete_after = 90  # Compliance SOC 2
    }

    copy_action {
      destination_vault_arn = aws_backup_vault.dr_region.arn
      lifecycle {
        delete_after = 90
      }
    }
  }

  rule {
    rule_name         = "hourly-backups"
    target_vault_name = aws_backup_vault.production.name
    schedule          = "cron(0 * * * ? *)"

    lifecycle {
      delete_after = 7
    }
  }
}

resource "aws_backup_selection" "production_rds" {
  iam_role_arn = aws_iam_role.backup.arn
  name         = "production-rds"
  plan_id      = aws_backup_plan.production.id

  selection_tag {
    type  = "STRINGEQUALS"
    key   = "Backup"
    value = "true"
  }

  resources = [
    "arn:aws:rds:*:*:db:*",
    "arn:aws:rds:*:*:cluster:*"
  ]
}

# Plan de testing de restauración
resource "aws_backup_restore_testing_plan" "validation" {
  name = "automated-restore-validation"

  recovery_point_selection {
    algorithm             = "LATEST_WITHIN_WINDOW"
    recovery_point_types  = ["SNAPSHOT"]
    selection_window_days = 1
  }

  schedule_expression = "cron(0 8 * * ? *)"
}

Script Python para Validación de Backup

import boto3
import time
from datetime import datetime, timedelta

class BackupValidator:
    def __init__(self, region='us-east-1'):
        self.backup = boto3.client('backup', region_name=region)
        self.rds = boto3.client('rds', region_name=region)

    def validate_rds_backup(self, db_identifier, max_age_hours=24):
        """Validar que existe backup RDS y es restaurable."""
        # Obtener snapshot más reciente
        snapshots = self.rds.describe_db_snapshots(
            DBInstanceIdentifier=db_identifier,
            SnapshotType='automated'
        )['DBSnapshots']

        if not snapshots:
            return {'status': 'FAILED', 'reason': 'No se encontraron snapshots'}

        latest = max(snapshots, key=lambda x: x['SnapshotCreateTime'])
        age = datetime.now(latest['SnapshotCreateTime'].tzinfo) - latest['SnapshotCreateTime']

        if age > timedelta(hours=max_age_hours):
            return {
                'status': 'WARNING',
                'reason': f'Snapshot más reciente tiene {age.total_seconds()/3600:.1f} horas'
            }

        # Verificar que snapshot está disponible
        if latest['Status'] != 'available':
            return {'status': 'FAILED', 'reason': f"Estado de snapshot: {latest['Status']}"}

        return {
            'status': 'PASSED',
            'snapshot_id': latest['DBSnapshotIdentifier'],
            'snapshot_age_hours': age.total_seconds() / 3600
        }

    def test_restore(self, snapshot_id, test_db_identifier):
        """Realizar prueba real de restauración."""
        try:
            # Iniciar restauración
            self.rds.restore_db_instance_from_db_snapshot(
                DBInstanceIdentifier=test_db_identifier,
                DBSnapshotIdentifier=snapshot_id,
                DBInstanceClass='db.t3.micro',  # Mínimo para testing
                PubliclyAccessible=False,
                Tags=[{'Key': 'Purpose', 'Value': 'DR-Test'}]
            )

            # Esperar restauración
            start_time = time.time()
            waiter = self.rds.get_waiter('db_instance_available')
            waiter.wait(DBInstanceIdentifier=test_db_identifier)
            restore_time = time.time() - start_time

            return {
                'status': 'PASSED',
                'restore_time_seconds': restore_time,
                'meets_rto': restore_time < 7200  # RTO de 2 horas
            }

        finally:
            # Limpiar instancia de prueba
            try:
                self.rds.delete_db_instance(
                    DBInstanceIdentifier=test_db_identifier,
                    SkipFinalSnapshot=True,
                    DeleteAutomatedBackups=True
                )
            except Exception:
                pass

# Uso en pytest
def test_production_db_backup():
    validator = BackupValidator()
    result = validator.validate_rds_backup('production-db')
    assert result['status'] in ['PASSED', 'WARNING'], f"Validación de backup falló: {result}"

Testing de Azure Site Recovery

Test Failover con Azure CLI

# Crear plan de recuperación
az site-recovery recovery-plan create \
  --resource-group rg-dr \
  --vault-name vault-dr \
  --name "webapp-recovery-plan" \
  --primary-zone "East US" \
  --recovery-zone "West US" \
  --failover-deployment-model "Resource"

# Ejecutar test failover (no disruptivo)
az site-recovery recovery-plan test-failover \
  --resource-group rg-dr \
  --vault-name vault-dr \
  --name "webapp-recovery-plan" \
  --failover-direction "PrimaryToRecovery" \
  --network-id "/subscriptions/.../test-vnet"

# Validar entorno de prueba
az vm list --resource-group rg-dr-test --output table

# Limpiar test failover
az site-recovery recovery-plan test-failover-cleanup \
  --resource-group rg-dr \
  --vault-name vault-dr \
  --name "webapp-recovery-plan"

Terraform para Azure DR

# modules/azure-dr/main.tf

resource "azurerm_recovery_services_vault" "dr" {
  name                = "vault-dr-${var.environment}"
  location            = var.dr_region
  resource_group_name = azurerm_resource_group.dr.name
  sku                 = "Standard"

  soft_delete_enabled = true
}

resource "azurerm_site_recovery_fabric" "primary" {
  name                = "fabric-primary"
  resource_group_name = azurerm_resource_group.dr.name
  recovery_vault_name = azurerm_recovery_services_vault.dr.name
  location            = var.primary_region
}

resource "azurerm_site_recovery_fabric" "secondary" {
  name                = "fabric-secondary"
  resource_group_name = azurerm_resource_group.dr.name
  recovery_vault_name = azurerm_recovery_services_vault.dr.name
  location            = var.dr_region
}

resource "azurerm_site_recovery_replication_policy" "policy" {
  name                                                 = "replication-policy"
  resource_group_name                                  = azurerm_resource_group.dr.name
  recovery_vault_name                                  = azurerm_recovery_services_vault.dr.name
  recovery_point_retention_in_minutes                  = 1440  # 24 horas
  application_consistent_snapshot_frequency_in_minutes = 60    # RPO 1 hora
}

# Política de backup para VMs
resource "azurerm_backup_policy_vm" "daily" {
  name                = "daily-backup-policy"
  resource_group_name = azurerm_resource_group.dr.name
  recovery_vault_name = azurerm_recovery_services_vault.dr.name

  backup {
    frequency = "Daily"
    time      = "23:00"
  }

  retention_daily {
    count = 30
  }

  retention_weekly {
    count    = 12
    weekdays = ["Sunday"]
  }

  retention_monthly {
    count    = 12
    weekdays = ["Sunday"]
    weeks    = ["First"]
  }
}

Testing DR Automatizado con Terraform

Entorno de Prueba DR Efímero

# dr-test/main.tf - Levantar, probar, destruir

variable "run_dr_test" {
  description = "Establecer a true para ejecutar prueba DR"
  type        = bool
  default     = false
}

# Solo crear recursos durante prueba DR
resource "aws_db_instance" "dr_test" {
  count = var.run_dr_test ? 1 : 0

  identifier     = "dr-test-${formatdate("YYYYMMDD", timestamp())}"
  instance_class = "db.t3.medium"

  # Restaurar desde snapshot más reciente
  snapshot_identifier = data.aws_db_snapshot.latest.id

  vpc_security_group_ids = [aws_security_group.dr_test[0].id]
  db_subnet_group_name   = aws_db_subnet_group.dr_test[0].name

  skip_final_snapshot = true
  deletion_protection = false

  tags = {
    Purpose   = "DR-Test"
    AutoClean = "true"
  }
}

data "aws_db_snapshot" "latest" {
  db_instance_identifier = var.production_db_identifier
  most_recent            = true
}

# Probar conectividad de aplicación
resource "null_resource" "validate_dr" {
  count = var.run_dr_test ? 1 : 0

  depends_on = [aws_db_instance.dr_test]

  provisioner "local-exec" {
    command = <<-EOT
      # Esperar que DB esté lista
      aws rds wait db-instance-available \
        --db-instance-identifier ${aws_db_instance.dr_test[0].identifier}

      # Ejecutar test de conectividad
      python3 scripts/validate_dr.py \
        --endpoint ${aws_db_instance.dr_test[0].endpoint} \
        --expected-tables 50 \
        --expected-rows 1000000

      # Registrar RTO
      echo "Prueba DR completada. RTO: $(cat /tmp/dr_rto.txt)"
    EOT
  }
}

output "dr_test_results" {
  value = var.run_dr_test ? {
    db_endpoint  = aws_db_instance.dr_test[0].endpoint
    restore_time = timestamp()
    status       = "Entorno de prueba listo"
  } : null
}

Workflow GitHub Actions para Prueba DR

name: Prueba DR Trimestral

on:
  schedule:

    - cron: '0 6 1 */3 *'  # Primer día del trimestre a las 6 AM
  workflow_dispatch:
    inputs:
      test_type:
        description: 'Tipo de prueba DR'
        required: true
        default: 'backup-restore'
        type: choice
        options:

          - backup-restore
          - failover
          - full-dr

jobs:
  dr-test:
    runs-on: ubuntu-latest
    environment: dr-test

    steps:

      - uses: actions/checkout@v4

      - name: Configurar credenciales AWS
        uses: aws-actions/configure-aws-credentials@v4
        with:
          role-to-assume: ${{ secrets.DR_TEST_ROLE_ARN }}
          aws-region: us-west-2  # Región DR

      - name: Setup Terraform
        uses: hashicorp/setup-terraform@v3

      - name: Iniciar Prueba DR
        id: start_test
        run: |
          START_TIME=$(date +%s)
          echo "start_time=$START_TIME" >> $GITHUB_OUTPUT
          echo "Prueba DR iniciada a las $(date)"

      - name: Desplegar Entorno DR
        run: |
          cd terraform/dr-test
          terraform init
          terraform apply -auto-approve -var="run_dr_test=true"

      - name: Validar Recuperación
        id: validate
        run: |
          cd terraform/dr-test
          DB_ENDPOINT=$(terraform output -raw dr_test_db_endpoint)

          # Probar conectividad de base de datos
          python3 scripts/validate_dr.py \
            --endpoint "$DB_ENDPOINT" \
            --test-queries \
            --output-file dr-results.json

          # Calcular RTO
          END_TIME=$(date +%s)
          START_TIME=${{ steps.start_test.outputs.start_time }}
          RTO=$((END_TIME - START_TIME))
          echo "rto_seconds=$RTO" >> $GITHUB_OUTPUT

      - name: Limpiar Prueba DR
        if: always()
        run: |
          cd terraform/dr-test
          terraform destroy -auto-approve -var="run_dr_test=true"

      - name: Generar Reporte
        run: |
          cat << EOF > dr-report.md
          # Reporte de Prueba DR - $(date +%Y-%m-%d)

          ## Resultados
          - **Tipo de Prueba**: ${{ inputs.test_type || 'backup-restore' }}
          - **RTO Real**: ${{ steps.validate.outputs.rto_seconds }} segundos
          - **RTO Objetivo**: 7200 segundos (2 horas)
          - **Estado**: $([[ ${{ steps.validate.outputs.rto_seconds }} -lt 7200 ]] && echo "APROBADO" || echo "FALLIDO")

          ## Detalles de Validación
          $(cat dr-results.json | jq -r '.summary')
          EOF

      - name: Subir Reporte
        uses: actions/upload-artifact@v4
        with:
          name: dr-test-report
          path: dr-report.md

      - name: Notificar en Fallo
        if: failure()
        run: |
          # Enviar alerta si prueba DR falló
          aws sns publish \
            --topic-arn ${{ secrets.ALERTS_TOPIC_ARN }} \
            --message "Prueba DR FALLIDA. Revisión requerida."

Midiendo el Éxito

MétricaObjetivoCómo Rastrear
Tasa de éxito de backup100%Dashboards AWS Backup/Azure Backup
Tasa de éxito de pruebas de restauración100%Resultados de tests automatizados
RTO real vs ObjetivoReal < ObjetivoMediciones de pruebas DR
RPO real vs ObjetivoReal < ObjetivoAnálisis de timestamps de backup
Frecuencia de pruebas DRTrimestral mínimoSeguimiento en calendario
Vigencia de documentaciónActualizada después de cada pruebaFecha de última modificación

Señales de que tu testing DR no funciona:

  • Pruebas de restauración consistentemente omitidas por “restricciones de tiempo”
  • Mediciones de RTO no incluyen tiempo de coordinación humana
  • Backups existen pero nadie conoce el procedimiento de restauración
  • Documentación DR referencia infraestructura obsoleta
  • Lag de replicación cross-region no monitoreado

Conclusión

El testing efectivo de backup y DR requiere automatización y validación regular:

  1. Automatiza validación de backup con AWS Backup restore testing o herramientas similares
  2. Prueba restauraciones regularmente—ejercicios DR completos trimestralmente como mínimo
  3. Mide RTO/RPO durante pruebas reales, no estimaciones
  4. Documenta todo y haz que personal no familiarizado con procedimientos ejecute las pruebas
  5. Usa Terraform para crear entornos de prueba efímeros de forma rentable

La idea clave: backups no probados no son backups. La única forma de saber que tu plan DR funciona es ejecutarlo regularmente. Automatiza lo que puedas, pero asegura que los procedimientos humanos también se prueben.

Ver También

Recursos Oficiales