core code helper

Навантажувальне тестування з k6: від першого скрипта до порогів

Як написати навантажувальний тест на JavaScript, змоделювати реалістичне навантаження, задати пороги якості і не зробити типових помилок в інтерпретації результатів.

0 імпортували Імпортувати собі

Функціональні тести відповідають на питання «чи працює?». Навантажувальні — «чи працюватиме, коли прийдуть користувачі?». k6 — інструмент від Grafana Labs, у якому сценарії пишуться на JavaScript, а запускаються одним бінарним файлом.

Перший скрипт

// smoke.js
import http from 'k6/http';
import { check, sleep } from 'k6';

export const options = {
  vus: 5,          // віртуальних користувачів
  duration: '1m',
};

export default function () {
  const res = http.get('https://staging.example.com/api/products?limit=20');
  check(res, {
    'статус 200': (r) => r.status === 200,
    'є товари': (r) => r.json('items').length > 0,
  });
  sleep(1); // пауза «користувача» між діями
}
k6 run smoke.js

Кожен віртуальний користувач (VU) у циклі виконує функцію default. Наприкінці k6 виводить зведення: кількість запитів, частку помилок і розподіл часу відповіді.

Типи тестів

Тип Мета Профіль навантаження
Smoke Скрипт працює, система жива 1–5 VU, 1 хв
Load Поведінка при очікуваному навантаженні Плавний ріст до цільового, 15–30 хв
Stress Де межа і як система деградує Ріст вище очікуваного
Spike Різкий сплеск (розсилка, реклама) Стрибок у 5–10 разів за секунди
Soak Витоки пам'яті, переповнення пулів Помірне навантаження 2–8 год

Реалістичне навантаження: сценарії та arrival rate

Кількість VU — погана метрика: якщо сервер сповільнився, VU роблять менше запитів, і навантаження саме собою падає. Для API краще задавати швидкість надходження запитів:

export const options = {
  scenarios: {
    browse: {
      executor: 'ramping-arrival-rate',
      startRate: 10,
      timeUnit: '1s',
      preAllocatedVUs: 50,
      maxVUs: 300,
      stages: [
        { target: 100, duration: '5m' },  // ріст до 100 запитів/с
        { target: 100, duration: '15m' }, // тримаємо
        { target: 0, duration: '2m' },
      ],
    },
  },
};

Пороги: тест має падати

Без порогів навантажувальний тест — просто генератор графіків. Thresholds перетворюють його на перевірку, яка може зламати пайплайн:

export const options = {
  thresholds: {
    http_req_failed: ['rate<0.01'],                  // помилок менше 1%
    http_req_duration: ['p(95)<400', 'p(99)<1000'],  // 95% запитів швидше за 400 мс
    'http_req_duration{name:checkout}': ['p(95)<800'],
  },
};
Важливо

Дивіться на перцентилі, а не на середнє. Середнє 200 мс може приховувати те, що кожен двадцятий користувач чекає 5 секунд. p95 і p99 показують досвід «невдах» — а їх при великій кількості запитів дуже багато.

Сценарій користувача з даними

import http from 'k6/http';
import { check, group, sleep } from 'k6';
import { SharedArray } from 'k6/data';

const users = new SharedArray('users', () => JSON.parse(open('./users.json')));

export default function () {
  const user = users[__VU % users.length];

  group('login', () => {
    const res = http.post(`${__ENV.BASE_URL}/api/login`, JSON.stringify(user), {
      headers: { 'Content-Type': 'application/json' },
      tags: { name: 'login' },
    });
    check(res, { 'вхід успішний': (r) => r.status === 200 });
  });

  group('checkout', () => {
    const res = http.post(`${__ENV.BASE_URL}/api/checkout`, '{"cartId":"demo"}', {
      headers: { 'Content-Type': 'application/json' },
      tags: { name: 'checkout' },
    });
    check(res, { 'замовлення створено': (r) => r.status === 201 });
  });

  sleep(Math.random() * 3 + 1);
}
Порада

Тег name групує запити з різними URL (/orders/1, /orders/2…) в одну метрику. Без нього звіт розсиплеться на тисячі рядків.

Типові помилки

  • Генератор навантаження — вузьке місце. Слідкуйте за CPU машини з k6; якщо вона завантажена на 100%, результати брешуть.
  • Тест проти продакшену без попередження. Узгодьте з командою, вимкніть алерти або тестуйте на окремому середовищі.
  • Кеш «прикрашає» результати. 10 000 однакових запитів до одного товару — це тест кешу, а не бази.
  • Немає базової лінії. Зберігайте результати попередніх прогонів і порівнюйте тренд, а не одне число.
  • Дивитись лише на k6. Паралельно дивіться метрики сервера: CPU, пам'ять, з'єднання з БД, довжину черг. Саме там видно, чому p95 виріс.
Чому для тестування API краще задавати arrival rate, а не кількість VU?

При фіксованій кількості VU кожен користувач чекає відповіді перед наступним запитом. Якщо сервер сповільнюється, запитів стає менше — і тест «пом'якшує» навантаження саме тоді, коли треба його тримати. Arrival rate підтримує задану кількість запитів за секунду незалежно від швидкості відповідей.

Збережіть статтю собі

Увійдіть, щоб імпортувати її у власні нотатки, відмічати вивчене, ставити лайки й питати спільноту. Реєстрація безкоштовна: вхід через Google займає кілька секунд.

Увійти через Google Інші способи входу