Parte de nuestra serie Performance & Scalability
Leer la guía completaPrueba de carga k6: pruebe sus API antes del lanzamiento
Enviar un producto sin pruebas de carga es una apuesta. No conoce su punto de ruptura hasta que los usuarios lo encuentran por usted, generalmente durante el lanzamiento de un producto, un momento viral o un pico de ventas. k6 es la herramienta de prueba de carga moderna que le permite escribir pruebas en JavaScript, ejecutarlas desde CI y descubrir su límite de rendimiento antes de que lo hagan los usuarios. Es fácil de desarrollar, eficiente en recursos (k6 usa rutinas, no subprocesos) y se integra limpiamente con Grafana y Prometheus para obtener métricas en tiempo real.
Esta guía cubre k6 desde el primer script hasta pruebas de carga complejas de múltiples escenarios, métricas personalizadas, umbrales, integración de CI y patrones de prueba seguros para producción para las API de Node.js/NestJS.
Conclusiones clave
- Los scripts de k6 son JavaScript pero se ejecutan en un tiempo de ejecución de Go, sin API de Node.js (sin
require, sinfs, sinsetTimeout)- Los Usuarios Virtuales (VU) son usuarios simulados concurrentes; Las iteraciones son ejecuciones de scripts individuales.
- Establezca siempre umbrales antes de ejecutar: los umbrales fallidos detienen la prueba y fallan el CI
- Utilice escenarios (vus constante, vus en rampa, tasa de llegada constante) para modelar patrones de tráfico reales
- Nunca cargue la producción de prueba sin coordinarse con operaciones: pruebe contra la puesta en escena o un clon de producción
- La métrica
http_req_durationes su métrica principal de SLA: p95 y p99 importan más que los promedios- Limite la velocidad de sus corredores k6: el tráfico de prueba de carga no debe agotar su presupuesto de límite de velocidad
- Utilice k6 Cloud o Grafana k6 para pruebas de carga distribuida en regiones geográficas
Instalación y primer script
# macOS
brew install k6
# Ubuntu/Debian
sudo gpg -k && sudo gpg --no-default-keyring --keyring /usr/share/keyrings/k6-archive-keyring.gpg --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys C5AD17C747E3415A3642D57D77C6C491D6AC1D69
echo "deb [signed-by=/usr/share/keyrings/k6-archive-keyring.gpg] https://dl.k6.io/deb stable main" | sudo tee /etc/apt/sources.list.d/k6.list
sudo apt-get update && sudo apt-get install k6
# Windows (via Chocolatey)
choco install k6
Tu primer script k6:
// k6/homepage-load.js
import http from 'k6/http';
import { check, sleep } from 'k6';
import { Rate } from 'k6/metrics';
// Custom metric: error rate
const errorRate = new Rate('error_rate');
export const options = {
// Ramp up to 50 VUs over 1 minute, hold 2 minutes, ramp down
stages: [
{ duration: '1m', target: 50 },
{ duration: '2m', target: 50 },
{ duration: '1m', target: 0 },
],
thresholds: {
http_req_duration: ['p(95)<500', 'p(99)<1000'], // 95th pct < 500ms
http_req_failed: ['rate<0.01'], // Less than 1% errors
error_rate: ['rate<0.01'],
},
};
const BASE_URL = __ENV.BASE_URL || 'http://localhost:3000';
export default function () {
const res = http.get(`${BASE_URL}/`);
const ok = check(res, {
'status is 200': (r) => r.status === 200,
'response time < 500ms': (r) => r.timings.duration < 500,
'body contains ECOSIRE': (r) => r.body.includes('ECOSIRE'),
});
errorRate.add(!ok);
sleep(1); // 1 second think time between iterations
}
Ejecútelo:
k6 run k6/homepage-load.js
k6 run --env BASE_URL=https://staging.ecosire.com k6/homepage-load.js
k6 run --vus 100 --duration 30s k6/homepage-load.js # Quick smoke test
Prueba de carga de API con autenticación
// k6/api-load.js
import http from 'k6/http';
import { check, group, sleep } from 'k6';
import { Counter, Trend } from 'k6/metrics';
const apiCalls = new Counter('api_calls');
const authDuration = new Trend('auth_duration');
export const options = {
scenarios: {
// Constant arrival rate: 100 requests/second regardless of VU count
constant_load: {
executor: 'constant-arrival-rate',
rate: 100,
timeUnit: '1s',
duration: '3m',
preAllocatedVUs: 50,
maxVUs: 200,
},
},
thresholds: {
http_req_duration: ['p(95)<300', 'p(99)<500'],
'http_req_duration{type:api}': ['p(95)<200'],
http_req_failed: ['rate<0.005'], // 0.5% error budget
},
};
const BASE_URL = __ENV.BASE_URL || 'http://localhost:3001';
// setup() runs once before VUs start
export function setup() {
// Authenticate and return tokens for VUs to use
const loginRes = http.post(`${BASE_URL}/auth/login`, JSON.stringify({
email: __ENV.TEST_EMAIL,
password: __ENV.TEST_PASSWORD,
}), {
headers: { 'Content-Type': 'application/json' },
});
check(loginRes, { 'login successful': (r) => r.status === 200 });
const { accessToken } = loginRes.json();
return { accessToken };
}
export default function (data) {
const { accessToken } = data;
const headers = {
Authorization: `Bearer ${accessToken}`,
'Content-Type': 'application/json',
};
group('Contacts API', () => {
// List contacts
const listRes = http.get(`${BASE_URL}/contacts?page=1&limit=20`, {
headers,
tags: { type: 'api', endpoint: 'contacts-list' },
});
check(listRes, {
'list contacts: 200': (r) => r.status === 200,
'list contacts: has items': (r) => r.json('data') !== null,
});
apiCalls.add(1);
sleep(0.5);
// Create a contact
const createRes = http.post(`${BASE_URL}/contacts`, JSON.stringify({
name: `Load Test User ${Date.now()}`,
email: `load-test-${Date.now()}@example.com`,
}), {
headers,
tags: { type: 'api', endpoint: 'contacts-create' },
});
check(createRes, {
'create contact: 201': (r) => r.status === 201,
});
apiCalls.add(1);
});
sleep(1);
}
// teardown() runs once after all VUs finish
export function teardown(data) {
// Clean up test data if needed
console.log(`Test complete. Data: ${JSON.stringify(data)}`);
}
Ampliación de escenarios de VU
Modele patrones de tráfico reales con múltiples etapas y escenarios:
// k6/stress-test.js
export const options = {
scenarios: {
// Scenario 1: Normal traffic baseline
normal_traffic: {
executor: 'ramping-vus',
startVUs: 0,
stages: [
{ duration: '2m', target: 20 },
{ duration: '5m', target: 20 },
],
gracefulRampDown: '30s',
},
// Scenario 2: Sudden spike (marketing campaign goes live)
traffic_spike: {
executor: 'ramping-vus',
startVUs: 0,
startTime: '7m', // Starts after baseline is stable
stages: [
{ duration: '30s', target: 200 }, // Rapid spike
{ duration: '2m', target: 200 }, // Hold spike
{ duration: '30s', target: 20 }, // Fall back
],
},
// Scenario 3: Soak test for memory leaks
soak_test: {
executor: 'constant-vus',
vus: 10,
duration: '30m', // Run for 30 minutes
startTime: '10m',
},
},
thresholds: {
'http_req_duration{scenario:normal_traffic}': ['p(95)<300'],
'http_req_duration{scenario:traffic_spike}': ['p(95)<800'],
http_req_failed: ['rate<0.02'],
},
};
Prueba de rastreo de blog/mapa del sitio
// k6/sitemap-crawl.js
import http from 'k6/http';
import { check, sleep } from 'k6';
import { SharedArray } from 'k6/data';
// Load URLs from sitemap (pre-fetched and saved as JSON)
const urls = new SharedArray('sitemap urls', function () {
// In real usage, fetch sitemap XML and parse URLs beforehand
// SharedArray is loaded once and shared across all VUs (memory efficient)
return JSON.parse(open('./sitemap-urls.json'));
});
export const options = {
vus: 20,
duration: '5m',
thresholds: {
http_req_duration: ['p(95)<2000'], // Public pages can be slower
http_req_failed: ['rate<0.01'],
},
};
export default function () {
// Each VU picks a random URL from the sitemap
const url = urls[Math.floor(Math.random() * urls.length)];
const res = http.get(url, {
headers: {
// Identify as load tester in logs
'User-Agent': 'k6-load-tester/1.0',
},
timeout: '10s',
});
check(res, {
'status is 200': (r) => r.status === 200,
'no error page': (r) => !r.body.includes('Error'),
'has canonical tag': (r) => r.body.includes('rel="canonical"'),
'response time < 2000ms': (r) => r.timings.duration < 2000,
});
sleep(Math.random() * 2 + 1); // Random 1-3 second think time
}
Métricas y umbrales personalizados
// k6/checkout-load.js
import http from 'k6/http';
import { check, sleep } from 'k6';
import { Counter, Rate, Trend, Gauge } from 'k6/metrics';
// Custom metrics for business-specific tracking
const checkoutSuccesses = new Counter('checkout_successes');
const checkoutFailures = new Counter('checkout_failures');
const checkoutDuration = new Trend('checkout_duration');
const activeCheckouts = new Gauge('active_checkouts');
export const options = {
stages: [
{ duration: '1m', target: 10 },
{ duration: '3m', target: 30 },
{ duration: '1m', target: 0 },
],
thresholds: {
checkout_duration: ['p(95)<3000'], // Checkout < 3s at p95
checkout_failures: ['count<10'], // Max 10 absolute failures
http_req_duration: ['p(99)<2000'],
},
};
export default function (data) {
activeCheckouts.add(1);
const start = Date.now();
// Step 1: Add to cart
const cartRes = http.post('/api/cart/add', JSON.stringify({
productId: 'prod_odoo_customization',
quantity: 1,
}), { headers: { 'Content-Type': 'application/json' } });
if (!check(cartRes, { 'add to cart: 200': (r) => r.status === 200 })) {
checkoutFailures.add(1);
activeCheckouts.add(-1);
return;
}
sleep(2);
// Step 2: Create checkout session
const checkoutRes = http.post('/api/billing/checkout', JSON.stringify({
cartId: cartRes.json('cartId'),
}), { headers: { 'Content-Type': 'application/json' } });
const success = check(checkoutRes, {
'checkout session created': (r) => r.status === 200,
'has session URL': (r) => r.json('url') !== null,
});
if (success) {
checkoutSuccesses.add(1);
checkoutDuration.add(Date.now() - start);
} else {
checkoutFailures.add(1);
}
activeCheckouts.add(-1);
sleep(1);
}
Integración de salida de Grafana
Transmita métricas de k6 a InfluxDB y visualícelas en Grafana durante la prueba:
# Run with InfluxDB output
k6 run --out influxdb=http://localhost:8086/k6 k6/api-load.js
# Or use the k6 Grafana integration (k6 v0.45+)
k6 run --out experimental-prometheus-rw k6/api-load.js
# docker-compose.monitoring.yml
services:
influxdb:
image: influxdb:2.7
ports: ["8086:8086"]
environment:
INFLUXDB_DB: k6
INFLUXDB_ADMIN_USER: admin
INFLUXDB_ADMIN_PASSWORD: password
grafana:
image: grafana/grafana:latest
ports: ["3030:3000"]
environment:
GF_AUTH_ANONYMOUS_ENABLED: "true"
volumes:
- ./grafana/dashboards:/etc/grafana/provisioning/dashboards
- ./grafana/datasources:/etc/grafana/provisioning/datasources
Integración de CI
# .github/workflows/ci.yml (load test section)
load-tests:
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main' # Only on main branch
needs: [deploy-staging]
steps:
- uses: actions/checkout@v4
- name: Install k6
run: |
sudo gpg --no-default-keyring --keyring /usr/share/keyrings/k6-archive-keyring.gpg \
--keyserver hkp://keyserver.ubuntu.com:80 --recv-keys C5AD17C747E3415A3642D57D77C6C491D6AC1D69
echo "deb [signed-by=/usr/share/keyrings/k6-archive-keyring.gpg] https://dl.k6.io/deb stable main" \
| sudo tee /etc/apt/sources.list.d/k6.list
sudo apt-get update && sudo apt-get install k6
- name: Run smoke test (30 VUs, 60s)
run: |
k6 run --vus 30 --duration 60s \
--env BASE_URL=${{ secrets.STAGING_URL }} \
--env TEST_EMAIL=${{ secrets.TEST_EMAIL }} \
--env TEST_PASSWORD=${{ secrets.TEST_PASSWORD }} \
apps/api/k6/api-load.js
- name: Upload k6 results
uses: actions/upload-artifact@v4
if: always()
with:
name: k6-results
path: k6-results/
Líneas base de desempeño
Establecer líneas de base y alertar sobre regresiones:
| Punto final | p50 | p95 | p99 | Presupuesto erróneo |
|---|---|---|---|---|
GET / (página de inicio) | 120 ms | 400 ms | 800 ms | 0,1% |
| CÓDIGO0 | 50 ms | 150 ms | 300 ms | 0,5% |
| CÓDIGO0 | 80 ms | 200 ms | 400 ms | 0,5% |
| CÓDIGO0 | 200 ms | 500 ms | 1000 ms | 1% |
| CÓDIGO0 | 500 ms | 1500 ms | 3000 ms | 1% |
| CÓDIGO0 | 100 ms | 300 ms | 600 ms | 0,1% |
Preguntas frecuentes
¿Cuál es la diferencia entre VU y solicitudes por segundo?
Los VU (usuarios virtuales) son usuarios simulados concurrentes. Cada VU ejecuta su función predeterminada en un bucle. Las solicitudes por segundo dependen de la velocidad con la que se ejecuta cada iteración. Si cada iteración de VU dura 2 segundos (incluido el tiempo de suspensión) y tienes 100 VU, obtienes aproximadamente 50 RPS. Utilice el ejecutor constant-arrival-rate cuando necesite apuntar a un RPS específico independientemente de la duración de la iteración.
¿Debo cargar la prueba en producción o en preparación?
Prefiere siempre la puesta en escena. Las pruebas de carga de producción corren el riesgo de afectar a usuarios reales, agotar los límites de velocidad, crear pedidos reales a partir de datos de prueba y desencadenar eventos de webhook reales. Si debe probar la producción, hágalo durante períodos de poco tráfico, utilice tokens de pago de prueba y tenga un plan de reversión. k6 Cloud admite la generación de carga distribuida geográficamente para que pueda probar el rendimiento del borde de su CDN sin tocar el origen.
¿Cómo manejo la caducidad del token de autenticación durante pruebas largas?
Para pruebas cortas (menos de 15 minutos), obtenga un token en setup() y páselo a todas las VU a través de data. Para pruebas largas (más de 15 minutos), implemente la actualización del token en el script de prueba: verifique la antigüedad del token en la función predeterminada y actualice cuando se acerque a su vencimiento. Almacene el token en una variable JavaScript local para cada VU.
¿Cuál es la diferencia entre stages y scenarios?
stages es una abreviatura de un único escenario ramping-vus, bueno para patrones simples de aceleración/mantenimiento/desaceleración. scenarios le brinda control total: múltiples patrones de tráfico simultáneos, diferentes ejecutores por escenario, umbrales por escenario y etiquetado de escenarios en métricas. Utilice scenarios para pruebas realistas de múltiples patrones (línea de base + pico + remojo simultáneamente).
¿Cuántas VU puede manejar un solo proceso k6?
Un solo proceso k6 en hardware moderno puede soportar entre 5000 y 10 000 VU y generar entre 50 000 y 100 000 RPS, dependiendo de la complejidad del script y el tamaño de la respuesta. Para cargas más altas, use k6 cloud o ejecute varias instancias k6 detrás de un distribuidor de carga. Cada VU es extremadamente liviana (una gorutina): k6 usa muchos menos recursos que JMeter o Gatling para recuentos de VU equivalentes.
Próximos pasos
Las pruebas de carga revelan el verdadero perfil de rendimiento de su aplicación antes de que los usuarios la descubran bajo estrés. Los patrones de esta guía (escenarios de aceleración, umbrales personalizados, integración de CI y paneles de Grafana) le brindan la infraestructura para encontrar y solucionar cuellos de botella de rendimiento de forma continua.
ECOSIRE crea API NestJS con rendimiento validado con pruebas de carga k6 que cubren la página de inicio, puntos finales de API, flujos de pago y rastreos completos de mapas del sitio. Explore nuestros servicios de ingeniería backend para saber cómo construimos para lograr rendimiento desde el primer día.
Escrito por
ECOSIRE TeamTechnical Writing
The ECOSIRE technical writing team covers Odoo ERP, Shopify eCommerce, AI agents, Power BI analytics, GoHighLevel automation, and enterprise software best practices. Our guides help businesses make informed technology decisions.
ECOSIRE
Haga crecer su negocio con ECOSIRE
Soluciones empresariales en ERP, comercio electrónico, inteligencia artificial, análisis y automatización.
Artículos relacionados
Integración de la API de Hepsiburada con Odoo: guía de configuración completa
Guía completa para integrar Hepsiburada con Odoo ERP vía API. Automatice pedidos, inventario y cumplimiento en el mercado confiable de Turquía.
Shopify Integration Hub: Cómo conectar Shopify a cualquier sistema en 2026
Guía completa para integraciones de Shopify: API, webhooks, middleware, métodos iPaaS. Conecte Shopify a ERP, contabilidad, CRM, mercados y sistemas POS.
Limitación de tasa de API: patrones y mejores prácticas
Limitación de tasa de API maestra con depósito de tokens, ventana deslizante y patrones de contador fijos. Proteja su backend con el acelerador NestJS, Redis y ejemplos de configuración del mundo real.
Más de Performance & Scalability
Depuración y monitoreo de Webhook: la guía completa de solución de problemas
Domine la depuración de webhooks con esta guía completa que cubre patrones de falla, herramientas de depuración, estrategias de reintento, paneles de monitoreo y mejores prácticas de seguridad.
Configuración de producción de Nginx: SSL, almacenamiento en caché y seguridad
Guía de configuración de producción de Nginx: terminación SSL, HTTP/2, encabezados de almacenamiento en caché, encabezados de seguridad, limitación de velocidad, configuración de proxy inverso y patrones de integración de Cloudflare.
Ajuste del rendimiento de Odoo: PostgreSQL y optimización del servidor
Guía experta para ajustar el rendimiento de Odoo 19. Cubre la configuración, indexación, optimización de consultas, almacenamiento en caché de Nginx y dimensionamiento del servidor de PostgreSQL para implementaciones empresariales.
Odoo vs Acumatica: ERP en la nube para empresas en crecimiento
Comparación de Odoo y Acumatica para 2026: modelos de precios únicos, escalabilidad, profundidad de fabricación y qué ERP en la nube se adapta a su trayectoria de crecimiento.
Prueba y seguimiento de agentes de IA en producción
Una guía completa para probar y monitorear agentes de IA en entornos de producción. Cubre marcos de evaluación, observabilidad, detección de deriva y respuesta a incidentes para implementaciones de OpenClaw.
Agentes de monitoreo de cumplimiento con OpenClaw
Implemente agentes de OpenClaw AI para un monitoreo continuo del cumplimiento. Automatice las comprobaciones reglamentarias, la aplicación de políticas, la generación de pistas de auditoría y los informes de cumplimiento.