Parte da nossa série Performance & Scalability
Leia o guia completoEnviar um produto sem teste de carga é uma aposta. Você não sabe qual é o seu ponto de ruptura até que os usuários o encontrem para você – geralmente durante o lançamento de um produto, um momento viral ou um pico de vendas. k6 é a ferramenta moderna de teste de carga que permite escrever testes em JavaScript, executá-los a partir de CI e descobrir seu limite de desempenho antes dos usuários. É amigável ao desenvolvedor, eficiente em termos de recursos (k6 usa goroutines, não threads) e integra-se perfeitamente com Grafana e Prometheus para métricas em tempo real.
Este guia cobre o k6 desde o primeiro script até testes de carga complexos em vários cenários, métricas personalizadas, limites, integração de CI e padrões de teste seguros para produção para APIs Node.js/NestJS.
Principais conclusões
- Os scripts k6 são JavaScript, mas são executados em um tempo de execução Go — sem APIs Node.js (sem
require, semfs, semsetTimeout)- Usuários Virtuais (VUs) são usuários simulados simultâneos; iterações são execuções de script individuais
- Sempre defina limites antes de executar — limites com falha interrompem o teste e falham no CI
- Usar cenários (vus constante, vus em rampa, taxa de chegada constante) para modelar padrões de tráfego reais
- Nunca carregue a produção de teste sem coordenação com as operações — teste em relação à preparação ou a um clone de produção
- A métrica
http_req_durationé sua principal métrica de SLA — p95 e p99 são mais importantes que as médias- Limite de taxa para seus corredores k6 – o tráfego de teste de carga não deve esgotar seu orçamento de limite de taxa
- Use k6 Cloud ou Grafana k6 para testes de carga distribuídos em regiões geográficas
Instalação e primeiro 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
Seu primeiro 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
}
Execute:
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
Teste de carga de API com autenticação
// 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)}`);
}
Cenários VU de aceleração
Modele padrões de tráfego reais com vários estágios e cenários:
// 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'],
},
};
Teste de rastreamento de blog/sitemap
// 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 e limites 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);
}
Integração de saída Grafana
Transmita métricas k6 para o InfluxDB e visualize no Grafana durante o teste:
# 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
CI Integration
# .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/
Linhas de base de desempenho
Estabeleça linhas de base e alerte sobre regressões:
| Ponto final | p50 | p95 | p99 | Erro no orçamento |
|---|---|---|---|---|
GET / (página inicial) | 120ms | 400ms | 800ms | 0,1% |
| CÓDIGO0 | 50ms | 150ms | 300ms | 0,5% |
| CÓDIGO0 | 80ms | 200ms | 400ms | 0,5% |
| CÓDIGO0 | 200ms | 500ms | 1000ms | 1% |
| CÓDIGO0 | 500ms | 1500ms | 3000ms | 1% |
| CÓDIGO0 | 100ms | 300ms | 600ms | 0,1% |
Perguntas frequentes
Qual é a diferença entre VUs e solicitações por segundo?
VUs (Usuários Virtuais) são usuários simulados simultâneos. Cada VU executa sua função padrão em um loop. As solicitações por segundo dependem da rapidez com que cada iteração é executada. Se cada iteração da VU levar 2 segundos (incluindo o tempo de suspensão) e você tiver 100 VUs, obterá aproximadamente 50 RPS. Use o executor constant-arrival-rate quando precisar direcionar um RPS específico, independentemente da duração da iteração.
Devo carregar o teste em produção ou teste?
Prefira sempre a encenação. Os testes de carga de produção correm o risco de impactar usuários reais, esgotar os limites de taxa, criar pedidos reais a partir de dados de teste e acionar eventos reais de webhook. Se você precisar testar a produção, faça-o durante janelas de baixo tráfego, use tokens de pagamento de teste e tenha um plano de reversão. O k6 Cloud oferece suporte à geração de carga distribuída geograficamente para que você possa testar o desempenho da borda do CDN sem tocar na origem.
Como lidar com a expiração do token de autenticação durante testes longos?
Para testes curtos (menos de 15 minutos), obtenha um token em setup() e passe-o para todas as VUs via data. Para testes longos (mais de 15 minutos), implemente a atualização do token no script de teste: verifique a idade do token na função padrão e atualize quando ele expirar. Armazene o token em uma variável JavaScript local para cada VU.
Qual é a diferença entre stages e scenarios?
stages é uma abreviação para um único cenário ramping-vus – bom para padrões simples de aceleração/manutenção/desaceleração. scenarios oferece controle total: vários padrões de tráfego simultâneos, diferentes executores por cenário, limites por cenário e marcação de cenário em métricas. Use scenarios para testes realistas de vários padrões (linha de base + pico + absorção simultaneamente).
Quantas VUs um único processo k6 pode manipular?
Um único processo k6 em hardware moderno pode sustentar de 5.000 a 10.000 VUs e gerar de 50.000 a 100.000 RPS, dependendo da complexidade do script e do tamanho da resposta. Para cargas mais altas, use k6 cloud ou execute várias instâncias k6 atrás de um distribuidor de carga. Cada VU é extremamente leve (uma goroutine) — k6 usa muito menos recursos que JMeter ou Gatling para contagens de VU equivalentes.
Próximas etapas
O teste de carga revela o verdadeiro perfil de desempenho do seu aplicativo antes que os usuários o descubram sob estresse. Os padrões neste guia (cenários de rampa, limites personalizados, integração de CI e painéis Grafana) fornecem a infraestrutura para encontrar e corrigir gargalos de desempenho continuamente.
ECOSIRE cria APIs NestJS com desempenho validado com testes de carga k6 cobrindo página inicial, endpoints de API, fluxos de checkout e rastreamentos completos de mapas de sites. Explore nossos serviços de engenharia de back-end para saber como construímos para obter desempenho desde o primeiro dia.
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
Expanda o seu negócio com ECOSIRE
Soluções empresariais em ERP, comércio eletrônico, IA, análise e automação.
Artigos Relacionados
Otimização de velocidade do Shopify: uma lista de verificação técnica que realmente movimenta os principais sinais vitais da web (2026)
Uma lista de verificação de velocidade do Shopify testada em campo para 2026 – o que realmente melhora LCP, INP e CLS em lojas reais, o que desperdiça tempo e como auditar aplicativos e temas.
Odoo 19 HR: Matriz de Competências, Planos de Carreira, Ciclos de Desempenho
Atualização de RH Odoo 19: matriz de habilidades nativas, planejamento de carreira, ciclos de avaliação de desempenho, grade de 9 caixas, planejamento de sucessão, integração HRIS.
Benchmarks de desempenho do Odoo 19: números de ajuste do PostgreSQL 17
Benchmarks de desempenho do Odoo 19 no mundo real: velocidade do cliente web, taxa de transferência de ORM, configurações de ajuste PG17, pool de conexões, contagens de trabalhadores, limites de escala.
Mais de Performance & Scalability
Otimização de velocidade do Shopify: uma lista de verificação técnica que realmente movimenta os principais sinais vitais da web (2026)
Uma lista de verificação de velocidade do Shopify testada em campo para 2026 – o que realmente melhora LCP, INP e CLS em lojas reais, o que desperdiça tempo e como auditar aplicativos e temas.
Lista de verificação de auditoria técnica de SEO 2026: 47 verificações que executamos em cada site do cliente
A lista de verificação técnica de auditoria de SEO de 47 pontos que executamos em todos os sites de clientes em 2026 – rastreabilidade, indexação, canônicos, hreflang, Core Web Vitals e logs.
Odoo 19 HR: Matriz de Competências, Planos de Carreira, Ciclos de Desempenho
Atualização de RH Odoo 19: matriz de habilidades nativas, planejamento de carreira, ciclos de avaliação de desempenho, grade de 9 caixas, planejamento de sucessão, integração HRIS.
Benchmarks de desempenho do Odoo 19: números de ajuste do PostgreSQL 17
Benchmarks de desempenho do Odoo 19 no mundo real: velocidade do cliente web, taxa de transferência de ORM, configurações de ajuste PG17, pool de conexões, contagens de trabalhadores, limites de escala.
Otimização de custos do OpenClaw e eficiência de token em escala
Otimização de custos de token OpenClaw: cache de prompt, roteamento de modelo, cache de resposta, APIs em lote e proteções de custo por locatário para agentes de produção.
Atualização incremental do Power BI para tabelas com mais de 10 milhões de linhas
Manual de atualização incremental do Power BI para tabelas com mais de 10 milhões de linhas: design de partição, RangeStart/RangeEnd, políticas de atualização, dobramento de consultas e híbridos DirectQuery.