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 Test | Herramienta | Propósito | Cuándo Ejecutar |
|---|---|---|---|
| Smoke test | K6/Locust | Verificar que sistema funciona bajo carga mínima | Cada deployment |
| Load test | K6/Locust | Validar rendimiento bajo carga esperada | Semanalmente, antes de releases |
| Stress test | K6/Locust | Encontrar puntos de quiebre | Mensualmente, después de cambios de infra |
| Spike test | K6/Locust | Validar comportamiento de auto-scaling | Después de cambios de políticas de scaling |
| Soak test | K6/Locust | Encontrar memory leaks, agotamiento de conexiones | Trimestralmente |
Checklist de Validación de Auto-Scaling
| Validación | Qué Verificar | Criterio de Éxito |
|---|---|---|
| Disparo de scale-up | Tiempo desde breach de threshold a nueva instancia | < 3 minutos |
| Capacidad de scale-up | Nuevas instancias manejan tráfico inmediatamente | Sin fallos de requests |
| Disparo de scale-down | Instancias removidas cuando carga disminuye | Dentro de período de cooldown |
| Seguridad de scale-down | Sin terminación prematura durante tráfico | Cero requests perdidos |
| Capacidad máxima | Sistema maneja carga de máximo de instancias | Cumple 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étrica | Objetivo | Cómo Rastrear |
|---|---|---|
| Latencia de scale-up | < 3 minutos desde trigger | Métricas CloudWatch ASG |
| Latencia P95 durante scale | < 500ms | Resultados K6/Locust |
| Tasa de error durante pico | < 1% | Resultados K6/Locust |
| Precisión de scale-down | Dentro de 2x período de cooldown | Logs de actividad ASG |
| Eficiencia de costos | Sin sobre-provisionamiento | AWS 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:
- Prueba tres escenarios como mínimo: carga sostenida, pico y recuperación
- Usa infraestructura efímera con Terraform para testing rentable
- Integra con CI/CD para validación regular
- Correlaciona métricas entre resultados de pruebas de carga y scaling de infraestructura
- 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
- Estrategias de Testing de Terraform - Fundamentos de testing de infraestructura
- Testing de Configuración de Red - Validar que la red puede manejar escala
- Testing de Backup y Disaster Recovery - Asegurar que DR funciona a escala
- Testing de Infraestructura AWS - Estrategias más amplias de testing AWS
- Estrategias de Testing de Kubernetes - Testing de orquestación de contenedores
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.
