Testes de carga com k6: encontre o gargalo antes do seu usuário
1. Introdução aos Testes de Carga e ao k6
Testes de carga são uma prática essencial para garantir que sistemas web suportem o volume esperado de usuários sem degradação de performance. Eles simulam tráfego real para identificar gargalos antes que afetem clientes reais. Os principais tipos incluem:
- Testes de carga: verificam comportamento sob tráfego esperado
- Testes de estresse: levam o sistema além dos limites normais
- Testes de pico: simulam aumentos repentinos de tráfego
- Testes de resistência: avaliam desempenho ao longo do tempo
O k6 (da Grafana Labs) destaca-se por sua leveza (escrito em Go), scripts em JavaScript e integração nativa com CI/CD. Diferente de ferramentas como JMeter, o k6 é otimizado para execução em containers e pipelines automatizadas.
2. Configuração do Ambiente e Primeiro Script
Instalação (Linux/macOS):
# macOS via Homebrew
brew install k6
# Linux via script
curl -s https://dl.k6.io/install.sh | sudo bash
# Windows via Chocolatey
choco install k6
# Verificar instalação
k6 version
Estrutura básica de um script:
import http from 'k6/http';
import { sleep } from 'k6';
export default function () {
http.get('https://test-api.example.com/health');
sleep(1);
}
Execução:
k6 run script.js
O relatório inicial mostra métricas como http_req_duration (tempo médio de resposta), http_req_failed (taxa de erro) e vus (usuários virtuais simultâneos).
3. Criando Cenários de Teste Realistas
Usando stages para simular tráfego gradual:
import http from 'k6/http';
import { check, sleep } from 'k6';
export const options = {
stages: [
{ duration: '2m', target: 50 }, // ramp-up para 50 usuários
{ duration: '5m', target: 50 }, // carga constante
{ duration: '2m', target: 0 }, // ramp-down
],
thresholds: {
http_req_duration: ['p(95)<500'], // 95% das requisições < 500ms
http_req_failed: ['rate<0.01'], // erro < 1%
},
};
export default function () {
const payload = JSON.stringify({
username: `user_${__VU}`,
password: 'test123',
});
const params = {
headers: { 'Content-Type': 'application/json' },
};
const loginRes = http.post('https://api.example.com/login', payload, params);
check(loginRes, {
'login bem-sucedido': (r) => r.status === 200,
'token recebido': (r) => r.json().token !== undefined,
});
const token = loginRes.json().token;
const authHeaders = { Authorization: `Bearer ${token}` };
const dashboardRes = http.get('https://api.example.com/dashboard', {
headers: authHeaders,
});
check(dashboardRes, {
'dashboard carregado': (r) => r.status === 200,
});
sleep(Math.random() * 3 + 1); // pausa realista entre 1-4s
}
4. Métricas Essenciais e Análise de Resultados
Métricas principais:
- http_req_duration: tempo total da requisição (DNS, conexão, TTFB, download)
- http_req_waiting: tempo de espera no servidor (TTFB)
- http_req_failed: taxa de falhas (status >= 400)
- vus_max: pico de usuários simultâneos
- iterations: total de execuções completas
Interpretação do relatório:
http_req_duration..............: avg=342ms min=120ms med=310ms max=890ms p(90)=520ms p(95)=680ms
http_req_failed................: 0.5% ✓ 5 ✗ 995
vus............................: 50 min=0 max=50
vus_max........................: 50
Quando p(95) ultrapassa 500ms, indica que 5% dos usuários têm experiência ruim. Se p(99) > 2s, há gargalos sérios.
Exportando para análise externa:
k6 run --out json=results.json script.js
5. Identificando Gargalos e Pontos de Falha
Métricas por URL:
import http from 'k6/http';
import { Trend } from 'k6/metrics';
const slowEndpoint = new Trend('slow_endpoint_duration');
export default function () {
const res1 = http.get('https://api.example.com/endpoint1');
slowEndpoint.add(res1.timings.duration);
const res2 = http.get('https://api.example.com/endpoint2');
// Comparar métricas individuais
}
Uso de checks para depuração:
check(res, {
'status code 200': (r) => r.status === 200,
'response body não vazio': (r) => r.body.length > 0,
'tempo de resposta < 2s': (r) => r.timings.duration < 2000,
});
Isolamento de endpoints problemáticos:
# Testar apenas um endpoint específico
k6 run --vus 10 --duration 30s script.js --env TARGET_ENDPOINT=/reports
6. Integração Contínua e Automação com k6
GitHub Actions:
name: Performance Tests
on: [push, pull_request]
jobs:
k6-test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Run k6 test
uses: grafana/k6-action@v0.2.0
with:
filename: tests/load-test.js
flags: --out json=results.json
- name: Upload results
uses: actions/upload-artifact@v3
with:
name: k6-results
path: results.json
Thresholds para quebrar o build:
export const options = {
thresholds: {
http_req_duration: ['p(95)<1000'], // Falha se p95 > 1s
http_req_failed: ['rate<0.05'], // Falha se erro > 5%
checks: ['rate>0.95'], // Falha se menos de 95% dos checks passam
},
};
7. Boas Práticas e Cenários Avançados
Evitando "thundering herd":
import { sleep } from 'k6';
export default function () {
// Distribuir requisições uniformemente no tempo
sleep(Math.random() * 5); // 0-5s aleatório
// Usar __ITER para comportamento baseado em iteração
if (__ITER % 10 === 0) {
http.get('https://api.example.com/heavy-report');
}
}
Testes com WebSockets:
import ws from 'k6/ws';
export default function () {
const url = 'wss://api.example.com/socket';
const response = ws.connect(url, null, function (socket) {
socket.on('open', () => socket.send('ping'));
socket.on('message', (data) => console.log('received:', data));
socket.setTimeout(() => socket.close(), 5000);
});
}
Testes gRPC:
import grpc from 'k6/net/grpc';
import { check } from 'k6';
const client = new grpc.Client();
client.load(['definitions'], 'user.proto');
export default function () {
client.connect('localhost:50051', { plaintext: true });
const response = client.invoke('UserService/GetUser', { id: 1 });
check(response, { 'status OK': (r) => r.status === grpc.StatusOK });
client.close();
}
Dicas finais:
- Versionar scripts de teste junto com o código da aplicação
- Documentar resultados com screenshots dos dashboards do Grafana
- Estabelecer SLIs (Service Level Indicators) e SLOs (Service Level Objectives) baseados nos thresholds
- Realizar testes de carga regularmente, não apenas antes de releases
- Monitorar métricas de infraestrutura (CPU, memória, I/O) durante os testes
Referências
- Documentação oficial do k6 — Guia completo de instalação, scripts, métricas e integrações
- k6 no GitHub — Código-fonte, issues e exemplos da comunidade
- Testes de carga com k6 e Grafana — Tutorial prático de visualização de resultados com Grafana e InfluxDB
- Integração k6 com GitHub Actions — Passo a passo para executar testes em pipelines CI/CD
- k6 Academy - Curso gratuito — Curso oficial com módulos sobre fundamentos, cenários avançados e boas práticas
- Thresholds e métricas no k6 — Referência detalhada sobre configuração de limites de aceitação
- Testes de carga com WebSockets no k6 — Documentação específica para testes de conexões WebSocket