En pocas palabras
Herramienta open source de Grafana Labs para pruebas de carga, estrés y soak, con scripts en JavaScript, escenarios configurables y umbrales automáticos.
Qué problema resuelve
Saber cuánta carga soporta un servicio solo cuando falla en producción, o pruebas de carga manuales que nadie repite.
Cuándo utilizarlo
Antes de campañas, al cambiar infraestructura o como fitness function de rendimiento. Ejemplo: validar que la API de solicitudes aguanta 500 solicitudes por minuto con p95 < 800 ms.
De la idea a la práctica
Funcionamiento, ejemplos y decisiones para pasar al siguiente nivel.
Tipos de prueba
| Tipo | Objetivo | Forma de la carga |
|---|---|---|
| Smoke | Verificar que el script y el sistema funcionan | 1–2 usuarios, 1 minuto |
| Load | Comportamiento con la carga esperada | Rampa hasta el nivel normal |
| Stress | Encontrar el punto de ruptura | Rampa por encima del nivel normal |
| Spike | Reacción a picos repentinos | Salto brusco y caída |
| Soak | Fugas de memoria y degradación en el tiempo | Carga normal durante horas |
Ejemplo: carga con tasa de llegada constante
import http from 'k6/http';
import { check } from 'k6';
export const options = {
scenarios: {
solicitudes: {
executor: 'constant-arrival-rate',
rate: 500,
timeUnit: '1m',
duration: '10m',
preAllocatedVUs: 50,
},
},
thresholds: {
http_req_failed: ['rate<0.01'],
http_req_duration: ['p(95)<800'],
},
};
export default function () {
const res = http.post(
`${__ENV.BASE_URL}/v1/solicitudes`,
JSON.stringify({ monto: 1500000, plazo: 12 }),
{ headers: { 'Content-Type': 'application/json' } },
);
check(res, { aceptada: (r) => r.status === 202 });
}
k6 run -e BASE_URL=https://staging.example.com carga-solicitudes.js
Si un umbral no se cumple, k6 termina con código distinto de cero y el pipeline falla.
Modelo abierto frente a cerrado
Los executors de tasa de llegada (constant-arrival-rate, ramping-arrival-rate) generan peticiones a un ritmo fijo aunque el sistema se ralentice, como ocurre con usuarios reales. Los de VUs (constant-vus, ramping-vus) esperan a que termine cada iteración, por lo que un servicio lento recibe automáticamente menos carga y oculta el problema.
Autenticación y datos de prueba
import http from 'k6/http';
import { SharedArray } from 'k6/data';
import papaparse from 'https://jslib.k6.io/papaparse/5.1.1/index.js';
const clientes = new SharedArray(
'clientes',
() => papaparse.parse(open('./clientes.csv'), { header: true }).data,
);
export function setup() {
const res = http.post(`${__ENV.BASE_URL}/oauth/token`, {
grant_type: 'client_credentials',
client_id: __ENV.CLIENT_ID,
client_secret: __ENV.CLIENT_SECRET,
});
return { token: res.json('access_token') }; // se pasa a cada iteración
}
export default function (datos) {
const cliente = clientes[Math.floor(Math.random() * clientes.length)];
http.get(`${__ENV.BASE_URL}/v1/clientes/${cliente.id}/creditos`, {
headers: { Authorization: `Bearer ${datos.token}` },
tags: { endpoint: 'creditos-cliente' },
});
}
SharedArray carga el CSV una vez en memoria compartida entre usuarios virtuales. Usa datos sintéticos y cuentas de prueba, nunca clientes reales.
Mezcla de tráfico realista
export const options = {
scenarios: {
consultas: {
executor: 'constant-arrival-rate',
rate: 300,
timeUnit: '1m',
duration: '15m',
preAllocatedVUs: 40,
exec: 'consultar',
},
solicitudes: {
executor: 'ramping-arrival-rate',
startRate: 20,
timeUnit: '1m',
preAllocatedVUs: 30,
stages: [
{ target: 100, duration: '5m' },
{ target: 100, duration: '10m' },
],
exec: 'solicitar',
},
},
thresholds: {
'http_req_duration{endpoint:creditos-cliente}': ['p(95)<300'],
'http_req_duration{endpoint:crear-solicitud}': ['p(95)<800'],
http_req_failed: ['rate<0.01'],
},
};
Los umbrales por etiqueta permiten tener objetivos distintos por endpoint, alineados con los SLO.
Leer los resultados
| Observación | Interpretación |
|---|---|
| Latencia estable hasta cierta tasa y luego crece rápido | Punto de saturación: busca el recurso limitante |
dropped_iterations mayor que cero |
k6 no tuvo VUs suficientes para la tasa pedida |
| Errores que aparecen tras minutos de carga sostenida | Agotamiento de pools, colas o memoria |
| p99 muy superior al p95 | Colas puntuales, GC, bloqueos en base de datos |
Correlaciona siempre con métricas del sistema bajo prueba (CPU, conexiones, GC, trazas) durante la misma ventana.
Ejecución distribuida y resultados
k6 run -o experimental-prometheus-rw carga.js # métricas a Prometheus/Mimir para Grafana
Para generar más carga de la que soporta una máquina, el k6 Operator reparte la prueba en varios pods de Kubernetes; Grafana Cloud k6 ofrece lo mismo como servicio y desde varias regiones. También existe una extensión de navegador (k6/browser) para medir rendimiento de frontend con métricas web.
Criterios de implementación
Ejecuta un smoke test en cada pull request y la prueba de carga completa en un entorno representativo de forma programada. Exporta métricas a Prometheus o Grafana Cloud para correlacionarlas con trazas de Grafana Tempo. Deriva los umbrales de escenarios de atributos de calidad y de los SLO, y avisa a los proveedores externos o usa mocks antes de lanzar carga contra ellos.
Continúa en las fuentes
Documentación oficial para verificar detalles y explorar más.