Lab práctico · Semana 4: Bases de datos: RDS, Aurora, DynamoDB, ElastiCache y compañía
RDS for MySQL Multi-AZ - failover, réplica de lectura, promoción y backups
Qué vas a construir
Una base de datos RDS for MySQL cifrada, privada y Multi-AZ (Multi-AZ DB instance), con storage autoscaling y la contraseña maestra gestionada en Secrets Manager. Sobre ella:
- Forzarás un failover y verás cómo el primario cambia de AZ sin cambiar el endpoint.
- Crearás una réplica de lectura, mirarás su retraso y la promocionarás a instancia independiente.
- Harás un snapshot manual y comprobarás el punto de restauración más reciente para PITR.
flowchart LR
APP["Endpoint DNS lab07-mysql"] --> P["Primario (AZ a)"]
P -->|"síncrona"| S["Standby (AZ b)"]
P -->|"asíncrona"| R["Read replica lab07-replica"]
P -.->|"backups automáticos + PITR"| B["Snapshots en S3 gestionado"]
Antes de empezar
- Usuario de IAM Identity Center o IAM con MFA (no el raíz). CloudShell en eu-south-2.
- Nadie se conectará a la base de datos: la dejamos sin acceso público y con un security group sin reglas de entrada. Todo se comprueba con la API de RDS.
- Coste orientativo en eu-south-2 (septiembre de 2026):
db.t4g.microMySQL 0,035 USD/h en Multi-AZ y 0,018 USD/h en Single-AZ (la réplica), más almacenamiento (≈ 0,127 USD por GB-mes, proporcional a las horas) y el secreto de Secrets Manager (se prorratea). Si usas los créditos del plan gratuito, «crear una base de datos RDS» es además una de las actividades que te devuelven crédito.
Paso 0: variables y security group
export AWS_REGION=eu-south-2
VPC=$(aws ec2 describe-vpcs --filters Name=is-default,Values=true \
--query 'Vpcs[0].VpcId' --output text)
SG_DB=$(aws ec2 create-security-group --group-name lab07-rds \
--description "RDS lab07 sin entrada" --vpc-id $VPC --query GroupId --output text)
DB=lab07-mysql
En un diseño real añadirías una regla de entrada al puerto 3306 solo desde el security group de la aplicación.
Paso 1: crear la instancia Multi-AZ
aws rds create-db-instance \
--db-instance-identifier $DB \
--engine mysql \
--db-instance-class db.t4g.micro \
--storage-type gp3 \
--allocated-storage 20 \
--max-allocated-storage 30 \
--master-username admin \
--manage-master-user-password \
--multi-az \
--backup-retention-period 1 \
--storage-encrypted \
--no-publicly-accessible \
--vpc-security-group-ids $SG_DB \
--tags Key=proyecto,Value=lab07
aws rds wait db-instance-available --db-instance-identifier $DB
Qué hace cada opción clave:
--multi-az: standby síncrono en otra AZ (no sirve lecturas).--max-allocated-storage 30: activa storage autoscaling hasta 30 GiB.--manage-master-user-password: RDS genera la contraseña y la guarda (y rota) en Secrets Manager; nunca la ves en claro.--backup-retention-period 1: backups automáticos de 1 día (necesarios para PITR y para crear réplicas).--storage-encrypted: cifrado con la clave administradaaws/rds. Solo se puede elegir al crear.
La creación tarda de 10 a 20 minutos. Aprovecha para repasar la tabla Multi-AZ del módulo.
Paso 2: inspeccionar
aws rds describe-db-instances --db-instance-identifier $DB \
--query 'DBInstances[0].{Primario:AvailabilityZone,Standby:SecondaryAvailabilityZone,MultiAZ:MultiAZ,Endpoint:Endpoint.Address,Cifrado:StorageEncrypted,MaxGiB:MaxAllocatedStorage,Secreto:MasterUserSecret.SecretArn}'
ENDPOINT=$(aws rds describe-db-instances --db-instance-identifier $DB \
--query 'DBInstances[0].Endpoint.Address' --output text)
getent hosts $ENDPOINT
Anota la AZ del primario y la IP privada a la que resuelve el endpoint.
Paso 3: forzar un failover
aws rds reboot-db-instance --db-instance-identifier $DB --force-failover
aws rds wait db-instance-available --db-instance-identifier $DB
aws rds describe-db-instances --db-instance-identifier $DB \
--query 'DBInstances[0].{Primario:AvailabilityZone,Standby:SecondaryAvailabilityZone}'
getent hosts $ENDPOINT
aws rds describe-events --source-type db-instance --source-identifier $DB \
--duration 60 --query 'Events[].{Hora:Date,Mensaje:Message}' --output table
Observa:
- El primario y el standby han intercambiado las AZ.
- El nombre del endpoint es el mismo, pero ahora resuelve a otra IP: por eso las aplicaciones deben usar el DNS, reintentar conexiones y no cachear el DNS mucho tiempo (o usar RDS Proxy).
- Los eventos muestran el inicio y el fin del failover y cuánto tardó.
Paso 4: réplica de lectura
aws rds create-db-instance-read-replica \
--db-instance-identifier lab07-replica \
--source-db-instance-identifier $DB \
--db-instance-class db.t4g.micro \
--no-publicly-accessible \
--vpc-security-group-ids $SG_DB
aws rds wait db-instance-available --db-instance-identifier lab07-replica
aws rds describe-db-instances --db-instance-identifier $DB \
--query 'DBInstances[0].ReadReplicaDBInstanceIdentifiers'
aws rds describe-db-instances --db-instance-identifier lab07-replica \
--query 'DBInstances[0].{Origen:ReadReplicaSourceDBInstanceIdentifier,AZ:AvailabilityZone,Endpoint:Endpoint.Address}'
La réplica tiene su propio endpoint: la aplicación tiene que enviarle las lecturas explícitamente. Mira su retraso de replicación (métrica ReplicaLag, en segundos):
aws cloudwatch get-metric-statistics --namespace AWS/RDS --metric-name ReplicaLag \
--dimensions Name=DBInstanceIdentifier,Value=lab07-replica \
--start-time $(date -u -d '-30 min' +%Y-%m-%dT%H:%M:%SZ) \
--end-time $(date -u +%Y-%m-%dT%H:%M:%SZ) \
--period 60 --statistics Maximum --query 'Datapoints[].Maximum'
Con la base de datos sin carga, el retraso será cero o casi cero. En producción, un ReplicaLag alto significa que las lecturas de la réplica pueden devolver datos antiguos.
Paso 5: promocionar la réplica
Imagina que la región o el primario se han perdido y decides convertir la réplica en la nueva base de datos principal:
aws rds promote-read-replica --db-instance-identifier lab07-replica \
--backup-retention-period 1
aws rds wait db-instance-available --db-instance-identifier lab07-replica
aws rds describe-db-instances --db-instance-identifier lab07-replica \
--query 'DBInstances[0].{Origen:ReadReplicaSourceDBInstanceIdentifier,MultiAZ:MultiAZ}'
Origen queda vacío: ya es una instancia independiente de lectura-escritura y la replicación está rota para siempre. Es una acción manual de DR o de migración, no un failover automático (eso es Multi-AZ).
Paso 6: snapshot manual y PITR
aws rds create-db-snapshot --db-instance-identifier $DB \
--db-snapshot-identifier lab07-manual-1
aws rds wait db-snapshot-available --db-snapshot-identifier lab07-manual-1
aws rds describe-db-snapshots --db-snapshot-identifier lab07-manual-1 \
--query 'DBSnapshots[0].{Tipo:SnapshotType,Cifrado:Encrypted,GiB:AllocatedStorage}'
aws rds describe-db-instances --db-instance-identifier $DB \
--query 'DBInstances[0].{UltimoRestaurable:LatestRestorableTime,Retencion:BackupRetentionPeriod}'
LatestRestorableTime suele estar a pocos minutos del momento actual: ese es tu RPO con PITR.
Opcional: restaurar a un punto en el tiempo (15-20 minutos más)
aws rds restore-db-instance-to-point-in-time \
--source-db-instance-identifier $DB \
--target-db-instance-identifier lab07-pitr \
--use-latest-restorable-time \
--db-instance-class db.t4g.micro \
--no-multi-az --no-publicly-accessible \
--vpc-security-group-ids $SG_DB
aws rds wait db-instance-available --db-instance-identifier lab07-pitr
Fíjate en que PITR crea una instancia nueva con otro endpoint; nunca restaura encima de la original.
Para pensar: ¿y si la base de datos no estuviera cifrada?
No se puede activar el cifrado en una instancia existente. El procedimiento sería (no lo ejecutes):
aws rds copy-db-snapshot \
--source-db-snapshot-identifier snapshot-sin-cifrar \
--target-db-snapshot-identifier snapshot-cifrado \
--kms-key-id alias/aws/rds
aws rds restore-db-instance-from-db-snapshot \
--db-instance-identifier nueva-cifrada \
--db-snapshot-identifier snapshot-cifrado
Después, cambiar la aplicación al endpoint nuevo.
Comprueba que funciona
-
MultiAZestruey haySecondaryAvailabilityZone. - Tras el failover, primario y standby intercambiaron sus AZ y el endpoint resolvió a otra IP.
- La réplica apareció en
ReadReplicaDBInstanceIdentifiersy, tras promocionarla, dejó de tener origen. - El snapshot manual está
availabley cifrado.
Limpieza
for i in lab07-replica lab07-pitr $DB; do
aws rds delete-db-instance --db-instance-identifier $i \
--skip-final-snapshot --delete-automated-backups 2>/dev/null && echo "Borrando $i"
done
for i in lab07-replica lab07-pitr $DB; do
aws rds wait db-instance-deleted --db-instance-identifier $i 2>/dev/null
done
aws rds delete-db-snapshot --db-snapshot-identifier lab07-manual-1
aws ec2 delete-security-group --group-id $SG_DB
aws rds describe-db-instances --query 'DBInstances[].DBInstanceIdentifier'
aws rds describe-db-snapshots --snapshot-type manual --query 'DBSnapshots[].DBSnapshotIdentifier'
El secreto que creó --manage-master-user-password lo borra RDS al eliminar la instancia. Comprueba en Billing → Bills al día siguiente que no queda gasto de RDS.
Preguntas para pensar como arquitecto
1. Los informes de negocio se lanzan cada hora y disparan la CPU del primario. ¿Añadirías más Multi-AZ, una réplica o una caché?
Una réplica de lectura a la que apunten los informes (o un Multi-AZ DB cluster, cuyos readers también sirven lecturas). Multi-AZ DB instance no ayuda: su standby no acepta lecturas. Si los informes repiten las mismas consultas, una caché (ElastiCache) reduce aún más la carga.
2. Tu RTO es de 1 minuto y el failover de 60-120 segundos (lo típico según la documentación de RDS) más la caché DNS de la aplicación lo supera. ¿Qué opciones tienes?
RDS Proxy (mantiene las conexiones y enruta al nuevo primario sin depender del DNS), migrar a Multi-AZ DB cluster (failover típico de menos de 35 segundos, MySQL y PostgreSQL) o a Aurora con réplicas (normalmente menos de 60 segundos y a menudo menos de 30). Y revisar el TTL del DNS en el cliente.
3. Necesitas DR en otra región con RPO de minutos para esta base MySQL y el menor coste posible. ¿Qué diseñarías?
Una cross-Region read replica (pequeña, que se agranda al promocionarla) o, si el RPO puede ser de horas, copias automáticas de snapshots entre regiones con AWS Backup (backup and restore, más barato). En caso de desastre, promocionar la réplica y apuntar la aplicación (por ejemplo, con Route 53 failover).
4. Lambda abre cientos de conexiones en los picos y aparecen errores «Too many connections». ¿Subirías la clase de instancia?
No como primera opción: RDS Proxy agrupa y reutiliza conexiones, pone en cola las que sobran y permite autenticación con IAM y secretos de Secrets Manager. Es más barato y resuelve la causa.