k6: testes de carga e performance em APIs

    k6 é uma ferramenta de testes de carga open-source escrita em Go com scripts em JavaScript — próxima do que você já conhece como desenvolvedor Node.js. Com ela você descobre gargalos antes que seus usuários descubram: como a API se comporta com 100 usuários simultâneos? E com 1000? E com um pico súbito de tráfego?

    Instalar k6 e primeiro teste

    Instalação e script básico de teste de carga:

    javascript
    # Instalar k6:
    # macOS:
    brew install k6
    
    # Ubuntu/Debian:
    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 k6
    
    # Docker:
    docker run --rm -i grafana/k6 run - < script.js
    
    # Primeiro script (tests/carga/api-basico.js):
    import http from 'k6/http'
    import { sleep, check } from 'k6'
    import { Rate } from 'k6/metrics'
    
    const erros = new Rate('erros')
    
    export const options = {
      vus: 10,          // 10 virtual users simultâneos
      duration: '30s',  // por 30 segundos
      thresholds: {
        http_req_duration: ['p(95)<500'],  // 95% das req em < 500ms
        http_req_failed: ['rate<0.01'],    // menos de 1% de erros
        erros: ['rate<0.05'],
      },
    }
    
    export default function () {
      const res = http.get('https://api.seudominio.com.br/api/health')
    
      check(res, {
        'status 200': (r) => r.status === 200,
        'latência < 200ms': (r) => r.timings.duration < 200,
      })
    
      erros.add(res.status !== 200)
      sleep(1)  // pausar 1s entre iterações (simular comportamento humano)
    }

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

    Diferentes perfis de carga para testar diferentes hipóteses:

    javascript
    // tests/carga/cenarios.js:
    import http from 'k6/http'
    import { sleep, check } from 'k6'
    
    export const options = {
      scenarios: {
        // Cenário 1: rampa gradual — simula crescimento de tráfego normal
        ramp_up: {
          executor: 'ramping-vus',
          startVUs: 0,
          stages: [
            { duration: '2m', target: 50 },   // subir para 50 VUs em 2min
            { duration: '5m', target: 50 },   // manter 50 VUs por 5min
            { duration: '2m', target: 0 },    // descer para 0
          ],
        },
    
        // Cenário 2: spike — testar resiliência a pico repentino
        spike: {
          executor: 'ramping-vus',
          startVUs: 0,
          stages: [
            { duration: '30s', target: 10 },   // carga normal
            { duration: '10s', target: 200 },  // spike súbito!
            { duration: '3m', target: 200 },   // manter o pico
            { duration: '30s', target: 10 },   // voltar ao normal
          ],
        },
    
        // Cenário 3: soak test — resistência a carga sustentada por horas
        soak: {
          executor: 'ramping-vus',
          stages: [
            { duration: '5m', target: 30 },    // aquecimento
            { duration: '2h', target: 30 },    // 2 horas de carga contínua
            { duration: '5m', target: 0 },     // resfriamento
          ],
        },
      },
    
      thresholds: {
        'http_req_duration{scenario:ramp_up}': ['p(99)<1000'],
        'http_req_duration{scenario:spike}': ['p(95)<3000'],   // mais tolerante no spike
        http_req_failed: ['rate<0.05'],
      },
    }
    
    export default function () {
      const res = http.post(
        'https://api.seudominio.com.br/api/auth/login',
        JSON.stringify({ email: 'teste@exemplo.com', senha: 'senha-teste' }),
        { headers: { 'Content-Type': 'application/json' } }
      )
      check(res, { 'login com sucesso': (r) => r.status === 200 })
      sleep(Math.random() * 3 + 1)  // pausa aleatória de 1-4s (comportamento humano)
    }

    Testar endpoints autenticados e fluxos completos

    Scripts para testar fluxos que exigem autenticação JWT:

    javascript
    // tests/carga/fluxo-completo.js:
    import http from 'k6/http'
    import { check, group, sleep } from 'k6'
    import { SharedArray } from 'k6/data'
    
    // Dados de teste pré-carregados (evitar criar usuário a cada iteração):
    const usuarios = new SharedArray('usuarios', function () {
      return JSON.parse(open('./dados/usuarios-teste.json'))
    })
    
    export const options = {
      vus: 20,
      duration: '5m',
      thresholds: {
        'http_req_duration{name:login}': ['p(95)<300'],
        'http_req_duration{name:listar-pedidos}': ['p(95)<500'],
        'http_req_duration{name:criar-pedido}': ['p(95)<1000'],
      },
    }
    
    export default function () {
      const usuario = usuarios[Math.floor(Math.random() * usuarios.length)]
    
      // ── Fase 1: Login ──────────────────────────────────────────────
      group('autenticação', () => {
        const loginRes = http.post(
          'https://api.seudominio.com.br/api/auth/login',
          JSON.stringify({ email: usuario.email, senha: usuario.senha }),
          { headers: { 'Content-Type': 'application/json' }, tags: { name: 'login' } }
        )
    
        check(loginRes, { 'login OK': (r) => r.status === 200 })
        const token = loginRes.json('accessToken') as string
    
        const headers = { Authorization: `Bearer ${token}`, 'Content-Type': 'application/json' }
    
        // ── Fase 2: Navegar pela aplicação ─────────────────────────────
        sleep(1)
    
        group('pedidos', () => {
          const listRes = http.get(
            'https://api.seudominio.com.br/api/v2/pedidos',
            { headers, tags: { name: 'listar-pedidos' } }
          )
          check(listRes, { 'lista OK': (r) => r.status === 200 })
    
          sleep(Math.random() * 2)
    
          const criarRes = http.post(
            'https://api.seudominio.com.br/api/v2/pedidos',
            JSON.stringify({ itens: [{ produto_id: usuario.produtoId, quantidade: 1 }] }),
            { headers, tags: { name: 'criar-pedido' } }
          )
          check(criarRes, { 'pedido criado': (r) => r.status === 201 })
        })
      })
    
      sleep(Math.random() * 3 + 2)
    }

    Exportar métricas do k6 para Prometheus

    Ver métricas de carga em tempo real no Grafana durante o teste:

    bash
    # k6 pode exportar métricas para Prometheus via remote write:
    
    # k6 com output para Prometheus Remote Write:
    K6_PROMETHEUS_RW_SERVER_URL=http://localhost:9090/api/v1/write   k6 run --out experimental-prometheus-rw script.js
    
    # Ou via InfluxDB (mais simples para uso local):
    # docker run -p 8086:8086 influxdb:1.8
    k6 run --out influxdb=http://localhost:8086/k6 script.js
    
    # Dashboard Grafana para k6 + InfluxDB:
    # Importar ID 2587 — k6 Load Testing Results (oficial Grafana)
    
    # Configurar no docker-compose.yml:
    services:
      influxdb:
        image: influxdb:1.8
        environment:
          INFLUXDB_DB: k6
        ports:
          - "8086:8086"
    
      grafana:
        environment:
          GF_DATASOURCES_DEFAULT_TYPE: influxdb
          GF_DATASOURCES_DEFAULT_URL: http://influxdb:8086
          GF_DATASOURCES_DEFAULT_DATABASE: k6
    
    # Rodar k6 apontando para o InfluxDB do Docker:
    docker run --rm -i --network host grafana/k6 run   --out influxdb=http://localhost:8086/k6 - < script.js

    Integrar testes de carga no CI/CD

    Rodar k6 automaticamente antes de cada deploy em produção:

    yaml
    # .github/workflows/carga.yml:
    name: Testes de Carga
    
    on:
      push:
        branches: [main]
    
    jobs:
      k6:
        runs-on: ubuntu-latest
        steps:
          - uses: actions/checkout@v4
    
          - name: Instalar 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 update && sudo apt install k6
    
          - name: Rodar teste de carga (staging)
            run: |
              k6 run \
                --env BASE_URL=https://staging.seudominio.com.br \
                --out json=resultados.json \
                tests/carga/api-basico.js
    
          - name: Upload resultados
            uses: actions/upload-artifact@v4
            if: always()
            with:
              name: k6-resultados
              path: resultados.json

    $ runstack deploy --plan starter

    Não quer configurar manualmente?

    Não quer configurar manualmente? Implante o VPS para Node.js em menos de 3 minutos com a Runstack. Infraestrutura da OPEN DATACENTER, com servidores no Brasil.

    Perguntas frequentes