Executors, scenarios и управление нагрузкой
Почему всегда через scenarios
- K6 принимает executors только внутри
options.scenarios. - Top-level
vus/duration/stages— shortcuts для простых случаев, но не для кастомных 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-vusvsconstant-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: научимся моделировать реалистичные пользовательские потоки.