k6 é uma ferramenta de testes de carga que usa scripts JavaScript para descrever tráfego, cenários, checks e limites de desempenho. Embora o script seja escrito em JavaScript, ele não executa dentro do Node.js: k6 possui runtime próprio. Por isso, módulos e APIs precisam ser compatíveis com a plataforma k6.
Em aplicações Node.js, k6 é útil para validar APIs HTTP, gRPC, WebSocket e fluxos de usuário sob smoke, carga média, stress, spike, soak e breakpoint. Ele pode rodar localmente, em containers, Kubernetes ou Grafana Cloud.
Instalação
Use o pacote oficial do sistema ou Docker:
docker run --rm -i grafana/k6 run - < load-test.jsTambém é possível instalar o binário conforme o sistema operacional. Fixe a versão em CI para evitar diferenças entre execuções.
Primeiro script
import http from 'k6/http';
import { check, sleep } from 'k6';
export const options = {
vus: 10,
duration: '30s',
};
export default function () {
const response = http.get('http://host.docker.internal:3000/health');
check(response, {
'status é 200': (res) => res.status === 200,
'resposta abaixo de 200 ms': (res) => res.timings.duration < 200,
});
sleep(1);
}Execute:
k6 run load-test.jsInit context e VU code
Código fora da função padrão roda na inicialização de cada VU. Use-o para opções, dados compartilhados e definições. A função default é repetida conforme o executor.
Não coloque autenticação única ou setup caro no corpo se pode ser feito uma vez no setup().
Virtual users
VUs são loops paralelos que executam o cenário. Mais VUs não correspondem diretamente a usuários reais sem considerar tempo de espera, duração das requisições e comportamento.
Calcule um modelo baseado na taxa de chegada ou na concorrência esperada.
Stages
export const options = {
stages: [
{ duration: '30s', target: 20 },
{ duration: '2m', target: 20 },
{ duration: '30s', target: 0 },
],
};Essa configuração aquece, mantém carga e reduz. Para controle avançado, use scenarios.
Scenarios
export const options = {
scenarios: {
leitura: {
executor: 'constant-arrival-rate',
rate: 200,
timeUnit: '1s',
duration: '2m',
preAllocatedVUs: 20,
maxVUs: 100,
exec: 'readItems',
},
escrita: {
executor: 'ramping-vus',
startVUs: 0,
stages: [
{ duration: '30s', target: 10 },
{ duration: '1m', target: 10 },
{ duration: '20s', target: 0 },
],
exec: 'createItem',
},
},
};
export function readItems() {}
export function createItem() {}Cenários permitem cargas independentes e proporções mais realistas.
Modelo fechado e aberto
Executores por VU representam modelo fechado: novas iterações dependem da conclusão anterior. Arrival-rate representa modelo aberto: requisições chegam em taxa definida. Para APIs públicas, o modelo aberto costuma revelar melhor degradação e filas.
Checks
check(response, {
'status correto': (r) => r.status === 200,
'json válido': (r) => r.json('id') !== undefined,
});Checks registram sucesso, mas não fazem o teste falhar automaticamente. Use thresholds.
Thresholds
export const options = {
thresholds: {
http_req_failed: ['rate<0.01'],
http_req_duration: ['p(95)<300', 'p(99)<800'],
checks: ['rate>0.99'],
},
};Se um threshold falha, k6 retorna código de saída diferente de zero, permitindo bloquear pipeline.
Métricas principais
http_req_duration: duração total;http_req_waiting: tempo até primeiro byte;http_req_failed: taxa de falhas;iterations: iterações concluídas;vus: usuários ativos;dropped_iterations: carga que não conseguiu ser iniciada;data_receivededata_sent.
POST com JSON
const payload = JSON.stringify({
name: `user-${__VU}-${__ITER}`,
email: `user-${__VU}-${__ITER}@example.com`,
});
const response = http.post(`${baseUrl}/users`, payload, {
headers: {
'Content-Type': 'application/json',
},
});Use identificadores únicos e ambiente descartável para não acumular colisões.
Variáveis de ambiente
k6 run -e BASE_URL=https://staging.example.com load-test.jsconst baseUrl = __ENV.BASE_URL;Não grave tokens em scripts. Injete por secret e evite imprimir.
Setup e teardown
export function setup() {
const response = http.post(`${baseUrl}/sessions`, JSON.stringify({
username: __ENV.USERNAME,
password: __ENV.PASSWORD,
}), { headers: { 'Content-Type': 'application/json' } });
return { token: response.json('token') };
}
export default function (data) {
http.get(`${baseUrl}/profile`, {
headers: { Authorization: `Bearer ${data.token}` },
});
}
export function teardown(data) {
// limpeza opcional
}Dados retornados por setup são serializados para VUs. Evite objetos grandes.
SharedArray
Para carregar dados uma vez:
import { SharedArray } from 'k6/data';
const users = new SharedArray('users', () =>
JSON.parse(open('./users.json')),
);Não carregue um arquivo separado para cada VU.
Tags e grupos
import { group } from 'k6';
group('checkout', () => {
http.get(`${baseUrl}/cart`, { tags: { endpoint: 'cart' } });
http.post(`${baseUrl}/checkout`, null, { tags: { endpoint: 'checkout' } });
});Tags permitem thresholds por rota:
'http_req_duration{endpoint:checkout}': ['p(95)<500']Sleep e think time
Usuários reais não enviam requisições continuamente. Adicione pausa quando o cenário representa jornada. Em testes de capacidade pura, talvez não seja necessário.
HTTP batch
const responses = http.batch([
['GET', `${baseUrl}/profile`],
['GET', `${baseUrl}/notifications`],
['GET', `${baseUrl}/preferences`],
]);Batch simula requisições paralelas, mas pode aumentar carga de forma diferente do cliente real.
gRPC e WebSocket
k6 possui APIs para gRPC e WebSocket. Defina métricas e checks específicos, incluindo mensagens, duração de conexão e erros.
Smoke test
export const options = {
vus: 1,
duration: '30s',
thresholds: {
http_req_failed: ['rate==0'],
},
};Rode em cada deploy para verificar script e ambiente.
Average load
Use tráfego típico, dados representativos e duração suficiente para estabilizar cache, pools e GC. Esse cenário define baseline.
Stress e breakpoint
Aumente carga gradualmente até SLO falhar. Observe onde latência cresce, erros aparecem e recursos saturam. Interrompa antes de danificar dados ou infraestrutura compartilhada.
Spike
Suba rapidamente para validar autoscaling, filas e proteção. Compare tempo de recuperação após o pico.
Soak
Execute por horas para encontrar vazamentos de memória, crescimento de filas, conexões abandonadas e degradação de cache.
Docker
docker run --rm \
-i \
-e BASE_URL=http://host.docker.internal:3000 \
grafana/k6 run - < load-test.jsEm Linux, configure acesso ao host ou use rede adequada.
CI
- name: Smoke performance
run: |
docker run --rm -i \
-e BASE_URL="$BASE_URL" \
grafana/k6:VERSAO run - < test/k6/smoke.jsFixe a imagem. Benchmarks precisos não devem depender de runner compartilhado; smoke e thresholds amplos funcionam bem.
Kubernetes Operator
Para carga distribuída, k6 Operator executa TestRun com paralelismo. Dimensione geradores e monitore se eles próprios saturam.
Resultados
k6 envia métricas para JSON, Prometheus remote write, InfluxDB, Grafana Cloud e outras saídas. Correlacione carga com métricas do serviço.
Custom metrics
import { Trend, Rate } from 'k6/metrics';
const checkoutDuration = new Trend('checkout_duration');
const businessErrors = new Rate('business_errors');
checkoutDuration.add(response.timings.duration);
businessErrors.add(response.status !== 201);Cardinalidade
Não use ID único como tag. Isso cria séries demais e pode sobrecarregar backend de métricas. Tags devem ter conjunto limitado.
Dados e segurança
Execute em ambiente autorizado, use contas de teste, limite custos e proteja segredos. Testes de carga são tráfego destrutivo em potencial.
k6 ou Autocannon
Autocannon é ótimo para benchmark HTTP local e API programática Node.js. k6 oferece cenários, thresholds, execução distribuída, jornadas e integrações de observabilidade. Muitas equipes usam os dois.
Fluxo recomendado
Comece com smoke, estabeleça baseline, defina SLOs como thresholds, modele chegada real, observe aplicação e repita em ambiente controlado. Combine com Autocannon no Node.js, métricas em Prometheus, autoscaling em Kubernetes HPA e alertas em Alertmanager.
Consulte a documentação oficial para executar k6 e o guia oficial de thresholds.



