# Pruebas de rendimiento con k6 y Grafana

**Categoría:** Testing y QA · **Nivel:** Intermedio · **Lectura:** 15 min
**Publicada:** 2026-07-27 · **Actualizada:** 2026-07-27 · **Autoría:** Underc0de
**Versión HTML (canónica):** https://underc0de.org/guias/testing/pruebas-de-rendimiento-con-k6-y-grafana/

Una prueba de carga sin umbrales produce gráficos que nadie mira. Con umbrales produce una decisión que puede detener un despliegue. Acá está cómo llegar a la segunda, empezando por elegir bien el tipo de prueba.

## Respuesta rápida

**k6** es una herramienta de pruebas de rendimiento de código abierto donde los tests se escriben en **JavaScript** y se ejecutan desde la línea de comandos, así que viven en el repositorio como cualquier otro código. Su documentación define **seis tipos de prueba** —humo, carga media, estrés, resistencia, pico y punto de quiebre—, y cada uno responde una pregunta distinta. La pieza que convierte una medición en un control automático son los **umbrales**: «los criterios de aprobación o fallo que definís para las métricas». Si no se cumplen, k6 **sale con código distinto de cero** y la integración continua puede detener el despliegue. Y una regla que ahorra disgustos: los umbrales se escriben sobre **percentiles**, no sobre promedios.

## Las dos preguntas que responde

Las pruebas de rendimiento suelen postergarse porque se plantean como un proyecto grande, cuando en realidad responden dos preguntas bastante concretas:

- **¿Aguanta lo que va a recibir?** Con la carga esperada, ¿responde en un tiempo aceptable y sin errores?
- **¿Empeoró desde la última vez?** Esta es la que más valor tiene a largo plazo y la que más se ignora, porque exige tener una medición anterior con la que comparar.

La segunda pregunta explica por qué el rendimiento conviene medirlo **seguido y automáticamente**, en lugar de una vez antes de salir a producción. Una degradación del 30 % introducida por un cambio se detecta comparando; no se detecta mirando un número aislado, porque nadie sabe si 400 milisegundos son buenos o malos sin referencia.

## Qué es k6

Su documentación lo define como «una herramienta de pruebas de rendimiento **de código abierto, extensible y pensada para desarrolladores**, que ayuda a detectar problemas de rendimiento temprano y a mejorar la fiabilidad de forma proactiva».

En la práctica, lo que lo distingue son tres decisiones de diseño:

- **Los tests se escriben en JavaScript.** No hay que grabar sesiones ni aprender un formato propio.
- **Se ejecuta desde la línea de comandos.** Sin interfaz gráfica de por medio, lo que lo hace natural en integración continua.
- **El test es código.** Vive en el repositorio, se versiona, se revisa en un *pull request* y evoluciona con la aplicación.

Esa última es la que más cambia la dinámica de un equipo: el rendimiento deja de ser un informe que produce alguien externo y pasa a ser parte del mismo flujo que el resto de las pruebas.

## Los seis tipos de prueba

Elegir mal el tipo es la causa más común de una prueba que no dice nada. La documentación de k6 define seis, cada uno con su perfil de **usuarios virtuales** —los *VUs*, que simulan personas usando el sistema en paralelo— y su duración:

![Los seis tipos de prueba de carga de k6 con su perfil: humo, carga media, estrés, resistencia, pico y punto de quiebre. Debajo, los umbrales como criterios de aprobación con un ejemplo de configuración y la explicación de que k6 sale con código distinto de cero cuando fallan. Al final, la advertencia de usar percentiles en lugar del promedio.](../../assets/img/guias/pruebas-de-rendimiento-con-k6-y-grafana-tipos.svg)

| Tipo | Qué verifica | Perfil |
|---|---|---|
| **Humo** *(smoke)* | Que el script funcione y que el sistema responda con carga mínima | Pocos VUs, segundos o minutos |
| **Carga media** *(average-load)* | Cómo se comporta en las condiciones normales esperadas | VUs de producción, 5 a 60 min |
| **Estrés** *(stress)* | Cómo se comporta en sus límites, con carga sobre la media | Muchos VUs, 5 a 60 min |
| **Resistencia** *(soak)* | Fiabilidad y rendimiento durante períodos prolongados | VUs medios, horas |
| **Pico** *(spike)* | Comportamiento y supervivencia ante subidas súbitas y masivas | Muchísimos VUs, pocos minutos |
| **Punto de quiebre** *(breakpoint)* | El límite de capacidad del sistema | VUs suben hasta el fallo |

> **El orden que conviene, y por qué.** **Humo primero, siempre.** Un script con un error, una URL mal escrita o una autenticación vencida produce resultados que parecen un problema de rendimiento y no lo son. La prueba de humo es barata y descarta eso en segundos. Después, **carga media**, que es la que conviene dejar corriendo en integración continua. Los otros cuatro son ejercicios puntuales con una pregunta específica detrás, no cosas que se corran en cada cambio.

## Tu primer script

La estructura mínima de un test de k6 tiene tres partes: las importaciones, las **opciones** que describen la carga y los criterios, y una función por defecto que es lo que ejecuta cada usuario virtual en cada iteración.

```javascript
// prueba-humo.js
import http from 'k6/http';
import { check, sleep } from 'k6';

export const options = {
  vus: 3,                // tres usuarios virtuales
  duration: '30s',       // durante treinta segundos
  thresholds: {
    http_req_failed: ['rate<0.01'],     // menos del 1 % de errores
    http_req_duration: ['p(95)<500'],   // el 95 % bajo 500 ms
  },
};

export default function () {
  const res = http.get('https://tu-entorno-de-pruebas/api/productos');

  check(res, {
    'responde 200': (r) => r.status === 200,
    'trae productos': (r) => r.json().length > 0,
  });

  sleep(1);  // pausa entre iteraciones, para parecerse a una persona
}
```

Y se ejecuta con un comando:

```bash
k6 run prueba-humo.js
```

Dos detalles que conviene entender desde el principio:

- **`check` no falla la prueba.** Registra si una condición se cumplió y aparece en el resumen, pero un *check* en rojo no cambia el código de salida. Lo que falla la prueba son los umbrales. Es una distinción que confunde a todo el mundo la primera vez.
- **`sleep` importa más de lo que parece.** Sin pausas, cada usuario virtual dispara peticiones tan rápido como puede, lo que no se parece a ninguna persona real y produce números que no representan nada.

## Escenarios y ejecutores

Con `vus` y `duration` alcanza para empezar, pero describe una carga plana. Los **escenarios** permiten algo mejor: según la documentación, «configuran cómo se programan los usuarios virtuales y las iteraciones con detalle granular» y con ellos «podés modelar distintas *cargas de trabajo* o patrones de tráfico».

Cada escenario usa un **ejecutor**, y la elección del ejecutor define si modelás la carga por *cantidad de usuarios* o por *ritmo de llegada*:

| Ejecutor | Qué hace |
|---|---|
| `shared-iterations` | Reparte un total de iteraciones entre los usuarios virtuales |
| `per-vu-iterations` | Cada usuario virtual ejecuta la cantidad de iteraciones configurada |
| `constant-vus` | Mantiene una cantidad constante de usuarios virtuales |
| `ramping-vus` | Sube y baja la cantidad de usuarios según etapas configuradas |
| `constant-arrival-rate` | Inicia iteraciones a un ritmo constante |
| `ramping-arrival-rate` | Sube o baja el ritmo de iteraciones según etapas |

La distinción entre los dos últimos y el resto es la que más cambia los resultados. Modelar por **usuarios** significa que si el sistema se pone lento, cada usuario hace menos peticiones y la carga real *baja*: el sistema se protege solo. Modelar por **ritmo de llegada** significa que las peticiones entran al ritmo fijado sin importar cuánto tarde el sistema, que es lo que hace el tráfico real. Para probar un sistema que puede saturarse, el segundo enfoque es mucho más honesto.

```javascript
// Una prueba de pico: sube fuerte, se mantiene poco, baja
export const options = {
  scenarios: {
    pico: {
      executor: 'ramping-vus',
      startVUs: 10,
      stages: [
        { duration: '30s', target: 10 },    // tráfico normal
        { duration: '20s', target: 400 },   // el golpe
        { duration: '1m', target: 400 },    // se sostiene
        { duration: '30s', target: 10 },    // vuelve a lo normal
      ],
    },
  },
};
```

Ese último tramo, el de bajada, es el que más gente omite y el que más información da: sirve para ver si el sistema **se recupera** después del pico o si queda degradado.

## Umbrales: la parte que importa

Si hay una sección de esta guía que conviene aplicar, es esta. La documentación define los umbrales como «**los criterios de aprobación o fallo que definís para las métricas de tu prueba**», y agrega lo que los vuelve decisivos: «si el rendimiento del sistema bajo prueba no cumple las condiciones de tu umbral, **la prueba termina con estado de fallo**».

```javascript
export const options = {
  thresholds: {
    http_req_failed: ['rate<0.01'],      // errores por debajo del 1 %
    http_req_duration: ['p(95)<200'],    // el 95 % de las peticiones bajo 200 ms
  },
};
```

Y el detalle operativo que cierra el círculo: cuando un umbral falla, **k6 sale con un código distinto de cero**; cuando todos pasan, sale con código 0. Eso es exactamente lo que necesita un pipeline para detener un despliegue sin que nadie tenga que mirar un gráfico.

> **Los umbrales son tus objetivos de servicio, escritos en código.** La documentación lo plantea así: sirven para codificar los objetivos de nivel de servicio en criterios ejecutables. Es la forma más concreta de convertir «la aplicación tiene que andar rápido» —que no se puede verificar— en «el 95 % de las peticiones al listado responde en menos de 200 milisegundos y menos del 1 % falla», que sí. Y esa conversación, la de qué números son aceptables, es una decisión de producto que conviene tener *antes* de la primera medición.

## Qué métricas mirar

k6 produce bastantes métricas, y en la práctica el 90 % de las decisiones se toma con tres:

| Métrica | Qué mide | Qué buscar |
|---|---|---|
| `http_req_duration` | Cuánto tardó cada petición | El percentil, no el promedio |
| `http_req_failed` | Proporción de peticiones con error | Que no suba al aumentar la carga |
| `vus` e iteraciones | Cuánta carga se aplicó de verdad | Que coincida con lo que pediste |

La tercera fila es un control de sanidad que se pasa por alto: si pediste 400 usuarios virtuales y el resumen muestra que se aplicaron muchas menos iteraciones de las esperadas, el cuello de botella puede estar en *la máquina que corre la prueba*, no en el sistema probado. Medir con un generador de carga saturado produce conclusiones equivocadas.

### Por qué el promedio miente

Un promedio de 180 milisegundos es perfectamente compatible con que **una de cada veinte personas espere cuatro segundos**. Y esa minoría es la que abandona, la que se queja y la que aparece en el informe de soporte.

Por eso los umbrales se escriben sobre **percentiles**: `p(95)<200` afirma que el 95 % de las peticiones estuvo por debajo de 200 milisegundos. Cuanto más crítico el flujo, más alto el percentil que conviene vigilar: `p(99)` para un pago, por ejemplo. Un promedio sirve para una nota de color; un percentil sirve para tomar una decisión.

## Qué agrega Grafana

Con umbrales, k6 ya aprueba o falla sin ayuda. Lo que Grafana agrega es **historia y contexto**, y son dos cosas distintas:

- **Historia.** Al enviar las métricas de cada ejecución a una base de series temporales, la pregunta pasa de «¿está bien hoy?» a «¿está mejor o peor que hace tres semanas?». Esa es la pregunta que detecta las degradaciones graduales, que son las que nadie nota hasta que alguien se queja.
- **Contexto.** Poder ver, en el mismo tablero y en el mismo eje de tiempo, la carga que aplicaste y las métricas del sistema probado —procesador, memoria, conexiones a la base, latencia por servicio—. Ahí es donde una prueba de carga deja de decir «va lento» y empieza a decir *por qué*.

El flujo habitual es que k6 exporte sus métricas en tiempo real hacia el almacenamiento que ya uses para observabilidad, y que Grafana las grafique junto al resto. Si tu equipo ya tiene Grafana, esta es la parte que menos trabajo lleva y la que más valor agrega por hora invertida.

## En integración continua

Una configuración sensata, que evita los dos fracasos típicos —o no correr nunca las pruebas, o hacer el pipeline insoportablemente lento—:

1. **Prueba de humo en cada cambio.** Pocos usuarios, treinta segundos, umbrales holgados. Detecta que el entorno está roto o que el script quedó desactualizado, y cuesta casi nada.
2. **Carga media en cada integración a la rama principal.** Con los umbrales reales, los que reflejan tus objetivos de servicio. Es la que puede detener un despliegue.
3. **Estrés, pico y resistencia, programadas.** Una vez por semana, de noche, o antes de un evento previsto. No en cada cambio: son largas y su información no cambia tan seguido.
4. **Punto de quiebre, a mano y con intención.** Cuando hace falta saber el límite para planificar capacidad. Nunca automatizada.

## Dónde no correrlas

> **Una prueba de carga es técnicamente indistinguible de un ataque.** Generar miles de peticiones contra un sistema es, desde afuera, exactamente lo que hace una denegación de servicio. De ahí tres reglas que no son negociables: **correlas solo contra sistemas propios o con autorización explícita y por escrito**; nunca contra un servicio de terceros, aunque tu aplicación lo consuma; y avisá al equipo de infraestructura antes, porque una prueba no anunciada dispara alertas y hace perder una tarde a gente que creía estar ante un incidente. Contra producción, como mucho una prueba de humo. El marco de trabajo autorizado está en [fundamentos de hacking ético](../../hacking/fundamentos-hacking-etico/index.md).

## Errores frecuentes

- **Correr una prueba de carga sin umbrales.** Produce gráficos lindos y ninguna decisión.
- **Usar el promedio como criterio.** Esconde justamente a quien peor la pasa.
- **Saltear la prueba de humo.** Se termina analizando el rendimiento de una URL mal escrita.
- **Olvidar el `sleep`.** Sin pausas, los usuarios virtuales no se parecen a personas y los números no representan nada.
- **Modelar por usuarios cuando importa el ritmo.** Si el sistema se pone lento, la carga aplicada baja sola y el problema se disimula.
- **No mirar si la carga pedida se aplicó.** El cuello de botella puede estar en la máquina que genera la carga.
- **Probar con un solo dato.** Pedir siempre el mismo producto mide el rendimiento de la caché, no del sistema.
- **Comparar mediciones de entornos distintos.** Sin el mismo entorno, la comparación no significa nada.

## Preguntas frecuentes

### ¿Qué es k6 y qué lo diferencia de otras herramientas de carga?

Según su documentación, k6 es una herramienta de pruebas de rendimiento de código abierto, extensible y pensada para desarrolladores, que ayuda a detectar problemas de rendimiento temprano y a mejorar la fiabilidad de forma proactiva. Lo que más lo diferencia en la práctica es que los tests se escriben en JavaScript y se ejecutan desde la línea de comandos, así que viven en el repositorio como cualquier otro código: se versionan, se revisan y corren en integración continua sin necesidad de una interfaz gráfica ni de grabar sesiones.

### ¿Cuál es la diferencia entre una prueba de estrés y una de pico?

El perfil de carga y la pregunta que responden. La prueba de estrés usa muchos usuarios durante un rato largo, de cinco a sesenta minutos, y evalúa cómo se comporta el sistema en sus límites cuando la carga supera la media esperada. La prueba de pico usa muchísimos usuarios durante pocos minutos y valida el comportamiento y la supervivencia ante subidas repentinas y masivas de actividad. La primera pregunta «¿aguanta sostenidamente más de lo normal?»; la segunda, «¿sobrevive a un golpe y se recupera?».

### ¿Qué son los umbrales y por qué son la parte más importante?

Los umbrales son, textualmente, «los criterios de aprobación o fallo que definís para las métricas de tu prueba». Si el sistema no cumple las condiciones del umbral, la prueba termina con estado de fallo y k6 sale con un código distinto de cero. Eso es lo que los vuelve la parte más importante: convierten una medición en un control automático. Sin umbrales, una prueba de carga produce gráficos que alguien tiene que interpretar y que en la práctica nadie mira; con umbrales, produce una decisión que la integración continua puede usar para detener un despliegue.

### ¿Por qué no conviene usar el promedio como criterio?

Porque el promedio esconde exactamente a quien peor la pasa. Un promedio de 180 milisegundos es compatible con que una de cada veinte personas espere cuatro segundos, y esa minoría es la que abandona y la que se queja. Por eso los umbrales se escriben sobre percentiles: p(95) menor a 200 milisegundos dice que el 95 % de las peticiones estuvo por debajo de ese valor, que es una afirmación sobre la experiencia real. Cuanto más crítico el flujo, más alto el percentil que conviene mirar: p(99) para pagos, por ejemplo.

### ¿Qué agrega Grafana si k6 ya da resultados en la terminal?

Historia y comparación. La salida de k6 en la terminal te dice cómo fue esa ejecución, y con los umbrales alcanza para aprobar o fallar. Lo que no te dice es si el sistema viene degradándose desde hace tres semanas, ni cómo se compara esta ejecución con la de antes del último despliegue. Al enviar las métricas a una base de series temporales y visualizarlas en Grafana, la pregunta pasa de «¿está bien hoy?» a «¿está mejor o peor que antes?», que es la que suele importar. Y permite ver la carga junto a las métricas del sistema probado.

### ¿Se pueden correr pruebas de carga contra producción?

Con muchísimo cuidado y nunca las de estrés, pico o punto de quiebre, que están diseñadas para llevar el sistema al fallo. Una prueba de humo con pocos usuarios contra producción es una práctica común y razonable. Todo lo demás conviene hacerlo en un entorno equivalente, porque una prueba de carga sobre un sistema real es indistinguible de un ataque de denegación de servicio: puede degradar el servicio para personas reales, disparar alertas y, si el sistema no es tuyo o no tenés autorización explícita, meterte en un problema legal.

## Fuentes

Documentación oficial y material de la comunidad consultados para esta guía. Fecha de consulta: 27 de julio de 2026.

### Aportes de la comunidad Underc0de

1. **Underc0de, foro.** [El mundo de las pruebas de API](https://underc0de.org/foro/qa-testing/el-mundo-de-las-pruebas-de-api/), sección QA y Testing, 23 de julio de 2024.
2. **Underc0de, foro.** [Sección QA y Testing](https://underc0de.org/foro/qa-testing/). Hilos de la comunidad sobre herramientas y automatización de pruebas.

### Documentación oficial

3. **Grafana Labs.** [Grafana k6](https://grafana.com/docs/k6/latest/). Definición de k6 como herramienta de código abierto, extensible y pensada para desarrolladores.
4. **Grafana Labs.** [Load test types](https://grafana.com/docs/k6/latest/testing-guides/test-types/). Los seis tipos de prueba con su objetivo y su perfil de usuarios virtuales y duración, citados textualmente.
5. **Grafana Labs.** [Thresholds](https://grafana.com/docs/k6/latest/using-k6/thresholds/). Definición de umbral, ejemplo de configuración y comportamiento del código de salida.
6. **Grafana Labs.** [Scenarios](https://grafana.com/docs/k6/latest/using-k6/scenarios/). Definición de escenario y lista de ejecutores disponibles.

---

Esta guía forma parte de un proyecto de la comunidad Underc0de para organizar conocimiento técnico en español. Fuente canónica: https://underc0de.org/guias/testing/pruebas-de-rendimiento-con-k6-y-grafana/
