TL;DR

Los fallos de escalabilidad de infraestructura cuestan un promedio de $300.000 por hora en ingresos perdidos para plataformas de e-commerce, según el Gartner IT Downtime Study 2024. Según la CNCF Survey 2024, el 68% de las organizaciones con Kubernetes experimentaron problemas de auto-scaling en producción que podrían haberse detectado con pruebas pre-producción. Las pruebas de escalabilidad validan el ciclo completo del auto-scaling: condiciones de activación, velocidad de escalado, límites máximos y degradación gradual al alcanzar la capacidad máxima. Las prácticas de Chaos Engineering de Netflix muestran que los equipos que prueban el comportamiento del auto-scaling reducen los incidentes de escalado en producción un 80%.

Enfoques Asistidos por IA

Las herramientas de IA destacan generando scripts de pruebas de carga y analizando patrones de rendimiento.

Generando escenarios de prueba de carga con K6:

Escribe un script de prueba de carga K6 que valide comportamiento de auto-scaling:

Aplicación objetivo: API REST con endpoints /api/users, /api/orders
Infraestructura: AWS ALB + Auto Scaling Group (min: 2, max: 10, CPU objetivo: 70%)

Incluye tres etapas de prueba:

1. Calentamiento: Incremento gradual a 100 VUs en 2 minutos
2. Carga sostenida: Mantener 500 VUs por 10 minutos (debe disparar scale-up)
3. Pico: Ráfaga a 2000 VUs por 1 minuto, luego volver a 500
4. Enfriamiento: Disminuir gradualmente a 0 en 5 minutos (debe disparar scale-down)

Agrega thresholds para:

- Tiempo de respuesta p95 < 500ms
- Tasa de error < 1%
- Métricas personalizadas para rastrear eventos de scaling

Incluye integración con CloudWatch para correlacionar carga con conteo de instancias ASG.

Analizando comportamiento de auto-scaling:

Analiza estos resultados de prueba de carga y métricas de auto-scaling:

Timeline de prueba de carga:

- 0-2min: Rampa a 100 VUs, p95=120ms
- 2-12min: 500 VUs sostenidos, p95 comenzó en 150ms, creció a 800ms al minuto 8
- ASG escaló de 2 a 4 instancias al minuto 6, a 6 instancias al minuto 10
- 12-13min: Pico a 2000 VUs, p95=2500ms, tasa de error 15%

Preguntas:

1. ¿Es la política de scaling demasiado lenta? ¿Cuál debería ser el valor de target tracking?
2. ¿Por qué la latencia creció antes de que ocurriera el scaling?
3. ¿Qué explica la alta tasa de error durante el pico?
4. Recomienda cambios específicos a la configuración de auto-scaling.

Creando pruebas de carga distribuidas con Locust:

Crea una prueba de carga Locust para flujo de checkout de e-commerce:

1. Navegar productos (70% del tráfico)
2. Agregar al carrito (20% del tráfico)
3. Checkout (10% del tráfico)

Incluye:

- Tiempos de espera realistas entre acciones
- Manejo de sesión para estado del carrito
- Métricas personalizadas para cada etapa del flujo
- Configuración de setup distribuido para ejecutar en Kubernetes

Muestra cómo ejecutar esto con 10 pods worker para generar 50,000 usuarios concurrentes.

Cuándo Usar Diferentes Enfoques de Testing

Framework de Decisión de Estrategia de Testing

Tipo de TestHerramientaPropósitoCuándo Ejecutar
Smoke testK6/LocustVerificar que sistema funciona bajo carga mínimaCada deployment
Load testK6/LocustValidar rendimiento bajo carga esperadaSemanalmente, antes de releases
Stress testK6/LocustEncontrar puntos de quiebreMensualmente, después de cambios de infra
Spike testK6/LocustValidar comportamiento de auto-scalingDespués de cambios de políticas de scaling
Soak testK6/LocustEncontrar memory leaks, agotamiento de conexionesTrimestralmente

Checklist de Validación de Auto-Scaling

ValidaciónQué VerificarCriterio de Éxito
Disparo de scale-upTiempo desde breach de threshold a nueva instancia< 3 minutos
Capacidad de scale-upNuevas instancias manejan tráfico inmediatamenteSin fallos de requests
Disparo de scale-downInstancias removidas cuando carga disminuyeDentro de período de cooldown
Seguridad de scale-downSin terminación prematura durante tráficoCero requests perdidos
Capacidad máximaSistema maneja carga de máximo de instanciasCumple SLA a máxima escala

K6 para Testing de Escalabilidad

Test Básico de Validación de Auto-Scaling

// tests/autoscaling-validation.js
import http from 'k6/http';
import { check, sleep } from 'k6';
import { Rate, Trend } from 'k6/metrics';

// Métricas personalizadas
const errorRate = new Rate('errors');
const scalingLatency = new Trend('scaling_latency');

export const options = {
  scenarios: {
    // Etapa 1: Calentamiento
    warmup: {
      executor: 'ramping-vus',
      startVUs: 0,
      stages: [
        { duration: '2m', target: 100 },
      ],
      gracefulRampDown: '0s',
      exec: 'defaultScenario',
    },
    // Etapa 2: Carga sostenida (debe disparar scale-up)
    sustained: {
      executor: 'constant-vus',
      vus: 500,
      duration: '10m',
      startTime: '2m',
      exec: 'defaultScenario',
    },
    // Etapa 3: Pico (stress test de auto-scaling)
    spike: {
      executor: 'ramping-vus',
      startVUs: 500,
      stages: [
        { duration: '30s', target: 2000 },
        { duration: '1m', target: 2000 },
        { duration: '30s', target: 500 },
      ],
      startTime: '12m',
      exec: 'defaultScenario',
    },
    // Etapa 4: Enfriamiento (debe disparar scale-down)
    cooldown: {
      executor: 'ramping-vus',
      startVUs: 500,
      stages: [
        { duration: '5m', target: 0 },
      ],
      startTime: '14m',
      gracefulRampDown: '30s',
      exec: 'defaultScenario',
    },
  },
  thresholds: {
    http_req_duration: ['p(95)<500'],  // 95% de requests bajo 500ms
    errors: ['rate<0.01'],              // Tasa de error bajo 1%
    http_req_failed: ['rate<0.01'],     // Requests fallidos bajo 1%
  },
};

const BASE_URL = __ENV.TARGET_URL || 'https://api.example.com';

export function defaultScenario() {
  // Simular uso realista de API
  const endpoints = [
    { path: '/api/users', weight: 0.5 },
    { path: '/api/orders', weight: 0.3 },
    { path: '/api/products', weight: 0.2 },
  ];

  const random = Math.random();
  let cumulative = 0;
  let selectedEndpoint = endpoints[0].path;

  for (const endpoint of endpoints) {
    cumulative += endpoint.weight;
    if (random <= cumulative) {
      selectedEndpoint = endpoint.path;
      break;
    }
  }

  const response = http.get(`${BASE_URL}${selectedEndpoint}`);

  const success = check(response, {
    'status is 200': (r) => r.status === 200,
    'response time < 500ms': (r) => r.timings.duration < 500,
  });

  errorRate.add(!success);

  // Tiempo de espera realista
  sleep(Math.random() * 2 + 1);
}

Locust para Testing de Escalabilidad

Configuración de Prueba de Carga Distribuida

# locustfile.py
from locust import HttpUser, task, between, events
from locust.runners import MasterRunner
import time
import logging

class WebsiteUser(HttpUser):
    wait_time = between(1, 3)

    def on_start(self):
        """Inicializar sesión de usuario."""
        self.client.headers = {'Content-Type': 'application/json'}

    @task(5)
    def browse_products(self):
        """70% del tráfico - Navegar productos."""
        with self.client.get("/api/products", catch_response=True) as response:
            if response.status_code == 200:
                response.success()
            else:
                response.failure(f"Got status {response.status_code}")

    @task(2)
    def view_product_detail(self):
        """20% del tráfico - Ver detalles de producto."""
        product_id = self.get_random_product_id()
        self.client.get(f"/api/products/{product_id}")

    @task(1)
    def checkout_flow(self):
        """10% del tráfico - Flujo completo de checkout."""
        # Agregar al carrito
        self.client.post("/api/cart", json={
            "product_id": self.get_random_product_id(),
            "quantity": 1
        })

        # Checkout
        with self.client.post("/api/checkout", json={
            "payment_method": "card"
        }, catch_response=True) as response:
            if response.status_code in [200, 201]:
                response.success()
            elif response.status_code == 503:
                response.failure("Servicio no disponible - ¿problema de scaling?")

    def get_random_product_id(self):
        import random
        return random.randint(1, 1000)


# Métricas personalizadas para análisis de scaling
@events.request.add_listener
def track_response_time(request_type, name, response_time, response_length, **kwargs):
    if response_time > 1000:  # Registrar requests lentos
        logging.warning(f"Request lento: {name} tomó {response_time}ms")

Deployment en Kubernetes para Locust Distribuido

# locust-master.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: locust-master
spec:
  replicas: 1
  selector:
    matchLabels:
      app: locust
      role: master
  template:
    metadata:
      labels:
        app: locust
        role: master
    spec:
      containers:

        - name: locust
          image: locustio/locust:2.20.0
          args:

            - --master
            - -f
            - /mnt/locust/locustfile.py
            - --host
            - $(TARGET_HOST)
          env:

            - name: TARGET_HOST
              valueFrom:
                configMapKeyRef:
                  name: locust-config
                  key: target_host
          ports:

            - containerPort: 8089
            - containerPort: 5557
          volumeMounts:

            - name: locust-scripts
              mountPath: /mnt/locust
      volumes:

        - name: locust-scripts
          configMap:
            name: locust-scripts
---
# locust-worker.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: locust-worker
spec:
  replicas: 10  # 10 workers para testing distribuido
  selector:
    matchLabels:
      app: locust
      role: worker
  template:
    metadata:
      labels:
        app: locust
        role: worker
    spec:
      containers:

        - name: locust
          image: locustio/locust:2.20.0
          args:

            - --worker
            - --master-host=locust-master
            - -f
            - /mnt/locust/locustfile.py
          volumeMounts:

            - name: locust-scripts
              mountPath: /mnt/locust
      volumes:

        - name: locust-scripts
          configMap:
            name: locust-scripts

Terraform para Infraestructura de Pruebas de Carga

Entorno de Prueba de Carga Efímero

# modules/load-test-infra/main.tf

variable "run_load_test" {
  description = "Establecer a true para provisionar infraestructura de pruebas"
  type        = bool
  default     = false
}

variable "worker_count" {
  description = "Número de workers K6/Locust"
  type        = number
  default     = 5
}

# Cluster ECS para generadores de carga
resource "aws_ecs_cluster" "load_test" {
  count = var.run_load_test ? 1 : 0
  name  = "load-test-cluster"

  setting {
    name  = "containerInsights"
    value = "enabled"
  }

  tags = {
    Purpose   = "LoadTesting"
    AutoClean = "true"
  }
}

Integración CI/CD

Workflow GitHub Actions para Pruebas de Carga

name: Testing de Escalabilidad

on:
  schedule:

    - cron: '0 4 * * 1'  # Semanalmente Lunes 4 AM
  workflow_dispatch:
    inputs:
      test_duration:
        description: 'Duración del test (ej., 10m, 1h)'
        default: '20m'
      max_vus:
        description: 'Máximo usuarios virtuales'
        default: '1000'

jobs:
  load-test:
    runs-on: ubuntu-latest
    environment: load-test

    steps:

      - uses: actions/checkout@v4

      - name: Configurar credenciales AWS
        uses: aws-actions/configure-aws-credentials@v4
        with:
          role-to-assume: ${{ secrets.LOAD_TEST_ROLE_ARN }}
          aws-region: us-east-1

      - name: Setup K6
        run: |
          sudo apt-get update && sudo apt-get install k6

      - name: Ejecutar prueba de carga
        run: |
          k6 run tests/autoscaling-validation.js \
            --env TARGET_URL=${{ vars.TARGET_URL }} \
            --duration ${{ inputs.test_duration || '20m' }} \
            --vus ${{ inputs.max_vus || '1000' }} \
            --out json=results/k6-results.json

Midiendo el Éxito

MétricaObjetivoCómo Rastrear
Latencia de scale-up< 3 minutos desde triggerMétricas CloudWatch ASG
Latencia P95 durante scale< 500msResultados K6/Locust
Tasa de error durante pico< 1%Resultados K6/Locust
Precisión de scale-downDentro de 2x período de cooldownLogs de actividad ASG
Eficiencia de costosSin sobre-provisionamientoAWS Cost Explorer

Señales de que tu testing de escalabilidad no funciona:

  • Tests pasan pero producción aún tiene problemas de scaling
  • Políticas de scaling nunca se disparan durante tests (carga muy baja)
  • Entorno de prueba no coincide con producción (diferentes tipos de instancia, límites)
  • Resultados varían mucho entre ejecuciones de test (baseline inconsistente)

Conclusión

El testing efectivo de escalabilidad de infraestructura requiere escenarios realistas y herramientas apropiadas:

  1. Prueba tres escenarios como mínimo: carga sostenida, pico y recuperación
  2. Usa infraestructura efímera con Terraform para testing rentable
  3. Integra con CI/CD para validación regular
  4. Correlaciona métricas entre resultados de pruebas de carga y scaling de infraestructura
  5. Documenta thresholds y alerta cuando tests fallen

La idea clave: las políticas de auto-scaling necesitan validación bajo condiciones de carga reales. Los cálculos teóricos no son suficientes—prueba tu infraestructura antes de que lo hagan tus usuarios.

Ver También

Recursos Oficiales

“Las pruebas de escalabilidad nos salvaron de un desastre en Black Friday. Descubrimos que nuestro auto-scaling estaba configurado para esperar 5 minutos antes de escalar — bien para picos normales, catastrófico para ventas flash. Las pruebas lo encontraron en staging; arreglarlo tomó 10 minutos. Descubrirlo en producción habría costado millones.” — Yuri Kan, Senior QA Lead

FAQ

¿Qué es el testing de escalabilidad de infraestructura?

Validar el comportamiento del auto-scaling: HPA se activa en umbrales correctos de CPU/memoria, el escalado se completa en el SLA (60-90s para Kubernetes) y el rendimiento degrada gradualmente en capacidad pico.

¿Cómo probar el auto-scaling de Kubernetes?

Genera carga con k6/Locust, monitorea con kubectl get hpa, verifica que los pods escalan en 60 segundos. Prueba el escalado hacia abajo eliminando carga. Prueba límites superando el máximo de pods.

¿Escalado horizontal vs vertical?

Horizontal (añadir instancias): balanceo de carga, sin estado, sesiones. Vertical (instancias más grandes): límites de recursos, presión de memoria. La mayoría de sistemas cloud usan escalado horizontal principalmente.

¿Cómo probar los límites de infraestructura cloud?

Aumenta gradualmente la carga hasta que se active el auto-scaling. Verifica que se completa en el SLA. Prueba al máximo configurado. Usa ingeniería del caos para probar fallos de aprovisionamiento de recursos.