Amazon Route 53: mucho más que DNS
Durante años, el DNS se ha tratado como un trámite: compras un dominio, apuntas un par de registros A y sigues con lo “importante”.
Ese enfoque funciona… hasta que deja de hacerlo.
En entornos cloud modernos, el DNS no es un detalle administrativo: es la primera decisión de arquitectura que impacta en latencia, disponibilidad y experiencia de usuario.
Y aquí es donde Amazon Route 53 juega un papel que muchos equipos siguen infravalorando.
El DNS no es pasivo (aunque lo parezca)
Cuando un usuario accede a tu aplicación, el código todavía no ha hecho nada. No hay microservicios, no hay cachés, no hay base de datos.
Todo empieza antes, en una pregunta simple:
¿A qué IP tengo que ir?
Esa respuesta la da el DNS, y si es lenta, incorrecta o apunta al sitio equivocado, da igual lo bien que esté escrito tu backend.
La ilusión de la instantaneidad:
Flujo real de una petición (antes de llegar a tu app):
1. Resolución DNS (Route 53):
- Consulta recursiva desde el cliente
- Respuesta desde nameservers autoritativos
- TTL cacheado localmente
- Tiempo: 50-200ms primera vez, ~0ms si cacheado
2. Handshake TCP/TLS:
- SYN → SYN-ACK → ACK
- Negociación TLS 1.3
- Tiempo: 1-2 RTTs (20-100ms según distancia)
3. Petición HTTP/2 o HTTP/3:
- Request headers + body
- Tiempo: depende de payload
El DNS es el paso 0. Si falla o es lento, todo lo demás es irrelevante.
Route 53 no es solo un DNS autoritativo. Es un servicio global, altamente disponible y diseñado para tomar decisiones de enrutamiento en función de criterios reales:
- ✅ Salud del endpoint (health checks)
- ✅ Latencia medida (no teórica)
- ✅ Peso configurado (traffic shaping)
- ✅ Localización geográfica (compliance)
No se limita a resolver nombres: decide por dónde entra el tráfico.
Qué es realmente Amazon Route 53
A nivel funcional, Route 53 combina tres cosas clave en un único servicio:
Route 53 como sistema:
1. DNS autoritativo gestionado:
- Nameservers distribuidos globalmente
- 100% SLA de disponibilidad
- Anycast para baja latencia
- DNSSEC opcional
2. Motor de routing inteligente:
- Políticas de enrutamiento dinámicas
- Decisiones basadas en métricas reales
- Sin necesidad de código en la app
3. Sistema de health checks integrado:
- Monitoreo desde múltiples ubicaciones
- Alertas a CloudWatch
- Failover automático
Pero lo interesante no es la definición, sino lo que permite hacer en arquitecturas reales.
Route 53 vive:
- ❌ Fuera de tu VPC
- ❌ Fuera de tus clusters
- ❌ Fuera de tus balanceadores
Eso lo convierte en una capa de control independiente, ideal para tomar decisiones cuando otras piezas están fallando.
El error más común: pensar que el DNS “no escala”
Es habitual ver arquitecturas muy sofisticadas detrás de un DNS configurado con prisas.
El razonamiento suele ser:
“Esto solo apunta a un ALB, ya escalará solo.”
El problema es que el DNS también es parte del camino crítico.
Ejemplo realista que he visto fallar:
Escenario:
- Aplicación multi-región (us-east-1 + eu-west-1)
- ALBs en ambas regiones
- Auto Scaling Groups configurados
- CloudFront delante (opcional)
DNS configurado mal:
api.empresa.com → ALB us-east-1 (siempre)
¿Qué pasa cuando us-east-1 cae?
❌ El tráfico sigue entrando a us-east-1
❌ Usuarios ven timeouts
❌ eu-west-1 está funcionando perfectamente (pero nadie llega)
❌ Incidente P0 declarado
❌ On-call Engineer cambia DNS manualmente
❌ Propagación: 5-60 minutos según TTL
❌ Revenue loss: $$$$$
Con Route 53 failover routing:
✅ Health check detecta us-east-1 caído
✅ Failover automático a eu-west-1
✅ Tiempo de detección: 30-60 segundos
✅ Sin intervención manual
✅ Incident resolved automáticamente
Route 53 está diseñado justo para eso: absorber decisiones que no quieres meter en el código ni en el load balancer.
Routing policies: el verdadero valor de Route 53
Aquí es donde Route 53 deja de ser “otro DNS”.
1. Simple routing: lo justo para empezar
Es el caso básico: un nombre apunta a un destino.
# Terraform: Simple routing
resource "aws_route53_record" "simple" {
zone_id = aws_route53_zone.main.zone_id
name = "blog.example.com"
type = "A"
ttl = 300
records = ["192.0.2.1"]
}
# O apuntando a ALB
resource "aws_route53_record" "simple_alb" {
zone_id = aws_route53_zone.main.zone_id
name = "app.example.com"
type = "A"
alias {
name = aws_lb.main.dns_name
zone_id = aws_lb.main.zone_id
evaluate_target_health = true
}
}
Cuándo usarlo:
- ✅ Entornos pequeños o internos
- ✅ Servicios de prueba/desarrollo
- ✅ Cuando solo hay un endpoint
- ✅ No necesitas failover ni distribución
Cuándo NO usarlo:
- ❌ Producción con alta disponibilidad
- ❌ Multi-región
- ❌ Necesitas control de tráfico
2. Weighted routing: despliegues sin tocar infraestructura
Imagina que tienes dos versiones de una API:
- v1: estable, conocida, en producción
- v2: recién desplegada, necesitas probar
Con weighted routing puedes enviar:
- 90% del tráfico a v1
- 10% del tráfico a v2
Sin modificar el ALB, sin feature flags y sin lógica adicional.
# Terraform: Weighted routing para canary deployment
# 90% tráfico a versión estable
resource "aws_route53_record" "api_v1" {
zone_id = aws_route53_zone.main.zone_id
name = "api.example.com"
type = "A"
set_identifier = "api-v1-stable"
weighted_routing_policy {
weight = 90
}
alias {
name = aws_lb.v1.dns_name
zone_id = aws_lb.v1.zone_id
evaluate_target_health = true
}
}
# 10% tráfico a nueva versión (canary)
resource "aws_route53_record" "api_v2" {
zone_id = aws_route53_zone.main.zone_id
name = "api.example.com"
type = "A"
set_identifier = "api-v2-canary"
weighted_routing_policy {
weight = 10
}
alias {
name = aws_lb.v2.dns_name
zone_id = aws_lb.v2.zone_id
evaluate_target_health = true
}
}
Proceso de despliegue canary con Route 53:
Fase 1 - Despliegue inicial:
v1: 100% (weight = 100)
v2: 0% (no existe todavía)
Fase 2 - Canary (5%):
v1: 95% (weight = 95)
v2: 5% (weight = 5)
Monitoreo: error rates, latencia, métricas negocio
Fase 3 - Expansión (25%):
v1: 75% (weight = 75)
v2: 25% (weight = 25)
Validación: más usuarios, más carga
Fase 4 - Mayoría (50%):
v1: 50% (weight = 50)
v2: 50% (weight = 50)
Decisión: continuar o rollback
Fase 5 - Finalización (100%):
v1: 0% (eliminado)
v2: 100% (weight = 100)
Si algo falla en cualquier fase:
✅ Cambias pesos en Terraform
✅ Apply rápido (segundos)
✅ Rollback inmediato
✅ Sin downtime
Esto es oro puro para despliegues progresivos y rollbacks rápidos.
3. Latency-based routing: experiencia de usuario real
Aquí Route 53 responde con el endpoint que ofrece menor latencia desde la ubicación del cliente.
No por cercanía geográfica teórica, sino por medición real.
# Terraform: Latency-based routing multi-región
# ALB en us-east-1
resource "aws_route53_record" "api_us_east" {
zone_id = aws_route53_zone.main.zone_id
name = "api.example.com"
type = "A"
set_identifier = "api-us-east-1"
latency_routing_policy {
region = "us-east-1"
}
alias {
name = aws_lb.us_east.dns_name
zone_id = aws_lb.us_east.zone_id
evaluate_target_health = true
}
}
# ALB en eu-west-1
resource "aws_route53_record" "api_eu_west" {
zone_id = aws_route53_zone.main.zone_id
name = "api.example.com"
type = "A"
set_identifier = "api-eu-west-1"
latency_routing_policy {
region = "eu-west-1"
}
alias {
name = aws_lb.eu_west.dns_name
zone_id = aws_lb.eu_west.zone_id
evaluate_target_health = true
}
}
# ALB en ap-southeast-1
resource "aws_route53_record" "api_ap_southeast" {
zone_id = aws_route53_zone.main.zone_id
name = "api.example.com"
type = "A"
set_identifier = "api-ap-southeast-1"
latency_routing_policy {
region = "ap-southeast-1"
}
alias {
name = aws_lb.ap_southeast.dns_name
zone_id = aws_lb.ap_southeast.zone_id
evaluate_target_health = true
}
}
Resultado:
Usuario desde Madrid:
Consulta DNS → Route 53
Route 53 mide latencia:
- us-east-1: 80ms
- eu-west-1: 15ms ← MEJOR
- ap-southeast-1: 180ms
Responde: ALB eu-west-1
Usuario desde San Francisco:
Consulta DNS → Route 53
Route 53 mide latencia:
- us-east-1: 10ms ← MEJOR
- eu-west-1: 140ms
- ap-southeast-1: 120ms
Responde: ALB us-east-1
Usuario desde Singapur:
Consulta DNS → Route 53
Route 53 mide latencia:
- us-east-1: 210ms
- eu-west-1: 180ms
- ap-southeast-1: 8ms ← MEJOR
Responde: ALB ap-southeast-1
Sin reglas manuales. Sin redirecciones. Sin lógica en el código.
Usuarios europeos entran por Europa, americanos por EE. UU., asiáticos por Asia. Experiencia óptima automática.
4. Failover routing: resiliencia de verdad
Esta política permite definir un endpoint principal y uno secundario.
Si el health check falla, Route 53 deja de responder con el endpoint caído.
# Terraform: Failover routing activo-pasivo
# Endpoint PRIMARY
resource "aws_route53_record" "api_primary" {
zone_id = aws_route53_zone.main.zone_id
name = "api.example.com"
type = "A"
set_identifier = "api-primary-us-east-1"
failover_routing_policy {
type = "PRIMARY"
}
alias {
name = aws_lb.primary.dns_name
zone_id = aws_lb.primary.zone_id
evaluate_target_health = true
}
health_check_id = aws_route53_health_check.primary.id
}
# Endpoint SECONDARY
resource "aws_route53_record" "api_secondary" {
zone_id = aws_route53_zone.main.zone_id
name = "api.example.com"
type = "A"
set_identifier = "api-secondary-eu-west-1"
failover_routing_policy {
type = "SECONDARY"
}
alias {
name = aws_lb.secondary.dns_name
zone_id = aws_lb.secondary.zone_id
evaluate_target_health = true
}
}
# Health check para PRIMARY
resource "aws_route53_health_check" "primary" {
type = "HTTPS"
resource_path = "/health"
fqdn = aws_lb.primary.dns_name
port = 443
request_interval = 30
failure_threshold = 3
measure_latency = true
tags = {
Name = "api-primary-health-check"
}
}
# CloudWatch alarm para notificaciones
resource "aws_cloudwatch_metric_alarm" "primary_unhealthy" {
alarm_name = "route53-primary-endpoint-unhealthy"
comparison_operator = "LessThanThreshold"
evaluation_periods = 2
metric_name = "HealthCheckStatus"
namespace = "AWS/Route53"
period = 60
statistic = "Minimum"
threshold = 1
alarm_description = "Primary endpoint failing health checks"
alarm_actions = [aws_sns_topic.incidents.arn]
dimensions = {
HealthCheckId = aws_route53_health_check.primary.id
}
}
Comportamiento en failover:
Estado normal:
Health check PRIMARY: ✅ HEALTHY
Tráfico: 100% a PRIMARY
SECONDARY: standby (idle pero vivo)
Fallo detectado:
t=0s: PRIMARY empieza a fallar
t=30s: Primer health check falla
t=60s: Segundo health check falla
t=90s: Tercer health check falla (threshold = 3)
t=90s: Route 53 marca PRIMARY como UNHEALTHY
t=90s: Tráfico nuevo va a SECONDARY automáticamente
t=90s: CloudWatch alarm dispara → SNS → PagerDuty
Recuperación:
PRIMARY vuelve a estar sano
3 health checks consecutivos exitosos
Route 53 marca PRIMARY como HEALTHY
Tráfico vuelve a PRIMARY gradualmente (según TTL)
Ojo: no es instantáneo ni mágico. Depende del:
- ⏱️ Request interval (30s recomendado)
- ⏱️ Failure threshold (3 checks = 90s)
- ⏱️ TTL del registro (60-300s)
Pero bien configurado es una red de seguridad brutal cuando todo lo demás falla.
5. Geolocation routing: control y cumplimiento
No siempre quieres el endpoint “más rápido”. A veces necesitas el correcto.
Casos de uso reales:
Compliance legal:
- GDPR: datos europeos no salen de Europa
- China: requisitos de soberanía de datos
- Rusia: ley de localización de datos
Experiencia regionalizada:
- Contenido en idioma local
- Catálogos de productos diferentes
- Precios en moneda local
Restricciones de licencia:
- Contenido de streaming por país
- Aplicaciones bancarias reguladas
- Farmacéuticas con regulación regional
# Terraform: Geolocation routing por región
# Europa → data center europeo
resource "aws_route53_record" "app_europe" {
zone_id = aws_route53_zone.main.zone_id
name = "app.example.com"
type = "A"
set_identifier = "app-europe"
geolocation_routing_policy {
continent = "EU"
}
alias {
name = aws_lb.eu_west.dns_name
zone_id = aws_lb.eu_west.zone_id
evaluate_target_health = true
}
}
# América del Norte → data center US
resource "aws_route53_record" "app_north_america" {
zone_id = aws_route53_zone.main.zone_id
name = "app.example.com"
type = "A"
set_identifier = "app-north-america"
geolocation_routing_policy {
continent = "NA"
}
alias {
name = aws_lb.us_east.dns_name
zone_id = aws_lb.us_east.zone_id
evaluate_target_health = true
}
}
# China específicamente
resource "aws_route53_record" "app_china" {
zone_id = aws_route53_zone.main.zone_id
name = "app.example.com"
type = "A"
set_identifier = "app-china"
geolocation_routing_policy {
country = "CN"
}
alias {
name = aws_lb.cn_north.dns_name
zone_id = aws_lb.cn_north.zone_id
evaluate_target_health = true
}
}
# Default para resto del mundo
resource "aws_route53_record" "app_default" {
zone_id = aws_route53_zone.main.zone_id
name = "app.example.com"
type = "A"
set_identifier = "app-default"
geolocation_routing_policy {
continent = "*" # wildcard
}
alias {
name = aws_lb.us_east.dns_name
zone_id = aws_lb.us_east.zone_id
evaluate_target_health = true
}
}
Route 53 te permite decidir por origen, no por infraestructura.
6. Multivalue answer routing: pobre man’s load balancing
Devuelve múltiples valores (hasta 8) y el cliente elige uno aleatoriamente.
# Terraform: Multivalue answer routing
resource "aws_route53_record" "api_multivalue_1" {
zone_id = aws_route53_zone.main.zone_id
name = "api.example.com"
type = "A"
ttl = 60
set_identifier = "api-instance-1"
multivalue_answer_routing_policy = true
records = [aws_instance.api_1.public_ip]
health_check_id = aws_route53_health_check.instance_1.id
}
resource "aws_route53_record" "api_multivalue_2" {
zone_id = aws_route53_zone.main.zone_id
name = "api.example.com"
type = "A"
ttl = 60
set_identifier = "api-instance-2"
multivalue_answer_routing_policy = true
records = [aws_instance.api_2.public_ip]
health_check_id = aws_route53_health_check.instance_2.id
}
Útil para:
- ✅ Distribución simple sin ALB
- ✅ Reducir costes (sin balanceador)
- ✅ Servicios internos de baja criticidad
NO usar para:
- ❌ Producción crítica
- ❌ Cuando necesitas control real de tráfico
- ❌ Sticky sessions
Health checks: simples, pero estratégicos
Los health checks de Route 53 no sustituyen a Prometheus ni a Datadog, y no están pensados para eso.
Su función es otra: decidir si un endpoint debe recibir tráfico o no.
Tipos de health checks:
1. Endpoint health checks:
- HTTP/HTTPS/TCP
- IP pública o DNS name
- Request interval: 10s o 30s
- String matching (opcional)
- Latency measurement
2. Calculated health checks:
- Combina múltiples checks
- Lógica AND/OR/NOT
- Útil para dependencias
3. CloudWatch alarm health checks:
- Basado en métricas
- Integración profunda con AWS
- Custom metrics soportados
Ejemplo avanzado - Health check que realmente verifica salud:
# Terraform: Health check realista
# Health check básico (solo conectividad)
resource "aws_route53_health_check" "basic" {
type = "HTTPS"
resource_path = "/ping"
fqdn = "api.example.com"
port = 443
request_interval = 30
failure_threshold = 3
tags = {
Name = "basic-connectivity-check"
}
}
# Health check avanzado (verifica contenido)
resource "aws_route53_health_check" "advanced" {
type = "HTTPS"
resource_path = "/health"
fqdn = "api.example.com"
port = 443
request_interval = 30
failure_threshold = 3
measure_latency = true
enable_sni = true
search_string = "\"status\":\"healthy\""
tags = {
Name = "advanced-content-check"
}
}
# Health check basado en CloudWatch (métricas reales)
resource "aws_cloudwatch_metric_alarm" "api_error_rate" {
alarm_name = "api-high-error-rate"
comparison_operator = "GreaterThanThreshold"
evaluation_periods = 2
metric_name = "5XXError"
namespace = "AWS/ApplicationELB"
period = 60
statistic = "Average"
threshold = 5 # > 5% error rate
alarm_description = "API error rate too high"
dimensions = {
LoadBalancer = aws_lb.main.arn_suffix
}
}
resource "aws_route53_health_check" "cloudwatch_based" {
type = "CLOUDWATCH_METRIC"
cloudwatch_alarm_name = aws_cloudwatch_metric_alarm.api_error_rate.alarm_name
cloudwatch_alarm_region = "us-east-1"
insufficient_data_health_status = "Healthy"
tags = {
Name = "cloudwatch-error-rate-check"
}
}
# Health check calculado (combina múltiples)
resource "aws_route53_health_check" "calculated" {
type = "CALCULATED"
child_health_threshold = 2 # Al menos 2 de 3 deben estar sanos
child_healthchecks = [
aws_route53_health_check.basic.id,
aws_route53_health_check.advanced.id,
aws_route53_health_check.cloudwatch_based.id
]
tags = {
Name = "combined-health-check"
}
}
Endpoint que responde vs Endpoint que funciona:
Escenario común (MAL):
Health check: GET /ping
Respuesta: 200 OK
Pero la aplicación:
❌ Database connection pool agotado
❌ Requests reales fallan con 500
❌ Latencia p99 = 10 segundos
Resultado:
Health check: ✅ HEALTHY
Usuarios: 😡 errores constantes
Route 53: sigue enviando tráfico
Escenario correcto (BIEN):
Health check: GET /health/deep
Endpoint verifica:
✅ Database connectivity
✅ Redis cache
✅ S3 access
✅ Downstream APIs
✅ Memory/CPU usage
Respuesta: 200 solo si TODO está OK
Resultado:
Health check refleja salud real
Failover cuando corresponde
Experiencia de usuario protegida
Un endpoint que responde pero devuelve errores 500 puede estar “vivo” para un monitor, pero no debería recibir usuarios.
Diseñar bien estos checks marca la diferencia entre un failover útil y uno inútil.
Route 53 como capa de arquitectura (no como configuración)
Cuando entiendes Route 53, empiezas a usarlo como pieza de diseño, no como formulario de AWS.
Ejemplos muy reales:
1. Separar entornos sin duplicar infraestructura:
# Terraform: Entornos con hosted zones
# Zona principal corporativa
resource "aws_route53_zone" "corporate" {
name = "example.com"
tags = {
Environment = "shared"
ManagedBy = "platform-team"
}
}
# Subzona Development
resource "aws_route53_zone" "development" {
name = "dev.example.com"
tags = {
Environment = "development"
}
}
resource "aws_route53_record" "dev_ns" {
zone_id = aws_route53_zone.corporate.zone_id
name = "dev.example.com"
type = "NS"
ttl = 300
records = aws_route53_zone.development.name_servers
}
# Subzona Staging
resource "aws_route53_zone" "staging" {
name = "staging.example.com"
tags = {
Environment = "staging"
}
}
resource "aws_route53_record" "staging_ns" {
zone_id = aws_route53_zone.corporate.zone_id
name = "staging.example.com"
type = "NS"
ttl = 300
records = aws_route53_zone.staging.name_servers
}
# Zona Production (separada por seguridad)
resource "aws_route53_zone" "production" {
name = "prod.example.com"
tags = {
Environment = "production"
Critical = "true"
}
}
Beneficios:
- ✅ Aislamiento total entre entornos
- ✅ Permisos IAM granulares por zona
- ✅ Diferentes equipos gestionan diferentes zonas
- ✅ Blast radius contenido
2. Migración entre regiones sin tocar aplicaciones:
Escenario: migrar de us-east-1 a eu-west-1
Paso 1 - Estado inicial:
api.example.com → us-east-1 (100%)
Paso 2 - Desplegar en nueva región:
eu-west-1: infraestructura lista
Datos replicados
Sin tráfico aún
Paso 3 - Weighted routing (canary):
api.example.com:
- us-east-1: 95%
- eu-west-1: 5%
Monitoreo intensivo
Paso 4 - Incrementar gradualmente:
Día 1: 95% / 5%
Día 2: 80% / 20%
Día 3: 50% / 50%
Día 4: 20% / 80%
Día 5: 0% / 100%
Paso 5 - Cambio a latency-based:
Ambas regiones activas
Route 53 decide por latencia
Paso 6 - Decomisionar us-east-1:
Solo cuando estés seguro
Rollback siempre posible
Tiempo total: 1-2 semanas
Downtime: 0 segundos
Cambios en código: 0 líneas
3. Aislar incidentes graves redirigiendo tráfico:
Incident: RDS primary en us-east-1 corrupto
Opción A (sin Route 53):
1. Fix database (horas/días)
2. Downtime completo
3. Revenue loss
4. Reputation damage
Opción B (con Route 53 + multi-región):
1. Identificar incidente (minutos)
2. Cambiar weight: us-east-1 = 0%, eu-west-1 = 100%
3. Terraform apply
4. Tráfico redirigido (60-90 segundos)
5. Arreglar us-east-1 sin presión
6. Revertir cuando esté listo
Downtime: <2 minutos
Revenue loss: mínimo
Reputation: "tuvimos un problema breve, ya resuelto"
Todo esto ocurre antes de que el tráfico llegue a tu cloud. Y eso, para un perfil Cloud Engineer o SRE, es una ventaja enorme.
Private Hosted Zones: el DNS interno que nadie usa (pero debería)
Route 53 no solo es para Internet. También gestiona DNS interno dentro de tu VPC.
Problema clásico sin Private Hosted Zones:
Arquitectura típica:
- EC2 instances se comunican por IP privada
- RDS endpoint: db-prod-cluster.xxxx.us-east-1.rds.amazonaws.com
- ElastiCache: redis-prod.xxxx.cache.amazonaws.com
Problemas:
❌ IPs hardcodeadas en config files
❌ Endpoints AWS largos e incómodos
❌ Cambios requieren redeploy
❌ No hay consistencia entre entornos
❌ Service discovery manual
Solución con Private Hosted Zone:
# Terraform: Private Hosted Zone
resource "aws_route53_zone" "internal" {
name = "internal.example.com"
vpc {
vpc_id = aws_vpc.main.id
}
tags = {
Name = "internal-dns"
Environment = "production"
Visibility = "private"
}
}
# Database endpoint legible
resource "aws_route53_record" "database" {
zone_id = aws_route53_zone.internal.zone_id
name = "db.internal.example.com"
type = "CNAME"
ttl = 300
records = [aws_db_instance.main.address]
}
# Cache endpoint
resource "aws_route53_record" "cache" {
zone_id = aws_route53_zone.internal.zone_id
name = "cache.internal.example.com"
type = "CNAME"
ttl = 300
records = [aws_elasticache_cluster.main.cache_nodes[0].address]
}
# API interna entre servicios
resource "aws_route53_record" "api_internal" {
zone_id = aws_route53_zone.internal.zone_id
name = "api.internal.example.com"
type = "A"
alias {
name = aws_lb.internal.dns_name
zone_id = aws_lb.internal.zone_id
evaluate_target_health = true
}
}
# Service discovery pattern
resource "aws_route53_record" "service_discovery" {
zone_id = aws_route53_zone.internal.zone_id
name = "service-a.internal.example.com"
type = "A"
ttl = 60
records = aws_instance.service_a[*].private_ip
}
Ventajas:
Configuración de aplicación:
Antes:
DB_HOST=db-prod-cluster.cg8h3j2k1l9m.us-east-1.rds.amazonaws.com
CACHE_HOST=redis-prod.abc123.0001.use1.cache.amazonaws.com
Después:
DB_HOST=db.internal.example.com
CACHE_HOST=cache.internal.example.com
Beneficios:
✅ Legible y memorable
✅ Consistente entre entornos
✅ Cambios sin redeploy (solo DNS)
✅ Multi-VPC con VPC peering
✅ Logging y auditoría
El DNS también es responsabilidad del equipo
Uno de los problemas más habituales es que nadie “posee” el DNS.
Está ahí, funciona… hasta que alguien cambia algo sin entender TTLs, caché o propagación.
Anti-patterns comunes:
❌ DNS gestionado manualmente en consola:
- Cambios sin version control
- No hay audit trail
- Imposible hacer rollback
- Nadie sabe qué hace cada registro
❌ TTLs mal configurados:
- TTL = 86400 (24 horas) en producción
- Cambio urgente requiere esperar 24h
- Incident extended innecesariamente
❌ Sin health checks:
- Failover manual
- Depende de humanos despiertos
- Downtime prolongado
❌ Sin monitoreo:
- No sabes cuándo un health check falla
- Descubres problemas por usuarios
- Mean Time To Detection altísimo
❌ Mixing DNS providers:
- Route 53 + Cloudflare + GoDaddy
- Nadie sabe dónde está cada registro
- Debugging imposible
Best practices para equipos maduros:
✅ DNS como código (IaC):
- Terraform/CloudFormation
- Version control (Git)
- Peer review obligatorio
- CI/CD pipeline
- Rollback fácil
✅ TTLs estratégicos:
Producción:
- Normal: 300s (5 minutos)
- Pre-cambio: 60s (1 minuto)
- Post-cambio: volver a 300s
Desarrollo:
- 60s siempre (cambios frecuentes)
✅ Health checks + alarmas:
- CloudWatch alarms
- SNS → PagerDuty/Slack
- Runbooks documentados
- On-call informado
✅ Documentación viva:
- Diagrama de arquitectura DNS
- Matriz de routing policies
- Procedimientos de cambio
- Disaster recovery plan
✅ Ownership claro:
- RACI matrix
- Platform team como owner
- Application teams como consumers
- Proceso de request/approval
Ejemplo: proceso de cambio de DNS maduro:
Proceso de cambio DNS en equipo maduro:
1. Request:
- Ticket en Jira/ServiceNow
- Justificación de negocio
- Impacto estimado
- Rollback plan
2. Review:
- Tech Lead revisa
- Platform Team aprueba
- Security revisa (si aplica)
3. Pre-change:
- Reducir TTL a 60s
- Esperar 2x TTL viejo (10 min)
- Notificar stakeholders
4. Implementation:
- Pull request en Terraform
- Peer review
- terraform plan (preview)
- terraform apply (producción)
- Merge a main
5. Validation:
- dig/nslookup desde múltiples ubicaciones
- Health checks monitoreados
- Métricas de negocio
- User feedback
6. Post-change:
- TTL de vuelta a 300s
- Documentación actualizada
- Postmortem (si hubo issues)
- Lessons learned
Tiempo total: 30-60 minutos
Downtime: 0 segundos
Risk: minimizado
Route 53 facilita mucho la gestión, pero no elimina la necesidad de criterio técnico.
Un buen equipo trata el DNS como:
- ✅ Código (infraestructura versionada)
- ✅ Parte del diseño de resiliencia
- ✅ Elemento crítico en incidentes
Casos de uso avanzados
1. Blue/Green Deployment a nivel DNS:
# Terraform: Blue/Green con Route 53
# Blue environment (actual producción)
resource "aws_route53_record" "blue_green_blue" {
zone_id = aws_route53_zone.main.zone_id
name = "app.example.com"
type = "A"
set_identifier = "blue-environment"
weighted_routing_policy {
weight = 100 # 100% tráfico a blue
}
alias {
name = aws_lb.blue.dns_name
zone_id = aws_lb.blue.zone_id
evaluate_target_health = true
}
}
# Green environment (nueva versión)
resource "aws_route53_record" "blue_green_green" {
zone_id = aws_route53_zone.main.zone_id
name = "app.example.com"
type = "A"
set_identifier = "green-environment"
weighted_routing_policy {
weight = 0 # 0% tráfico a green (standby)
}
alias {
name = aws_lb.green.dns_name
zone_id = aws_lb.green.zone_id
evaluate_target_health = true
}
}
# Cutover: cambiar pesos
# Blue: 100 → 0
# Green: 0 → 100
# Rollback: invertir pesos
2. Disaster Recovery automatizado:
# Terraform: DR multi-región
# Primary region (activa)
resource "aws_route53_record" "dr_primary" {
zone_id = aws_route53_zone.main.zone_id
name = "app.example.com"
type = "A"
set_identifier = "primary-us-east-1"
failover_routing_policy {
type = "PRIMARY"
}
alias {
name = aws_lb.primary.dns_name
zone_id = aws_lb.primary.zone_id
evaluate_target_health = true
}
health_check_id = aws_route53_health_check.primary_comprehensive.id
}
# DR region (standby)
resource "aws_route53_record" "dr_secondary" {
zone_id = aws_route53_zone.main.zone_id
name = "app.example.com"
type = "A"
set_identifier = "dr-eu-west-1"
failover_routing_policy {
type = "SECONDARY"
}
alias {
name = aws_lb.dr.dns_name
zone_id = aws_lb.dr.zone_id
evaluate_target_health = true
}
}
# Health check complejo
resource "aws_route53_health_check" "primary_comprehensive" {
type = "CALCULATED"
child_health_threshold = 3 # 3 de 4 deben estar OK
child_healthchecks = [
aws_route53_health_check.primary_alb.id,
aws_route53_health_check.primary_rds.id,
aws_route53_health_check.primary_api.id,
aws_route53_health_check.primary_metrics.id
]
}
3. Geo-proximity routing (distancia física):
# Terraform: Geo-proximity routing
resource "aws_route53_record" "geo_prox_us_east" {
zone_id = aws_route53_zone.main.zone_id
name = "cdn.example.com"
type = "A"
set_identifier = "us-east-datacenter"
geoproximity_routing_policy {
aws_region = "us-east-1"
bias = 0 # neutral
}
alias {
name = aws_cloudfront_distribution.us_east.domain_name
zone_id = aws_cloudfront_distribution.us_east.hosted_zone_id
evaluate_target_health = false
}
}
resource "aws_route53_record" "geo_prox_eu_west" {
zone_id = aws_route53_zone.main.zone_id
name = "cdn.example.com"
type = "A"
set_identifier = "eu-west-datacenter"
geoproximity_routing_policy {
aws_region = "eu-west-1"
bias = 10 # atrae más tráfico (10% más de "área")
}
alias {
name = aws_cloudfront_distribution.eu_west.domain_name
zone_id = aws_cloudfront_distribution.eu_west.hosted_zone_id
evaluate_target_health = false
}
}
Monitoreo y observabilidad
CloudWatch Metrics para Route 53:
# Terraform: Monitoring completo
# Dashboard CloudWatch
resource "aws_cloudwatch_dashboard" "route53" {
dashboard_name = "route53-health-monitoring"
dashboard_body = jsonencode({
widgets = [
{
type = "metric"
properties = {
metrics = [
["AWS/Route53", "HealthCheckStatus", { stat = "Average" }],
[".", "HealthCheckPercentageHealthy", { stat = "Average" }],
[".", "ConnectionTime", { stat = "Average" }]
]
period = 60
stat = "Average"
region = "us-east-1"
title = "Health Check Status"
}
}
]
})
}
# Alarm: health check unhealthy
resource "aws_cloudwatch_metric_alarm" "health_check_failed" {
alarm_name = "route53-health-check-failed"
comparison_operator = "LessThanThreshold"
evaluation_periods = 2
metric_name = "HealthCheckStatus"
namespace = "AWS/Route53"
period = 60
statistic = "Minimum"
threshold = 1
alarm_description = "Route 53 health check failing"
alarm_actions = [aws_sns_topic.critical.arn]
dimensions = {
HealthCheckId = aws_route53_health_check.primary.id
}
}
# Query Logging (CloudWatch Logs)
resource "aws_route53_query_log" "main" {
cloudwatch_log_group_arn = aws_cloudwatch_log_group.route53_queries.arn
zone_id = aws_route53_zone.main.zone_id
}
resource "aws_cloudwatch_log_group" "route53_queries" {
name = "/aws/route53/${aws_route53_zone.main.name}"
retention_in_days = 7
tags = {
Purpose = "DNS query logging for debugging"
}
}
Logs de queries DNS (debugging):
// Ejemplo de log de query Route 53
{
"version": "1.100000",
"account_id": "123456789012",
"region": "us-east-1",
"vpc_id": "vpc-1a2b3c4d",
"query_timestamp": "2026-01-04T10:15:30.123Z",
"query_name": "api.example.com.",
"query_type": "A",
"query_class": "IN",
"rcode": "NOERROR",
"answers": [
{
"Rdata": "192.0.2.1",
"Type": "A",
"Class": "IN"
}
],
"srcaddr": "10.0.1.25",
"srcport": "54321",
"transport": "UDP",
"srcids": {
"instance": "i-0abcd1234efgh5678"
}
}
Costes: Route 53 no es caro (pero tampoco gratis)
Pricing actual (2025):
Hosted Zones:
- $0.50/mes por hosted zone
- Primeras 25 zonas
- Descuentos por volumen después
Queries:
- Primeras 1B queries/mes: $0.40 por millón
- Siguiente tramo: $0.20 por millón
- Alias queries a recursos AWS: GRATIS
Health Checks:
- Basic (Fast interval): $0.50/mes por check
- HTTPS con string matching: $0.75/mes
- Calculated: $1.00/mes
Query Logging:
- CloudWatch Logs estándar
- $0.50 por GB ingested
Traffic Flow (visual policy editor):
- $50/mes por policy record
- (Raramente usado, IaC es mejor)
Ejemplo realista:
3 hosted zones: $1.50/mes
100M queries: $40/mes
10 health checks: $5-7.50/mes
Query logging: $5-10/mes
Total: ~$52-59/mes para app mediana
Es barato comparado con el coste de downtime.
Conclusión: el DNS como decisión arquitectónica
Route 53 no es “el DNS de AWS”. Es una capa de control de tráfico que vive fuera de tu infraestructura y puede salvarte cuando todo lo demás falla.
Evolución de madurez:
Nivel 1 - DNS básico:
- Simple routing
- Registros manuales
- Sin health checks
- Sin monitoreo
Estado: funciona hasta que no funciona
Nivel 2 - DNS gestionado:
- Terraform/IaC
- Alias records a ALB
- Health checks básicos
- CloudWatch alarms
Estado: profesional pero reactivo
Nivel 3 - DNS como arquitectura:
- Routing policies estratégicas
- Failover automático
- Multi-región activo-activo
- Disaster recovery probado
- Canary deployments
- Private hosted zones
Estado: resiliente y proactivo
Nivel 4 - DNS como ventaja competitiva:
- Latency optimization global
- Geo-compliance automatizado
- Cost optimization por región
- A/B testing a nivel DNS
- Incident mitigation en <60s
Estado: excelencia operacional
La realidad que nadie te cuenta:
El DNS es la primera impresión que da tu infraestructura. Si falla, nada más importa.
Un DNS bien diseñado:
- ✅ Reduce MTTR (Mean Time To Recovery)
- ✅ Permite experimentos seguros (canary, blue/green)
- ✅ Actúa como última línea de defensa (failover)
- ✅ Simplifica compliance (geolocation)
- ✅ Mejora experiencia de usuario (latency-based)
Route 53 no es caro. El downtime sí.