El problema fundamental que aborda esta arquitectura es la gestión eficiente y escalable de servicios stateful multi-tenant en entornos de hyperscaler, donde el aislamiento de recursos es crítico para la estabilidad del servicio y la eficiencia operativa. Tradicionalmente, el aislamiento estricto por tenant (ej. una cuenta AWS por tenant) conduce a una infrautilización masiva de recursos y a procesos de onboarding lentos y costosos. Por otro lado, la compartición de recursos sin un aislamiento adecuado introduce el problema del 'vecino ruidoso' (noisy neighbor), especialmente en servicios stateful que mantienen datos en memoria.
La necesidad de una solución surge de la incapacidad de las arquitecturas celulares puras para escalar económicamente y de la inviabilidad de arquitecturas de recursos compartidos para cargas de trabajo stateful. La plataforma de ad-serving descrita, que maneja millones de solicitudes por segundo y mantiene estado en memoria por tenant, es un caso de uso clásico donde el rendimiento es primordial y la contención de recursos en memoria es un riesgo directo para la calidad del servicio. La solución propuesta busca un equilibrio entre el aislamiento de cluster y la eficiencia operativa, un desafío recurrente en sistemas distribuidos a gran escala.
Históricamente, el problema del multi-tenancy ha sido abordado con diversas estrategias, desde la virtualización completa (máquinas virtuales dedicadas) hasta la compartición de procesos y bases de datos. La evolución de los contenedores y los servicios serverless ha introducido nuevas dimensiones al problema, permitiendo granularidad en el aislamiento y la asignación de recursos. Sin embargo, para servicios stateful con requisitos de baja latencia y grandes conjuntos de datos en memoria, la elección de la estrategia de aislamiento sigue siendo un factor crítico de diseño.
Arquitectura del Sistema
La arquitectura implementa un modelo híbrido multi-tenant con aislamiento a nivel de cluster dentro de cuentas compartidas. Se estructura en una jerarquía de tres niveles: Tier, Cell e Infra Group. Un Tier es una clasificación lógica de tenants que comparten una huella de infraestructura común. Un Tier abarca una o más Cells, donde cada Cell es un límite de cuenta AWS que representa la unidad de escalado horizontal a nivel de cuenta. Dentro de cada Cell, uno o más Infra Groups sirven como unidad de infraestructura autocontenida, incluyendo una VPC, un Application Load Balancer (ALB), un conjunto de clusters ECS (uno por tenant), roles IAM y un stack de monitoreo.
La conectividad de servicios downstream se pre-integra a nivel de Tier mediante AWS PrivateLink, eliminando la necesidad de configurar VPC peering o Transit Gateway por cada tenant. Esto reduce significativamente la sobrecarga de configuración de red. Amazon Route 53 con weighted routing se utiliza para distribuir el tráfico de forma gradual entre ALBs en múltiples Infra Groups y Cells, permitiendo el escalado horizontal sin cambios del lado del cliente. Los ALBs utilizan listener rules basadas en la ruta de la solicitud o encabezados HTTP para enrutar el tráfico a los target groups específicos de cada tenant. Cada target group apunta a un cluster ECS dedicado por tenant, asegurando el aislamiento de recursos y de estado en memoria.
Los clusters ECS son configurados con definiciones de tareas que pasan el identificador del tenant como variable de entorno, permitiendo a la aplicación cargar solo la configuración y el estado relevantes de un caché remoto compartido. El escalado se gestiona a tres niveles: escalado vertical de tareas ECS o instancias EC2 para un solo tenant, adición de Infra Groups dentro de una Cell cuando se alcanzan los límites del ALB (ej. 50 tenants por Infra Group), y adición de Cells (nuevas cuentas AWS) cuando se alcanzan los límites de la cuenta AWS (ej. ENIs, VPC endpoints). La observabilidad se implementa con métricas a nivel de tenant en Amazon CloudWatch, utilizando el identificador del tenant como dimensión, y logs estructurados con tenant_id para facilitar el análisis de errores y rendimiento.
Flujo de Solicitud Multi-Tenant
- 1 Cliente Envía solicitud a endpoint DNS del Tier (ej. tier-1.us-east-1.example.com)
- 2 Route 53 Resuelve DNS usando weighted routing a un ALB en un Infra Group/Cell
- 3 Application Load Balancer Inspecciona la solicitud (path/header) para identificar el tenant
- 4 ALB Listener Rule Enruta la solicitud al Target Group específico del tenant
- 5 ECS Cluster (Tenant) Recibe la solicitud, carga estado en memoria específico del tenant
- 6 ECS Task Procesa la solicitud, accede a servicios downstream vía PrivateLink
- 7 AWS PrivateLink Conexión segura y privada a servicios downstream compartidos
- 8 Servicio Downstream Responde a la solicitud
Flujo de Onboarding de Nuevo Tenant
- 1 Creación de Tier Configuración inicial de VPCs, PrivateLink, IAM roles (una vez)
- 2 Adición de Infra Group/Cell Si es necesario, se provisiona una nueva unidad de escala
- 3 Configuración de Tenant Se añade entrada de configuración para el nuevo tenant
- 4 ALB Listener Rule Se crea una regla para enrutar tráfico al nuevo tenant
- 5 ECS Cluster (Tenant) Se provisiona un cluster ECS dedicado para el nuevo tenant
- 6 ECS Task Definition Se configura con el ID del tenant como variable de entorno
- 7 Registro en Target Group El servicio ECS se registra en el Target Group del ALB
- 8 Validación y Pruebas Verificación de enrutamiento, aislamiento y conectividad
| Capa | Tecnología | Justificación |
|---|---|---|
| networking | Amazon Route 53 | Servicio DNS para enrutamiento de tráfico global y distribución de carga entre ALBs usando weighted routing. Permite el escalado horizontal transparente sin cambios del lado del cliente. Weighted routing records, health checks. |
| networking | Application Load Balancer (ALB) | Balanceador de carga de capa 7 que enruta el tráfico a clusters ECS específicos de cada tenant utilizando listener rules basadas en path o HTTP headers. Proporciona un punto de entrada unificado para múltiples tenants. Listener rules, target groups, path-based routing, header-based routing. |
| compute | Amazon Elastic Container Service (ECS) | Orquestador de contenedores que aloja clusters dedicados por tenant. Proporciona aislamiento a nivel de cluster para los servicios stateful y permite la asignación granular de recursos. vs Amazon EKS, AWS Fargate Dedicated ECS clusters per tenant, task definitions with tenant_id environment variable. |
| networking | AWS PrivateLink | Establece conectividad privada y segura a servicios downstream compartidos. Se configura una vez a nivel de Tier, reduciendo la complejidad de red por tenant. vs VPC Peering, AWS Transit Gateway VPC interface endpoints, endpoint services. |
| security | AWS Identity and Access Management (IAM) | Gestiona los permisos de acceso a los recursos de AWS. Los roles IAM se pre-configuran a nivel de Tier y se reutilizan por los tenants, simplificando la gestión de seguridad. Tier-level IAM roles for ECS tasks. |
| observability | Amazon CloudWatch | Recopila y monitorea métricas y logs. Se utiliza para emitir métricas a nivel de tenant (con tenant_id como dimensión) y para el análisis de logs estructurados, facilitando la detección de problemas y la planificación de capacidad. Custom metrics with dimensions, structured logging, CloudWatch Alarms, CloudWatch Logs Insights. |
Trade-offs
Ganancias
- ▲▲ Tiempo de onboarding de tenants
- ▲ Eficiencia operativa (pasos de configuración)
- ▲ Aislamiento de recursos (evitar noisy neighbor)
- ▲ Capacidad de tenants por cuenta AWS
Costes
- △ Costo de VPC interface endpoints
- △ Complejidad inicial de la arquitectura
aws route53 change-resource-record-sets \
--hosted-zone-id Z35S****K \
--change-batch '{
"Changes": [
{
"Action": "UPSERT",
"ResourceRecordSet": {
"Name": "tier-1.us-east-1.example.com",
"Type": "A",
"AliasTarget": {
"HostedZoneId": "Z1L8K****L",
"DNSName": "dualstack.alb-1234567890.us-east-1.elb.amazonaws.com",
"EvaluateTargetHealth": true
},
"Weight": 50,
"SetIdentifier": "account-1"
}
}
]
}'aws elbv2 create-listener-rules \
--listener-arn arn:aws:elasticloadbalancing:us-east-1:123456789012:listener/app/my-alb/50dc6c495c0c9188/f2f7dc8efc522ab2 \
--rules '[{
"Priority": 10,
"Conditions": [
{
"Field": "path-pattern",
"Values": ["/tenant-a/*"]
}
],
"Actions": [
{
"Type": "forward",
"TargetGroupArn": "arn:aws:elasticloadbalancing:us-east-1:123456789012:targetgroup/tenant-a-tg/8c50187f20a7f806"
}
]
}]'{
"family": "my-ad-serving-app",
"containerDefinitions": [
{
"name": "ad-server",
"image": "your-ecr-image:latest",
"environment": [
{
"name": "TENANT_ID",
"value": "tenant-a"
}
],
"cpu": 256,
"memory": 512
}
]
}fields @timestamp, @message, tenant_id, tier_id, region
| filter @message like /ERROR/
| stats count() as error_count by tenant_id
| sort by error_count descFundamentos Teóricos
El problema del aislamiento de recursos y la gestión de 'noisy neighbors' en sistemas multi-tenant es un tema recurrente en la investigación de sistemas distribuidos y operativos. Conceptos como la asignación de recursos en sistemas de tiempo compartido, la virtualización y el aislamiento de procesos son fundamentales. La necesidad de balancear la eficiencia (alta utilización de recursos) con el aislamiento (garantías de rendimiento) se alinea con los principios de diseño de sistemas operativos y de bases de datos.
Aunque el artículo no cita directamente papers académicos, los principios subyacentes se relacionan con trabajos sobre scheduling de recursos en sistemas distribuidos, como los algoritmos de asignación de recursos en clústeres (ej. Mesos, YARN) que buscan optimizar la utilización mientras se cumplen los SLAs. La gestión de estado en memoria y la prevención de condiciones de out-of-memory debido a la contención de heap entre tenants resuenan con los desafíos de diseño de sistemas de gestión de bases de datos in-memory y cachés distribuidos, donde la partición de datos y la gestión de la memoria son críticas para el rendimiento y la estabilidad. La estrategia de 'pre-wiring' de dependencias se asemeja a patrones de diseño de infraestructura como código y aprovisionamiento declarativo, que buscan reducir la complejidad y el tiempo de despliegue, un concepto bien establecido en la ingeniería de software moderna.