Escalabilidad y Capacidad

Escalado horizontal vs vertical, auto-scaling, capacity planning y load testing para preparar el sistema ante la demanda.

¿Qué es escalabilidad?

La escalabilidad es la capacidad de un sistema para manejar un aumento en la carga de trabajo sin degradar su rendimiento. Un sistema escalable puede crecer para atender más usuarios, más requests o más datos sin necesidad de rediseñarlo.

La escalabilidad no es lo mismo que el rendimiento:

  • Rendimiento: qué tan rápido responde el sistema con la carga actual
  • Escalabilidad: qué tan bien mantiene su rendimiento cuando la carga aumenta

Escalado vertical vs horizontal

Escalado vertical (scale up)

Consiste en agregar más recursos a una máquina existente: más CPU, más RAM, más disco.

Ventajas:

  • Simple de implementar — no requiere cambios en la aplicación
  • No introduce complejidad de distribución

Desventajas:

  • Tiene un límite físico — no se puede escalar infinitamente
  • Punto único de fallo — si la máquina cae, todo cae
  • Generalmente más caro a gran escala

Escalado horizontal (scale out)

Consiste en agregar más instancias del servicio y distribuir la carga entre ellas.

Ventajas:

  • Teóricamente ilimitado — se pueden agregar tantas instancias como sea necesario
  • Mayor resiliencia — si una instancia falla, las demás siguen funcionando
  • Más económico a gran escala (commodity hardware)

Desventajas:

  • Requiere que la aplicación sea stateless o maneje estado distribuido
  • Introduce complejidad: balanceo de carga, consistencia de datos, sesiones distribuidas
  • Más difícil de depurar

¿Cuándo usar cada uno?

EscenarioRecomendación
Base de datos relacionalVertical primero, luego read replicas
Servicios web statelessHorizontal
Colas de mensajesHorizontal (particiones)
CacheHorizontal (sharding)
Aplicación monolíticaVertical primero, luego descomponer

Auto-scaling

El auto-scaling ajusta automáticamente el número de instancias según la demanda actual.

Horizontal Pod Autoscaler (HPA) en Kubernetes

apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: my-service-hpa
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: my-service
  minReplicas: 2
  maxReplicas: 20
  metrics:
    - type: Resource
      resource:
        name: cpu
        target:
          type: Utilization
          averageUtilization: 70
    - type: Resource
      resource:
        name: memory
        target:
          type: Utilization
          averageUtilization: 80

Métricas para auto-scaling

  • CPU utilization: la más común, escala cuando el CPU supera un umbral
  • Memory utilization: útil para aplicaciones memory-intensive
  • Request rate: escala basándose en el número de requests por segundo
  • Queue depth: escala cuando la cola de mensajes crece
  • Custom metrics: latencia p99, errores por segundo, etc.

Buenas prácticas de auto-scaling

  1. Definir mínimos y máximos razonables: evitar escalar a 0 (cold start) o a infinito (costos)
  2. Cooldown periods: esperar antes de escalar de vuelta para evitar oscilaciones
  3. Escalar por adelantado: si se conocen picos predecibles (ej: Black Friday), pre-escalar
  4. Monitorear el auto-scaler: verificar que escala cuando debe y no oscila

Capacity planning

El capacity planning es el proceso de determinar cuántos recursos necesita el sistema para manejar la carga esperada.

Proceso

  1. Definir métricas clave: requests por segundo, usuarios concurrentes, tamaño de datos
  2. Medir la capacidad actual: cuánta carga puede manejar el sistema hoy
  3. Proyectar el crecimiento: basándose en datos históricos y planes de negocio
  4. Calcular los recursos necesarios: CPU, memoria, almacenamiento, ancho de banda
  5. Agregar margen de seguridad: típicamente 30-50% sobre la proyección

Ejemplo práctico

Situación actual:
- 1000 requests/segundo con 3 pods
- Cada pod maneja ~350 req/s antes de degradarse
- CPU promedio: 60%

Proyección a 6 meses:
- 3000 requests/segundo esperados
- Necesitamos: 3000 / 350 = ~9 pods
- Con margen de seguridad (30%): 12 pods
- Auto-scaling: min=9, max=15

Load testing

El load testing verifica que el sistema puede manejar la carga esperada y ayuda a identificar cuellos de botella.

Tipos de load testing

  • Load test: simula la carga esperada en condiciones normales
  • Stress test: incrementa la carga más allá de lo esperado para encontrar el punto de quiebre
  • Spike test: simula picos repentinos de tráfico
  • Soak test: mantiene carga constante durante horas para detectar memory leaks y degradación gradual

Herramientas populares

  • k6: herramienta moderna de load testing con scripts en JavaScript
  • Apache JMeter: herramienta clásica con interfaz gráfica
  • Locust: load testing en Python, distribuido
  • Artillery: load testing para APIs y microservicios
  • Gatling: load testing en Scala con reportes detallados

Ejemplo con k6

import http from 'k6/http';
import { check, sleep } from 'k6';

export const options = {
  stages: [
    { duration: '2m', target: 100 },  // ramp up a 100 usuarios
    { duration: '5m', target: 100 },  // mantener 100 usuarios
    { duration: '2m', target: 200 },  // subir a 200 usuarios
    { duration: '5m', target: 200 },  // mantener 200 usuarios
    { duration: '2m', target: 0 },    // ramp down
  ],
  thresholds: {
    http_req_duration: ['p(95)<500'],  // 95% de requests < 500ms
    http_req_failed: ['rate<0.01'],    // menos de 1% de errores
  },
};

export default function () {
  const res = http.get('https://api.example.com/products');
  check(res, {
    'status is 200': (r) => r.status === 200,
    'response time < 500ms': (r) => r.timings.duration < 500,
  });
  sleep(1);
}

Qué medir en load tests

  • Throughput: requests por segundo que el sistema puede manejar
  • Latencia: tiempo de respuesta (p50, p95, p99)
  • Error rate: porcentaje de requests que fallan
  • Recursos: CPU, memoria, conexiones de DB durante la prueba
  • Punto de quiebre: carga a la que el sistema empieza a degradarse

Patrones de escalabilidad

Caching

Reducir la carga en servicios y bases de datos almacenando respuestas frecuentes:

  • CDN: para assets estáticos y respuestas cacheables
  • Redis/Memcached: para datos de acceso frecuente
  • Cache de aplicación: para cálculos costosos

Database read replicas

Distribuir las lecturas entre múltiples réplicas de la base de datos mientras las escrituras van al primario.

Sharding

Dividir los datos horizontalmente entre múltiples instancias de base de datos basándose en una clave de partición.

Event-driven architecture

Desacoplar servicios usando eventos asincrónicos para absorber picos de carga sin saturar los servicios downstream.

Resumen

La escalabilidad requiere planificación y medición continua. El escalado horizontal es preferible para servicios stateless, el auto-scaling automatiza la respuesta a la demanda, el capacity planning anticipa las necesidades futuras y el load testing valida que el sistema puede manejar la carga esperada.