Перейти к содержимому
К программе курса
k6: нагрузочное тестирование как система
6 / 1735%

Executors, scenarios и управление нагрузкой

25 минут

Почему всегда через scenarios

  • K6 принимает executors только внутри options.scenarios.
  • Top-level vus/duration/stagesshortcuts для простых случаев, но не для кастомных executors.
  • Требование курса: все примеры c кастомными executors — через scenarios, чтобы они запускались без сюрпризов.

Готовые шаблоны

// scenarios.js
import http from "k6/http";
import { sleep } from "k6";
 
export const options = {
  scenarios: {
    smoke: {
      executor: "shared-iterations",
      iterations: 50,
      vus: 5,
      maxDuration: "3m",
      tags: { flow: "smoke" },
    },
    load: {
      executor: "ramping-vus",
      startVUs: 10,
      stages: [
        { duration: "5m", target: 100 }, // ramp-up
        { duration: "20m", target: 100 }, // плато
        { duration: "5m", target: 0 }, // ramp-down
      ],
      gracefulRampDown: "2m",
      tags: { flow: "checkout" },
    },
    steady_rps: {
      executor: "constant-arrival-rate",
      rate: 300, // tps/rps
      timeUnit: "1s",
      duration: "15m",
      preAllocatedVUs: 50,
      maxVUs: 300,
      tags: { flow: "browse" },
    },
    external: {
      executor: "externally-controlled",
      vus: 0,
      maxVUs: 500,
      duration: "30m",
      tags: { flow: "ops" },
    },
  },
};
 
export default function () {
  http.get(`${__ENV.BASE_URL}/api/ping`);
  sleep(1);
}

Как выбирать:

  • shared-iterations — быстро прогнать N действий (smoke).
  • per-vu-iterations — фиксированное число итераций на VU, удобно для сценариев с подготовкой state.
  • ramping-vus — реалистичный рост/спад пользователей.
  • constant-arrival-rate и ramping-arrival-rate — держим RPS независимо от скорости ответов (нужен запас VU).
  • externally-controlled — управление нагрузкой из Grafana/k6 Cloud/REST API.

Warmup — это часть сценария. Добавьте первые 3–5 минут с малой нагрузкой, чтобы прогреть JIT, кэши, connection pool и не получить ложный стресс-пик.

Когда что выбирать

  • ramping-vus: важен профиль живых пользователей и think time. Подходит для веб-флоу и UX.
  • constant/ramping-arrival-rate: фиксированный RPS/TPM, имитация API-трафика или очередей. Требует запаса maxVUs.
  • shared/per-vu-iterations: быстрые smoke/регрессии на N действий, минимальный overhead.
  • externally-controlled: ручное управление нагрузкой из Grafana/k6 Cloud или REST API.

Правило: если цель измеряет «пользовательский опыт» — берите ramping-vus; если цель — «держать X rps» — берите constant/ramping-arrival-rate.

Визуализация паттернов нагрузки

ramping-vus фиксирует количество виртуальных пользователей — RPS будет зависеть от скорости ответов. constant-arrival-rate фиксирует RPS — k6 автоматически создаст столько VU, сколько нужно для поддержания целевого RPS. ramping-arrival-rate увеличивает RPS по stages — идеален для поиска breakpoint.

Как k6 исполняет ваш код (важно для лимитов)

  • Один VU = горутина, нет общего event loop как в Node. sleep отдает управление планировщику k6, но не блокирует другие VU.
  • http.batch() запускает запросы параллельно внутри VU, но тайминги учитываются в метриках как отдельные запросы.
  • Нет async/await: k6 синхронно исполняет JS, поэтому «параллельность» только через batch или несколько VU.
  • Планируйте VU и rate исходя из payload/RTT: тяжелые ответы = больше времени в receive → нужно больше VU для удержания целевого RPS.

Troubleshooting: типичные проблемы с executors

Причина: Недостаточно VU для генерации заданного RPS. k6 не может создать больше VU, чем указано в maxVUs.

Решение:

  • Увеличьте maxVUs в 1.5-2 раза от rate (если rate=300, ставьте maxVUs=450-600)
  • Проверьте метрику dropped_iterations — если >0, генератор не успевает
  • Уменьшите sleep в сценарии или используйте http.batch()
  • Если генератор под нагрузкой (CPU >80%) — добавьте ресурсов или распределите нагрузку (k6-operator)
scenarios: {
api_load: {
  executor: "constant-arrival-rate",
  rate: 300,
  timeUnit: "1s",
  duration: "10m",
  preAllocatedVUs: 200,
  maxVUs: 500, // Запас для пиков латентности
}
}

Причина: Генератор k6 не успевает создать новые итерации по заданному расписанию. Это значит, что результаты теста недостоверны — вы не знаете, какой RPS на самом деле был.

Решение:

  • Критично: Остановите тест и устраните проблему, прежде чем делать выводы
  • Проверьте CPU/RAM генератора — если >85%, добавьте ресурсов
  • Увеличьте maxVUs для arrival-rate executors
  • Уменьшите сложность сценария (меньше вычислений в JS)
  • Используйте несколько генераторов (k6-operator с parallelism)

dropped_iterations — это красный флаг. Пока он не 0, нельзя доверять метрикам.

Выбирайте ramping-vus если:

  • Тестируете пользовательский опыт (веб-приложения, мобильные клиенты)
  • Важен think time между действиями (пользователь читает, думает, кликает)
  • RPS не фиксирован — зависит от скорости ответа сервера
  • Нужно симулировать реальное поведение людей

Выбирайте constant-arrival-rate если:

  • Тестируете API с фиксированным входящим трафиком (очереди, webhooks, интеграции)
  • Нужно гарантировать конкретный RPS независимо от латентности
  • Тестируете saturation — "выдержит ли сервис 500 RPS?"
  • Важно проверить автоскейлинг при стабильной нагрузке

Правило большого пальца: Если тест про "сколько пользователей" — используйте ramping-vus. Если про "сколько RPS" — используйте constant-arrival-rate.

Причина: k6 не смог запустить нужное количество VU за отведенное время (обычно из-за нехватки ресурсов).

Решение:

  • Добавьте gracefulRampDown для плавного завершения VU
  • Увеличьте maxDuration для executor
  • Уменьшите количество VU или rate
  • Проверьте, что генератор не упирается в ulimit (файловые дескрипторы): ulimit -n 65535
scenarios: {
load: {
  executor: "ramping-vus",
  startVUs: 0,
  stages: [
    { duration: "5m", target: 100 }
  ],
  gracefulRampDown: "30s", // Даём время завершить итерации
}
}

Причина: Сервер не успевает отвечать, latency растет, поэтому каждый VU делает меньше итераций в секунду.

Что происходит:

  • Добавили VU: 50 → 100
  • Латентность выросла: 200ms → 800ms
  • RPS остался на месте или вырос незначительно

Это нормально для ramping-vus! Executor фиксирует количество VU, а не RPS. Если сервер тормозит, RPS падает.

Решение:

  • Проверьте метрики сервера: CPU, БД, внешние API
  • Это может быть bottleneck — исследуйте через APM/traces
  • Если нужен фиксированный RPS — используйте constant-arrival-rate

Зачем нужен warmup:

  • JIT-компиляция (Java, .NET, Node.js)
  • Прогрев connection pool (БД, Redis)
  • Заполнение кэшей (application-level, CDN)
  • Инициализация thread pool

Без warmup: Первые 1-3 минуты показывают аномально высокую латентность, что искажает результаты.

Решение — добавьте warmup stage:

scenarios: {
load_test: {
  executor: "ramping-vus",
  stages: [
    { duration: "3m", target: 10 },   // Warmup: малая нагрузка
    { duration: "5m", target: 100 },  // Ramp-up
    { duration: "20m", target: 100 }, // Plateau (основной тест)
    { duration: "2m", target: 0 }     // Ramp-down
  ]
}
}

Для constant-arrival-rate:

scenarios: {
warmup: {
  executor: "constant-arrival-rate",
  rate: 50,
  duration: "2m",
  preAllocatedVUs: 20,
  maxVUs: 50,
  startTime: "0s",
},
main_load: {
  executor: "constant-arrival-rate",
  rate: 300,
  duration: "15m",
  preAllocatedVUs: 150,
  maxVUs: 400,
  startTime: "2m", // Начнется после warmup
}
}

Практика: что сделать до следующего урока

1.Создайте load-тест с ramping-vus

  • Напишите сценарий с ramping-vus: warmup 2 мин (10 VU) → ramp-up 3 мин (50 VU) → plateau 10 мин (50 VU) → ramp-down 1 мин (0 VU)
  • Добавьте tags: { flow: 'load-test' }
  • Добавьте thresholds: http_req_duration p(95)<800, http_req_failed rate<0.01
  • Запустите и проверьте, что тест проходит thresholds
  • Экспортируйте summary для baseline: --summary-export baseline-load.json

2.Сравните ramping-vus vs constant-arrival-rate

  • Создайте второй файл с constant-arrival-rate: rate=100, duration=10m
  • Не забудьте preAllocatedVUs=50, maxVUs=200 (запас x2)
  • Запустите оба теста на одном endpoint
  • Сравните: как отличается RPS? Как ведет себя latency при одинаковой нагрузке?
  • Выводы: когда использовать каждый executor?

3.Проверьте здоровье генератора

  • Во время теста мониторьте CPU/RAM машины с k6 (htop / Activity Monitor)
  • Проверьте метрику dropped_iterations в summary (должна быть 0)
  • Если CPU > 80% или dropped_iterations > 0 — уменьшите нагрузку или добавьте ресурсов
  • Запишите вывод: при какой нагрузке ваша машина начинает упираться в генератор?

✅ Чек-лист завершения урока

После этого урока вы должны уметь:

Понимание executors:

  • Знаете, когда использовать ramping-vus vs constant-arrival-rate
  • Понимаете разницу между VU-based (vus) и RPS-based (arrival-rate) executors
  • Знаете, что такое preAllocatedVUs и maxVUs для arrival-rate
  • Понимаете, зачем нужен warmup и как его добавить

Практика с scenarios:

  • Написали тест с ramping-vus: warmup → ramp-up → plateau → ramp-down
  • Написали тест с constant-arrival-rate: rate, preAllocatedVUs, maxVUs
  • Добавили tags для разделения потоков: { flow: 'checkout' }
  • Добавили gracefulRampDown для плавного завершения

Troubleshooting:

  • Знаете, что значит dropped_iterations и почему это критично
  • Умеете проверить CPU/RAM генератора во время теста
  • Понимаете, когда увеличивать maxVUs для arrival-rate
  • Знаете, как добавить несколько scenarios в один тест

Анализ поведения:

  • Запустили ramping-vus и наблюдали, как RPS зависит от latency
  • Запустили constant-arrival-rate и убедились, что RPS стабилен
  • Сравнили результаты: какой executor подходит для вашего сценария?

Практическое задание:

  • Создали load-test.js с правильным executor для вашего user journey
  • Проверили, что dropped_iterations = 0
  • Экспортировали baseline: --summary-export baseline-load.json

Если чек-лист пройден — переходите к уроку 07: научимся моделировать реалистичные пользовательские потоки.

Executors, scenarios и управление нагрузкой — k6: нагрузочное тестирование как система — Potapov.me