k6: testes de carga e stress test na VPS

k6 é a ferramenta de load testing moderna que substituiu o JMeter, scripts em JavaScript, métricas detalhadas e integração com Grafana. Com k6 você descobre o limite real da sua VPS antes que os usuários descubram, identifica onde a latência aumenta sob carga e valida se um deploy melhorou ou piorou a performance.

Instalar k6 e primeiro teste

Instalação e script básico de load test:

javascript
# Instalar k6 no Ubuntu:
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 update && sudo apt install -y k6

# Script básico — test.js:
import http from 'k6/http'
import { check, sleep } from 'k6'

export const options = {
  vus: 50,          // 50 usuários virtuais simultâneos
  duration: '30s',  // por 30 segundos
}

export default function () {
  const res = http.get('https://api.seudominio.com.br/health')
  check(res, {
    'status 200': (r) => r.status === 200,
    'latência < 200ms': (r) => r.timings.duration < 200,
  })
  sleep(1)  // pausa de 1s entre requisições por VU
}

# Executar:
k6 run test.js

Cenários de carga: ramp-up, stress e spike

Simular diferentes padrões de tráfego:

javascript
// scenarios.js — múltiplos cenários de carga

export const options = {
  scenarios: {
    // Ramp-up gradual (simula crescimento normal):
    ramp_up: {
      executor: 'ramping-vus',
      startVUs: 0,
      stages: [
        { duration: '2m', target: 50 },   // subir para 50 VUs em 2 min
        { duration: '5m', target: 50 },   // manter 50 VUs por 5 min
        { duration: '2m', target: 0 },    // reduzir para 0
      ],
    },

    // Stress test (encontrar o ponto de quebra):
    stress: {
      executor: 'ramping-vus',
      stages: [
        { duration: '2m', target: 100 },
        { duration: '2m', target: 200 },
        { duration: '2m', target: 300 },  // aumentar até quebrar
        { duration: '2m', target: 0 },
      ],
    },

    // Spike test (pico súbito):
    spike: {
      executor: 'ramping-vus',
      stages: [
        { duration: '10s', target: 0 },
        { duration: '1m',  target: 500 }, // pico de 500 VUs de repente
        { duration: '3m',  target: 0 },
      ],
    },
  },

  thresholds: {
    http_req_duration: ['p(95)<500'],  // 95% das req < 500ms
    http_req_failed:   ['rate<0.01'],  // < 1% de falhas
  },
}

Testar fluxo completo de API autenticada

Script realista com login, token e chamadas encadeadas:

javascript
// api-flow.js
import http from 'k6/http'
import { check, group } from 'k6'

const BASE_URL = 'https://api.seudominio.com.br'

export default function () {
  // 1. Login e obter token:
  group('Auth', () => {
    const loginRes = http.post(`${BASE_URL}/auth/login`, JSON.stringify({
      email: 'teste@runstack.com.br',
      senha: 'senha123',
    }), { headers: { 'Content-Type': 'application/json' } })

    check(loginRes, {
      'login 200': (r) => r.status === 200,
      'token presente': (r) => JSON.parse(r.body).token !== undefined,
    })

    const { token } = JSON.parse(loginRes.body)
    const headers = {
      Authorization: `Bearer ${token}`,
      'Content-Type': 'application/json',
    }

    // 2. Buscar lista de pedidos:
    group('Pedidos', () => {
      const pedidosRes = http.get(`${BASE_URL}/pedidos?page=1`, { headers })
      check(pedidosRes, {
        'pedidos 200': (r) => r.status === 200,
        'lista não vazia': (r) => JSON.parse(r.body).data.length > 0,
      })
    })
  })
}

Analisar métricas e interpretar resultados

Entender o output do k6 e identificar problemas:

bash
# Output do k6 — métricas principais:
# http_req_duration ............. p(90)=45ms p(95)=89ms p(99)=320ms
# http_req_failed ............... 0.12% ✓ 988 ✗ 12
# http_reqs ..................... 5000   83.3/s
# vus_max ....................... 50

# Interpretar:
# p(95) > 500ms     → 5% dos usuários estão com má experiência
# http_req_failed > 1% → aplicação começando a falhar sob carga
# Latência p(95) crescendo → memory leak ou saturação de recursos

# Exportar resultado como JSON para análise:
k6 run --out json=resultado.json test.js

# Integrar com Grafana via InfluxDB:
k6 run --out influxdb=http://localhost:8086/k6 test.js

# Verificar o que acontece no servidor durante o teste:
# (em outro terminal, durante k6 run)
watch -n 1 "ps aux | grep node | awk '{print $3, $4, $11}'"
watch -n 1 "ss -s"
htop

Comparar antes e depois de uma otimização

Metodologia para validar melhorias de performance:

bash
#!/bin/bash
# compare-perf.sh — baseline vs otimizado

echo "=== Baseline (antes da otimização) ==="
k6 run --quiet --out json=/tmp/baseline.json \
  -e BASE_URL=https://api.seudominio.com.br test.js

echo "=== Aplicar otimização (deploy nova versão) ==="
# ... seu processo de deploy aqui

echo "=== Após otimização ==="
k6 run --quiet --out json=/tmp/otimizado.json \
  -e BASE_URL=https://api.seudominio.com.br test.js

# Comparar p95 de latência:
python3 << 'EOF'
import json

def p95(filename):
    with open(filename) as f:
        for line in f:
            data = json.loads(line)
            if data.get('type') == 'Point' and data['metric'] == 'http_req_duration':
                pass  # simplificado — usar k6 summary direto

baseline_p95 = 89    # ms — extrair do output
otimizado_p95 = 45   # ms

melhoria = (baseline_p95 - otimizado_p95) / baseline_p95 * 100
print(f"Melhoria: {melhoria:.1f}% ({baseline_p95}ms → {otimizado_p95}ms)")
EOF

Não quer configurar manualmente?

Implante o VPS para APIs em menos de 5 minutos com a Runstack. Infraestrutura da OPEN Datacenter, com servidores em São Paulo.

Perguntas frequentes

k6: scripts JavaScript modernos, leve, integra com CI/CD, métricas ricas, melhor escolha para equipes de dev. JMeter: GUI, mais configurações, mas pesado e XML verboso, legado em muitas empresas. Locust: scripts Python, fácil para quem já sabe Python, UI web simples. Para projetos novos com CI/CD: k6. Para QA com GUI: JMeter. Para Python shops: Locust.

Da sua máquina local ou de outra VPS, nunca da mesma VPS que está sendo testada. Se rodar na mesma VPS: o k6 compete por CPU/memória com a aplicação, distorcendo os resultados. Para testes de rede reais: rode de uma localização geográfica próxima dos usuários finais. Para alta concorrência: k6 Cloud ou múltiplas instâncias k6 distribuídas.

Comece com o esperado em produção, depois aumente. Para 1.000 usuários/dia com sessões de 5 minutos: ~3-5 VUs simultâneos. Para pico de 10.000 req/hora: ~30-50 VUs com sleep(1). O número de VUs sem sleep é muito mais agressivo que com sleep, calibre para representar o comportamento real dos usuários.

p95 = 95% das requisições foram mais rápidas que esse valor. p99 = 99% foram mais rápidas. p95 < 200ms: excelente. p95 < 500ms: aceitável. p95 > 1s: problema. O p99 alto com p95 baixo indica outliers (queries ocasionalmente lentas). O p95 crescendo com a carga indica saturação do sistema, você está próximo do limite.

Sim, k6 tem suporte nativo a WebSocket (import { WebSocket } from "k6/experimental/websockets") e pode consumir SSE via http.get com streaming. Para testar pub/sub em tempo real: conecte N VUs via WebSocket e meça a latência de entrega das mensagens. Útil para testar aplicações de chat, dashboards em tempo real e notificações push.