Lab 5: Simulación de Incidente
Lab 5: Simulación de Incidente y Verificación del Failover
¡Llegó el momento de la verdad! Vamos a simular una caída del sitio primario y verificar que todo el flujo funciona: detección → notificación → failover automático.
El plan de la simulación
Hay dos formas de simular la caída:
| Método | Descripción | Velocidad |
|---|---|---|
| A. Detener nginx | Parar el servicio nginx en EC2 | Health Check falla en ~2 min |
| B. Forzar alarma CloudWatch | Poner la alarma directamente en ALARM | Inmediato |
Usaremos ambos: primero el método B para ver el flujo completo rápido, y luego el A para la experiencia “real”.
Pre-check: Estado inicial
Antes de simular, confirma que todo está en estado normal:
export AWS_REGION="us-east-1"
export AWS_ACCOUNT_ID=$(aws sts get-caller-identity --query Account --output text)
# Recuperar valores
export PRIMARY_RC_ARN=$(aws cloudformation describe-stacks \
--stack-name route53-arc-routing --region $AWS_REGION \
--query 'Stacks[0].Outputs[?OutputKey==`PrimaryRoutingControlArn`].OutputValue' \
--output text)
export SECONDARY_RC_ARN=$(aws cloudformation describe-stacks \
--stack-name route53-arc-routing --region $AWS_REGION \
--query 'Stacks[0].Outputs[?OutputKey==`SecondaryRoutingControlArn`].OutputValue' \
--output text)
export ARC_CLUSTER_ARN=$(aws cloudformation describe-stacks \
--stack-name route53-arc-routing --region $AWS_REGION \
--query 'Stacks[0].Outputs[?OutputKey==`ARCClusterArn`].OutputValue' \
--output text)
export CLUSTER_ENDPOINT=$(aws route53-recovery-control-config describe-cluster \
--cluster-arn $ARC_CLUSTER_ARN --region $AWS_REGION \
--query 'Cluster.ClusterEndpoints[0].Endpoint' --output text)
export PRIMARY_ALB_DNS=$(aws cloudformation describe-stacks \
--stack-name route53-arc-primary --region $AWS_REGION \
--query 'Stacks[0].Outputs[?OutputKey==`ALBDNSName`].OutputValue' \
--output text)
export LAMBDA_NAME=$(aws cloudformation describe-stacks \
--stack-name route53-arc-automation --region $AWS_REGION \
--query 'Stacks[0].Outputs[?OutputKey==`LambdaName`].OutputValue' \
--output text)
export EC2_ID=$(aws cloudformation describe-stacks \
--stack-name route53-arc-primary --region $AWS_REGION \
--query 'Stacks[0].Outputs[?OutputKey==`EC2InstanceID`].OutputValue' \
--output text)
echo "=== Estado inicial ==="
echo "ALB DNS: $PRIMARY_ALB_DNS"
echo "EC2 ID: $EC2_ID"
echo "Cluster EP: $CLUSTER_ENDPOINT"
echo ""
# Verificar Routing Controls
echo "--- Routing Control States ---"
aws route53-recovery-cluster get-routing-control-state \
--routing-control-arn $PRIMARY_RC_ARN \
--endpoint-url $CLUSTER_ENDPOINT \
--region $AWS_REGION \
--query 'RoutingControlState' --output text \
| xargs -I{} echo "Primario: {}"
aws route53-recovery-cluster get-routing-control-state \
--routing-control-arn $SECONDARY_RC_ARN \
--endpoint-url $CLUSTER_ENDPOINT \
--region $AWS_REGION \
--query 'RoutingControlState' --output text \
| xargs -I{} echo "Secundario: {}"
# Verificar que el sitio primario responde
echo ""
echo "--- Sitio primario ---"
curl -s -o /dev/null -w "HTTP %{http_code} en %{time_total}s\n" \
http://$PRIMARY_ALB_DNS/El estado esperado antes de simular:
- Primario:
On - Secundario:
Off - ALB responde:
HTTP 200
Simulación A: Forzar alarma CloudWatch (método rápido)
Este método dispara el failover inmediatamente sin esperar al Health Check:
# Nombre de la alarma con SNS
ALARM_NAME="route53-arc-primary-unhealthy-with-sns"
echo "=== INICIO SIMULACIÓN: Forzando alarma CloudWatch ==="
echo "Hora de inicio: $(date)"
# Poner la alarma en estado ALARM manualmente
aws cloudwatch set-alarm-state \
--alarm-name $ALARM_NAME \
--state-value ALARM \
--state-reason "Simulación de incidente – Workshop roLearning" \
--region $AWS_REGION
echo "✅ Alarma forzada a estado ALARM"
echo "⏳ Esperando que Lambda reaccione (10-15 segundos)..."
sleep 15Verificar que Lambda ejecutó el failover:
echo "=== Verificando resultado del failover ==="
# Estado de los Routing Controls después del failover
echo "--- Routing Control States (post-failover) ---"
aws route53-recovery-cluster get-routing-control-state \
--routing-control-arn $PRIMARY_RC_ARN \
--endpoint-url $CLUSTER_ENDPOINT \
--region $AWS_REGION \
--query 'RoutingControlState' --output text \
| xargs -I{} echo "Primario: {} (esperado: Off)"
aws route53-recovery-cluster get-routing-control-state \
--routing-control-arn $SECONDARY_RC_ARN \
--endpoint-url $CLUSTER_ENDPOINT \
--region $AWS_REGION \
--query 'RoutingControlState' --output text \
| xargs -I{} echo "Secundario: {} (esperado: On)"Verificar los logs de Lambda:
echo ""
echo "--- Logs de Lambda (últimas invocaciones) ---"
LOG_GROUP="/aws/lambda/$LAMBDA_NAME"
# Obtener el log stream más reciente
LATEST_STREAM=$(aws logs describe-log-streams \
--log-group-name $LOG_GROUP \
--order-by LastEventTime \
--descending \
--max-items 1 \
--region $AWS_REGION \
--query 'logStreams[0].logStreamName' \
--output text 2>/dev/null)
if [ "$LATEST_STREAM" != "None" ] && [ -n "$LATEST_STREAM" ]; then
aws logs get-log-events \
--log-group-name $LOG_GROUP \
--log-stream-name "$LATEST_STREAM" \
--region $AWS_REGION \
--query 'events[*].message' \
--output text | tail -20
else
echo "⚠️ No hay logs todavía. Lambda aún no fue invocada."
fiSimulación B: Detener nginx en EC2 (método “real”)
Primero, restablece el estado normal del Lab A:
# Restaurar estado normal antes de probar el método B
aws route53-recovery-cluster update-routing-control-states \
--update-routing-control-state-entries \
"[{\"RoutingControlArn\":\"$PRIMARY_RC_ARN\",\"RoutingControlState\":\"On\"},
{\"RoutingControlArn\":\"$SECONDARY_RC_ARN\",\"RoutingControlState\":\"Off\"}]" \
--endpoint-url $CLUSTER_ENDPOINT \
--region $AWS_REGION
# Restablecer la alarma
aws cloudwatch set-alarm-state \
--alarm-name $ALARM_NAME \
--state-value OK \
--state-reason "Estado restaurado para nueva simulación" \
--region $AWS_REGION
echo "✅ Estado restaurado a normal"
sleep 5Ahora detén nginx en la instancia EC2 usando SSM:
echo "=== INICIO SIMULACIÓN B: Deteniendo nginx en EC2 ==="
echo "Hora de inicio: $(date)"
# Enviar comando via SSM (sin Key Pair necesario)
COMMAND_ID=$(aws ssm send-command \
--instance-ids $EC2_ID \
--document-name AWS-RunShellScript \
--parameters 'commands=["systemctl stop nginx && echo NGINX_STOPPED"]' \
--region $AWS_REGION \
--query 'Command.CommandId' \
--output text)
echo "Comando SSM enviado: $COMMAND_ID"
echo "Esperando confirmación..."
sleep 10
# Verificar resultado del comando
aws ssm get-command-invocation \
--command-id $COMMAND_ID \
--instance-id $EC2_ID \
--region $AWS_REGION \
--query '{Status: Status, Output: StandardOutputContent}' \
--output jsonMonitorear el Health Check en tiempo real:
echo ""
echo "=== Monitoreando Health Check (puede tardar ~2-3 min) ==="
echo "Presiona Ctrl+C para detener el monitoreo"
echo ""
HC_ID=$(aws cloudformation describe-stacks \
--stack-name route53-arc-routing --region $AWS_REGION \
--query 'Stacks[0].Outputs[?OutputKey==`HealthCheckId`].OutputValue' \
--output text)
for i in $(seq 1 20); do
# Estado del Health Check
HC_STATUS=$(aws route53 get-health-check-status \
--health-check-id $HC_ID \
--query 'HealthCheckObservations[0].StatusReport.Status' \
--output text 2>/dev/null | cut -c1-30)
# Estado del ALB
HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" \
--connect-timeout 3 --max-time 5 \
http://$PRIMARY_ALB_DNS/ 2>/dev/null)
# Estado de los Routing Controls
PRIMARY_STATE=$(aws route53-recovery-cluster get-routing-control-state \
--routing-control-arn $PRIMARY_RC_ARN \
--endpoint-url $CLUSTER_ENDPOINT \
--region $AWS_REGION \
--query 'RoutingControlState' --output text 2>/dev/null)
SECONDARY_STATE=$(aws route53-recovery-cluster get-routing-control-state \
--routing-control-arn $SECONDARY_RC_ARN \
--endpoint-url $CLUSTER_ENDPOINT \
--region $AWS_REGION \
--query 'RoutingControlState' --output text 2>/dev/null)
echo "[$(date '+%H:%M:%S')] HC: $HC_STATUS | ALB: HTTP$HTTP_CODE | Primary RC: $PRIMARY_STATE | Secondary RC: $SECONDARY_STATE"
# Si el failover ocurrió, salir
if [ "$PRIMARY_STATE" = "Off" ] && [ "$SECONDARY_STATE" = "On" ]; then
echo ""
echo "🎉 ¡FAILOVER DETECTADO! El sistema reaccionó automáticamente."
break
fi
sleep 30
done✅ Verificación del Lab 5
echo "=== Verificación Lab 5: Simulación de Incidente ==="
echo ""
# 1. Routing Controls en estado de failover
PRIMARY_STATE=$(aws route53-recovery-cluster get-routing-control-state \
--routing-control-arn $PRIMARY_RC_ARN \
--endpoint-url $CLUSTER_ENDPOINT \
--region $AWS_REGION \
--query 'RoutingControlState' --output text 2>/dev/null)
SECONDARY_STATE=$(aws route53-recovery-cluster get-routing-control-state \
--routing-control-arn $SECONDARY_RC_ARN \
--endpoint-url $CLUSTER_ENDPOINT \
--region $AWS_REGION \
--query 'RoutingControlState' --output text 2>/dev/null)
[ "$PRIMARY_STATE" = "Off" ] \
&& echo "✅ Routing Control Primario: $PRIMARY_STATE (failover activo)" \
|| echo "⚠️ Routing Control Primario: $PRIMARY_STATE"
[ "$SECONDARY_STATE" = "On" ] \
&& echo "✅ Routing Control Secundario: $SECONDARY_STATE (tráfico redirigido)" \
|| echo "⚠️ Routing Control Secundario: $SECONDARY_STATE"
# 2. Lambda fue invocada
LAMBDA_INVOCATIONS=$(aws cloudwatch get-metric-statistics \
--namespace AWS/Lambda \
--metric-name Invocations \
--dimensions Name=FunctionName,Value=$LAMBDA_NAME \
--start-time $(date -u -d '1 hour ago' +%Y-%m-%dT%H:%M:%S 2>/dev/null || date -u -v-1H +%Y-%m-%dT%H:%M:%S) \
--end-time $(date -u +%Y-%m-%dT%H:%M:%S) \
--period 3600 \
--statistics Sum \
--region $AWS_REGION \
--query 'Datapoints[0].Sum' \
--output text 2>/dev/null)
echo ""
echo "📊 Invocaciones Lambda última hora: ${LAMBDA_INVOCATIONS:-0}"
echo ""
echo "📬 Recuerda revisar tu email para la notificación SNS."
echo ""
echo "Para continuar al failback → Lab 6"Consejo
¿Recibiste el email? Debería haber llegado un mensaje con asunto “🚨 [FAILOVER ACTIVADO] Sitio primario caído” con todos los detalles del evento. Si no llegó, revisa la suscripción SNS en la consola.
Siguiente paso → Lab 6: Failback