DEV Community

Cover image for Separando Responsabilidades (100-1,000 usuarios)
Norman Torres
Norman Torres

Posted on

Separando Responsabilidades (100-1,000 usuarios)

Esto es fantasía (Parte 2).

Lanzamos el 1 de Enero de 2026. Para inicios de Febrero, el crecimiento fue exponencial: llegamos a los 1,000 usuarios únicos al mes.

Cuando teníamos 100 usuarios, nuestra humilde infraestructura (una sola instancia EC2 corriendo todo) soportaba la carga sin despeinarse. Pero al cruzar la barrera de los 1,000, la realidad nos golpeó. La base de datos y la API empezaron a competir salvajemente por la CPU y la RAM de la instancia.

El síntoma: Si un usuario generaba un reporte pesado, la base de datos consumía el 100% del CPU. Resultado: La API dejaba de responder a todos los demás usuarios. Tiempos de respuesta de 200ms pasaron a 15 segundos o timeouts.

Nuestra primera reacción fue "fuerza bruta": escalar verticalmente a una instancia más grande (t3.medium). Funcionó... por tres días. El problema de fondo persistía: acoplamiento de recursos.

Decidimos que era hora de madurar la arquitectura.

Paso 1: Desacoplando la Base de Datos (RDS)

Mover la base de datos fuera de nuestro servidor fue la prioridad. Migramos de un contenedor Docker local a AWS RDS (Amazon Relational Database Service) usando Postgres.

¿Por qué?

  • Recursos Dedicados: La API ya no pelea por CPU con la DB.
  • Estabilidad: Si la API crashea por un bug, la DB sigue viva.
  • Mantenimiento: Backups automáticos y actualizaciones gestionadas por AWS.

El Cambio

En nuestro docker-compose.yml, eliminamos el servicio db y actualizamos la configuración de la API.

# docker-compose.yml (Actualizado)
services:
  api:
    image: turegistro/api:latest
    restart: unless-stopped
    environment:
      - NODE_ENV=production
      # Ahora apuntamos al endpoint de RDS
      - DATABASE_URL=postgresql://admin:password_super_seguro@finanzas-db.cluster-cxyz.us-west-2.rds.amazonaws.com:5432/finanzas
      - PORT=3000
    networks:
      - internal
    # Ya no dependemos de un servicio local 'db'
Enter fullscreen mode Exit fullscreen mode

Paso 2: Alta Disponibilidad (Load Balancer)

Con la DB separada, notamos otro problema: cada vez que hacíamos un deploy o reiniciábamos el servidor, el servicio se caía por completo durante unos segundos (o minutos). Además, si esa única instancia EC2 fallaba, estábamos fuera del aire.

No queríamos soluciones parches como api2.midominio.com. Queríamos transparencia.

Implementamos un Application Load Balancer (ALB).
El ALB funciona como un policía de tráfico: recibe todas las peticiones y las distribuye entre nuestros servidores disponibles.

La Nueva Arquitectura

Ahora tenemos 2 instancias EC2 idénticas (para redundancia) y una base de datos externa.

                                  ┌───────────────────┐
                                  │    AWS RDS        │
                                  │  (PostgreSQL)     │
                                  └─────────▲─────────┘
                                            │
                                     ┌──────┴──────┐
                                     │             │
                    ┌───────────────▶│ Instancia A │
┌──────────┐        │                │ (API Docker)│
│ Usuario  │──HTTPS─┼─▶  ALB  ──────▶└─────────────┘
└──────────┘        │  (Balanceador)
                    │                ┌─────────────┐
                    └───────────────▶│ Instancia B │
                                     │ (API Docker)│
                                     └─────────────┘
Enter fullscreen mode Exit fullscreen mode

Si la Instancia A muere, el ALB automáticamente manda todo el tráfico a la Instancia B. El usuario ni se entera.

Networking y Seguridad (VPC)

Aquí es donde las cosas se pusieron serias. Tuvimos que configurar correctamente nuestros Security Groups para no dejar nada expuesto.

  1. Security Group del ALB:

    • Inbound: Permite tráfico 80/443 desde todo el mundo (0.0.0.0/0).
    • Outbound: Solo hacia el Security Group de las EC2.
  2. Security Group de las EC2 (App):

    • Inbound: SOLO permite tráfico en el puerto 3000 proveniente del Security Group del ALB. Nadie puede conectarse directo a la IP de la instancia (excepto nosotros por SSH).
  3. Security Group de RDS:

    • Inbound: SOLO permite tráfico en el puerto 5432 proveniente del Security Group de las EC2.

Resultado: La base de datos es invisible desde internet. Las instancias son invisibles desde internet (solo el ALB les habla).

El Costo del Crecimiento

La "fantasía" de los $25 USD se termina aquí. La redundancia y los servicios gestionados cuestan.

Concepto Costo Mensual (Estimado)
2x EC2 t3.small ~$30.00 USD
AWS RDS (db.t3.micro) ~$18.00 USD
Application Load Balancer ~$16.00 USD + tráfico
Data Transfer & Storage ~$10.00 USD
Total ~$75 - $85 USD

Pasamos de gastar lo de una cena barata a pagar una suscripción de software empresarial. Pero a cambio, ganamos:

  1. Resiliencia: Podemos perder un servidor y seguir operando.
  2. Escalabilidad: ¿Más usuarios? Agregamos una tercera instancia EC2 al balanceador y listo.
  3. Paz mental: Ya no reiniciamos servidores los domingos a la noche.

Conclusión

Separar responsabilidades es el primer paso real hacia una arquitectura distribuida. Aumentamos la complejidad y el costo, sí, pero compramos estabilidad.

¿Qué sigue? Con 1,000 usuarios, las consultas de reportes siguen siendo lentas aunque la DB esté separada.

Top comments (0)