JMeter роками був стандартом навантажувального тестування. Але k6, розроблений Grafana Labs, активно переймає позиції — особливо серед команд з DevOps культурою і CI/CD пайплайнами.

У цьому гайді: від встановлення до першого реального тесту і інтеграції в GitHub Actions.

Чому k6, а не JMeter

JMeterk6
Мова скриптівJava/Groovy/XMLJavaScript
ІнтерфейсGUI (важкий)CLI + код
CI/CD інтеграціяСкладнаНативна
РесурсиБагато RAMЛегкий
ПротоколиHTTP, SOAP, JMS, FTP...HTTP, WebSocket, gRPC
Open-source
Grafana інтеграціяЧерез плагіниНативна

k6 написаний на Go і виконує JS-скрипти. Він споживає набагато менше пам'яті ніж JMeter при тих самих навантаженнях.

Встановлення

1# macOS
2brew install k6
3
4# Ubuntu/Debian
5sudo gpg -k
6sudo gpg --no-default-keyring --keyring /usr/share/keyrings/k6-archive-keyring.gpg --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys C5AD17C747E3415A3642D57D77C6C491D6AC1D69
7echo "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
8sudo apt-get update
9sudo apt-get install k6
10
11# Windows (через Chocolatey)
12choco install k6
13
14# Docker
15docker run --rm -i grafana/k6 run - <script.js

Перший тест

Створіть файл basic-load-test.js:

1import http from 'k6/http';
2import { check, sleep } from 'k6';
3
4// Налаштування тесту
5export const options = {
6  vus: 10,          // 10 віртуальних користувачів
7  duration: '30s',  // тривалість 30 секунд
8};
9
10export default function () {
11  // Виконуємо GET запит
12  const response = http.get('https://api.example.com/users');
13
14  // Перевіряємо відповідь
15  check(response, {
16    'status is 200': (r) => r.status === 200,
17    'response time < 500ms': (r) => r.timings.duration < 500,
18    'has users array': (r) => JSON.parse(r.body).users !== undefined,
19  });
20
21  sleep(1); // пауза 1 секунда між ітераціями
22}

Запуск:

1k6 run basic-load-test.js

Розуміння метрик

Після виконання k6 виводить метрики:

1          /\      |‾‾| /‾‾/   /‾‾/   
2     /\  /  \     |  |/  /   /  /    
3    /  \/    \    |     (   /   ‾‾\  
4   /          \   |  |\  \ |  (‾)  | 
5  / __________ \  |__| \__\ \_____/ .io
6
7  scenarios: (100.00%) 1 scenario, 10 max VUs, 1m0s max duration
8  default: 10 looping VUs for 30s (gracefulStop: 30s)
9
10✓ status is 200
11✓ response time < 500ms  
12✓ has users array
13
14checks.........................: 100.00% ✓ 300  ✗ 0
15data_received..................: 1.2 MB  40 kB/s
16data_sent......................: 45 kB   1.5 kB/s
17http_req_blocked...............: avg=1.2ms    min=1µs    med=3µs    max=65ms   p(90)=5µs    p(95)=9µs   
18http_req_connecting............: avg=456µs    min=0s     med=0s     max=23ms   p(90)=0s     p(95)=0s    
19http_req_duration..............: avg=234ms    min=98ms   med=210ms  max=687ms  p(90)=401ms  p(95)=512ms 
20http_reqs......................: 300     10/s
21iteration_duration.............: avg=1.23s    min=1.1s   med=1.21s  max=1.69s  p(90)=1.41s  p(95)=1.51s
22iterations.....................: 300     10/s
23vus............................: 10      min=10     max=10

Ключові метрики для розуміння:

  • http_req_duration — час відповіді. Дивіться на p(90) і p(95) — 90-й і 95-й перцентилі. Якщо p(95) = 512ms — 95% запитів виконались швидше ніж 512мс.
  • http_reqs — кількість запитів на секунду (RPS/TPS)
  • checks — скільки перевірок пройшло/впало
  • vus — кількість активних віртуальних користувачів

Сценарії навантаження

Spike test (стрибок навантаження)

1export const options = {
2  stages: [
3    { duration: '1m', target: 10 },   // розігрів до 10 users
4    { duration: '10s', target: 200 },  // різкий стрибок до 200
5    { duration: '1m', target: 200 },   // тримаємо навантаження
6    { duration: '10s', target: 0 },    // завершення
7  ],
8};

Stress test (пошук точки зламу)

1export const options = {
2  stages: [
3    { duration: '2m', target: 50 },
4    { duration: '5m', target: 50 },
5    { duration: '2m', target: 100 },
6    { duration: '5m', target: 100 },
7    { duration: '2m', target: 200 },
8    { duration: '5m', target: 200 },
9    { duration: '2m', target: 0 },
10  ],
11};

Soak test (тест на витривалість)

1export const options = {
2  stages: [
3    { duration: '5m', target: 50 },   // розігрів
4    { duration: '8h', target: 50 },   // 8 годин під навантаженням
5    { duration: '5m', target: 0 },    // завершення
6  ],
7};

Тестування API з авторизацією

1import http from 'k6/http';
2import { check, sleep } from 'k6';
3
4const BASE_URL = __ENV.BASE_URL || 'https://api.example.com';
5
6// Отримуємо токен один раз на початку
7export function setup() {
8  const loginResponse = http.post(`${BASE_URL}/auth/login`, JSON.stringify({
9    email: 'test@example.com',
10    password: 'testpassword',
11  }), {
12    headers: { 'Content-Type': 'application/json' },
13  });
14
15  const token = JSON.parse(loginResponse.body).access_token;
16  return { token };
17}
18
19export default function (data) {
20  const headers = {
21    'Authorization': `Bearer ${data.token}`,
22    'Content-Type': 'application/json',
23  };
24
25  // Тест на отримання профілю
26  const profileRes = http.get(`${BASE_URL}/profile`, { headers });
27  check(profileRes, {
28    'profile: status 200': (r) => r.status === 200,
29    'profile: has email': (r) => JSON.parse(r.body).email !== undefined,
30  });
31
32  sleep(Math.random() * 2 + 1); // рандомна пауза 1-3 секунди
33}

Інтеграція в GitHub Actions

1# .github/workflows/performance.yml
2name: Performance Tests
3
4on:
5  push:
6    branches: [main]
7  schedule:
8    - cron: '0 6 * * 1'  # кожен понеділок о 6:00
9
10jobs:
11  k6-load-test:
12    runs-on: ubuntu-latest
13    steps:
14      - uses: actions/checkout@v4
15
16      - name: Run k6 load test
17        uses: grafana/k6-action@v0.3.1
18        with:
19          filename: tests/performance/load-test.js
20        env:
21          BASE_URL: ${{ secrets.STAGING_URL }}
22          K6_CLOUD_TOKEN: ${{ secrets.K6_CLOUD_TOKEN }}
23
24      - name: Upload results
25        uses: actions/upload-artifact@v4
26        with:
27          name: k6-results
28          path: results.json

Thresholds: автоматичний фейл при порушенні SLA

1export const options = {
2  thresholds: {
3    // 95% запитів швидше за 500мс
4    http_req_duration: ['p(95)<500'],
5    // менше 1% помилок
6    http_req_failed: ['rate<0.01'],
7    // всі checks пройдені
8    checks: ['rate>0.99'],
9  },
10};

Якщо threshold порушено — k6 завершується з exit code 99, CI/CD пайплайн падає. Ідеально для quality gates.

Підсумок

k6 — сучасний вибір для performance testing у 2026. Код як тест, легка CI/CD інтеграція, низьке споживання ресурсів і нативна Grafana інтеграція роблять його кращим варіантом для DevOps-орієнтованих команд.

JMeter залишається актуальним для legacy систем і де потрібна підтримка екзотичних протоколів. Але для нових проектів і команд що прагнуть "testing as code" — k6 є очевидним вибором.

Наступний крок: спробуйте запустити базовий тест на вашому staging середовищі і подивіться на перцентилі часу відповіді. Це займе 15 хвилин і дасть реальне розуміння продуктивності вашого API.