k6: сценарное нагрузочное тестирование для разработчиков
k6 — инструмент нагрузочного тестирования, который хорошо вписывается в рабочий процесс разработчика: сценарии пишутся на JavaScript, запускаются из CLI, легко попадают в Git и CI/CD. В отличие от того, чтобы просто «выстрелить» тысячей запросов в один URL, k6 помогает описывать поведение пользователей: логин, просмотр каталога, создание заказа, работу с API, паузы между действиями и разные профили нагрузки.
К k6 обычно переходят, когда простых бенчмарков уже недостаточно. ab или wrk быстро покажут, сколько RPS выдерживает один endpoint, но не ответят, как система ведёт себя при смеси операций, авторизации, зависимостях от базы и внешних сервисов. Если нужен быстрый старт с более простыми утилитами, посмотрите материал про нагрузочное тестирование wrk и ab, а здесь разберём сценарный подход.
k6 не заменяет мониторинг продакшена: нагрузочный тест показывает поведение системы в контролируемых условиях, а мониторинг доступности фиксирует, что происходит с реальным сайтом после релиза. Например, Statuser может проверять сайт с заданным интервалом и присылать уведомление о сбое, пока k6 используется командой для проверки гипотез до выката.
Что такое k6 и чем он удобен разработчику
k6 — open-source инструмент для нагрузочного тестирования HTTP API, веб-сервисов и некоторых других протоколов. Сценарии пишутся на JavaScript, но выполняются не в Node.js, а в отдельном runtime на Go. Поэтому нельзя просто подключить любую npm-библиотеку, зато сам раннер быстрый, предсказуемый и удобный для CLI-запуска.
Главная идея k6: тест — это код. Его можно ревьюить, версионировать, запускать локально, параметризовать переменными окружения и встраивать в пайплайн. Для разработчика это привычнее, чем настраивать сложный GUI-инструмент и хранить сценарии в бинарном формате.
k6 особенно полезен, когда нужно проверить не один endpoint, а пользовательский путь или цепочку API-вызовов. Например:
- открыть главную страницу;
- получить список товаров;
- авторизоваться;
- добавить товар в корзину;
- оформить заказ;
- проверить, что API отвечает корректными статусами и укладывается в latency-бюджет.
Для DevOps-инженера ценность в другом: k6 хорошо автоматизируется. Его можно запускать в Docker, GitHub Actions, GitLab CI, Kubernetes Job, на отдельной машине-генераторе нагрузки. Результаты можно выгружать в Prometheus-compatible хранилища, InfluxDB, Grafana Cloud k6 или обрабатывать из стандартного вывода.
Владельцу сайта k6 помогает ответить на практичные вопросы: выдержит ли сервис рекламную кампанию, не деградирует ли оформление заказа, сколько запаса есть у текущей инфраструктуры, что сломается первым — приложение, база, кэш, балансировщик или внешний API.
Установка и первый тест
Установить k6 можно пакетным менеджером или запустить через Docker. На macOS:
brew install k6На Ubuntu/Debian обычно используют репозиторий Grafana k6 или готовые пакеты из документации проекта. Через Docker можно стартовать без установки:
docker run --rm -i grafana/k6 run - <script.jsДля локальной разработки удобнее установить бинарник и запускать сценарии напрямую:
k6 versionМинимальный тест выглядит так:
import http from 'k6/http';
import { sleep, check } from 'k6';
export const options = {
vus: 10,
duration: '30s',
};
export default function () {
const res = http.get('https://example.com/');
check(res, {
'status is 200': (r) => r.status === 200,
'response time < 500ms': (r) => r.timings.duration < 500,
});
sleep(1);
}Запуск:
k6 run smoke.jsЗдесь есть несколько базовых понятий.
1) vus — виртуальные пользователи. Один VU выполняет функцию default в цикле, пока идёт тест.
2) duration — длительность теста. В примере 10 виртуальных пользователей будут выполнять сценарий 30 секунд.
3) http.get — HTTP-запрос. Также есть http.post, http.put, http.del, http.batch.
4) check — проверка корректности ответа. Она не останавливает тест, но показывает, сколько итераций прошло ожидаемые условия.
5) sleep — пауза между действиями. Без пауз VU будет генерировать запросы максимально быстро, что часто не похоже на реальное поведение пользователей.
После запуска k6 выведет метрики: количество запросов, длительность итераций, ошибки, перцентили времени ответа — подробнее о том, как их читать, в разделе про метрики ниже. Если тема перцентилей в новинку, полезно разобраться, почему p99 важнее среднего.
Сценарии нагрузки: smoke, load, stress и soak
В k6 можно описывать разные профили нагрузки. Не стоит начинать с «сразу 10 000 пользователей». Лучше двигаться ступенями: сначала проверить, что сценарий работает, затем оценить нормальную нагрузку, потом найти пределы.
1) Smoke test — короткий тест на минимальной нагрузке. Нужен, чтобы убедиться, что сценарий не сломан: URL правильные, токены передаются, проверки работают, тестовые данные доступны. Обычно это 1–5 VU и несколько десятков секунд.
2) Load test — проверка ожидаемой рабочей нагрузки. Например, если сервис обычно держит 100–200 активных пользователей, тест должен моделировать именно это поведение, а не абстрактные RPS. Цель — подтвердить, что latency, ошибки и потребление ресурсов остаются в допустимых рамках.
3) Stress test — постепенное повышение нагрузки до деградации. Такой тест отвечает на вопрос: где предел и как система ломается. Хороший результат stress-теста — не только число RPS, но и понимание первого узкого места.
4) Spike test — резкий скачок нагрузки. Полезен для рекламных кампаний, пуш-рассылок, распродаж и новостного трафика. Здесь часто всплывают проблемы с connection pooling, rate limiting, кэшем и очередями.
5) Soak test — длительный тест на умеренной нагрузке. Он помогает найти утечки памяти, накопление соединений, рост очередей, деградацию кешей, проблемы с ротацией логов. Короткий тест может пройти идеально, а через 3–6 часов сервис начнёт отвечать медленнее.
В k6 профили удобно описывать через stages:
import http from 'k6/http';
import { sleep } from 'k6';
export const options = {
stages: [
{ duration: '2m', target: 50 },
{ duration: '5m', target: 50 },
{ duration: '2m', target: 150 },
{ duration: '5m', target: 150 },
{ duration: '2m', target: 0 },
],
};
export default function () {
http.get('https://api.example.com/products');
sleep(1);
}Такой тест сначала плавно поднимает нагрузку до 50 VU, держит её, затем повышает до 150 VU и плавно завершает. Это безопаснее, чем мгновенно ударить по системе максимальной нагрузкой.
Как писать реалистичные пользовательские сценарии
Сильная сторона k6 — сценарность. Вместо одного запроса можно описать путь пользователя и добавить проверки на каждом шаге.
Пример для API:
import http from 'k6/http';
import { check, sleep } from 'k6';
const BASE_URL = __ENV.BASE_URL || 'https://api.example.com';
export const options = {
vus: 20,
duration: '2m',
};
export default function () {
const loginRes = http.post(`${BASE_URL}/auth/login`, JSON.stringify({
email: 'load-user@example.com',
password: 'password',
}), {
headers: { 'Content-Type': 'application/json' },
});
check(loginRes, {
'login status is 200': (r) => r.status === 200,
'token exists': (r) => Boolean(r.json('accessToken')),
});
const token = loginRes.json('accessToken');
const productsRes = http.get(`${BASE_URL}/products`, {
headers: { Authorization: `Bearer ${token}` },
});
check(productsRes, {
'products status is 200': (r) => r.status === 200,
'products returned': (r) => Array.isArray(r.json()),
});
sleep(Math.random() * 3 + 1);
}Здесь есть важная деталь: сценарий проверяет не только скорость, но и корректность. Если API начинает возвращать 500, пустые списки или невалидный JSON, это должно быть видно в результате теста. Для анализа кодов ответов пригодится справочник по HTTP-статусам.
Чтобы сценарии были ближе к реальности, учитывайте несколько правил.
1) Не используйте одного пользователя для всего теста — если все VU логинятся под одной учётной записью, вы проверяете не только систему, но и конфликты вокруг одного пользователя: блокировки, лимиты, кэш, гонки. Лучше заранее создать набор тестовых пользователей и распределять их по VU.
2) Добавляйте think time — реальные пользователи не кликают без пауз. sleep между действиями делает модель нагрузки реалистичнее. Без пауз вы получите синтетический стресс, который может быть полезен, но не равен пользовательскому поведению.
3) Разделяйте чтение и запись — просмотр страниц, поиск и получение карточки товара обычно происходят чаще, чем оформление заказа. Сценарий должен отражать пропорции операций, иначе тест покажет искажённую картину.
4) Учитывайте кэш — если каждый запрос уникален, вы можете обойти кэши и получить худший случай. Если все запросы одинаковые, наоборот, проверите в основном кэш. Обычно нужны оба варианта: реалистичный профиль и отдельный worst-case.
5) Не ломайте продакшен — нагрузочное тестирование лучше проводить на staging-среде, максимально похожей на продакшен. Если тестируете боевой сайт, согласуйте окно, лимиты и мониторинг. Внешний uptime monitoring при этом полезен как независимый сигнал: если сайт перестал отвечать для пользователей, команда быстро узнает о проблеме.
Thresholds: как превратить тест в pass/fail
Нагрузочный тест без критериев превращается в набор графиков. k6 позволяет задавать thresholds — условия, при нарушении которых тест считается проваленным. Это удобно для CI/CD: если p95 latency выше нормы или ошибок слишком много, пайплайн падает.
Пример:
export const options = {
stages: [
{ duration: '1m', target: 20 },
{ duration: '3m', target: 20 },
{ duration: '1m', target: 0 },
],
thresholds: {
http_req_failed: ['rate<0.01'],
http_req_duration: ['p(95)<500', 'p(99)<1200'],
checks: ['rate>0.99'],
},
};Что здесь задано:
- доля HTTP-ошибок должна быть меньше 1%;
p95времени ответа должен быть меньше 500 мс;p99должен быть меньше 1200 мс;- успешными должны быть больше 99%
check.
Критерии лучше привязывать к пользовательскому опыту и SLO, а не брать с потолка. Для публичного сайта может быть критична скорость открытия главной страницы, для API — latency записи, для внутренней админки — стабильность и отсутствие ошибок. Подробнее про практичные метрики API можно почитать в статье про мониторинг HTTP API: RED-метрики, latency и error budget.
Хорошая практика — иметь несколько наборов threshold:
1) Локальный smoke — мягкие ограничения, чтобы быстро ловить явные ошибки в сценарии.
2) CI-тест перед merge — короткий тест на небольшой нагрузке, который не перегружает стенд, но проверяет базовую производительность.
3) Ночной или предрелизный тест — более длинный профиль с рабочей нагрузкой и строгими условиями.
4) Stress-тест вручную — не всегда должен падать по threshold, потому что его цель — найти пределы, а не подтвердить норму.
Threshold не отменяет анализа графиков. Если тест прошёл, но p99 начал приближаться к порогу, это уже сигнал. Особенно если одновременно растут CPU, использование пула соединений, очереди в брокере или время запросов к базе.
Метрики k6: что смотреть в результате
После запуска k6 выводит набор метрик. На первых тестах легко утонуть в цифрах, поэтому начните с базового набора.
1) http_reqs — общее количество HTTP-запросов. Само по себе число не говорит о качестве, но помогает понять масштаб теста.
2) http_req_duration — полное время запроса от начала до конца. Смотрите не только avg, но и p(90), p(95), p(99). Именно хвосты часто показывают проблемы, которые не видны по среднему.
3) http_req_failed — доля неуспешных HTTP-запросов. Ошибки нужно расшифровывать: 500, 502, 503, 504, 429, таймауты, TLS-ошибки — это разные причины и разные зоны ответственности.
4) checks — успешность ваших бизнес-проверок. Endpoint может отвечать 200, но возвращать неправильное тело. checks помогают ловить такие случаи.
5) iteration_duration — длительность полной итерации сценария. Если один пользовательский путь включает логин, поиск и оформление заказа, эта метрика ближе к пользовательскому опыту, чем время отдельного запроса.
6) vus и vus_max — фактическое количество виртуальных пользователей. Полезно проверять, что тест действительно выполняется в ожидаемом профиле.
Метрики k6 нужно сопоставлять с серверными метриками. Если растёт p99, смотрите CPU, память, GC, количество соединений к базе, slow queries, очереди, saturation на балансировщике, сетевые ретрансляции. Один k6 покажет симптом, но не всегда объяснит причину.
Хороший набор наблюдаемости для нагрузочного теста:
- метрики приложения;
- метрики базы данных;
- метрики Nginx или балансировщика;
- логи ошибок;
- трассировки медленных запросов;
- системные метрики хоста или контейнера.
Если инфраструктура контейнерная, полезно заранее разобраться с сетями и лимитами. Для локальных стендов пригодится статья про Docker Compose, а для боевых сервисов — полноценный мониторинг ресурсов и зависимостей.
Параметризация, тестовые данные и переменные окружения
Реальный тест почти всегда требует параметров: адрес стенда, токены, логины, лимиты, режимы нагрузки. В k6 для этого удобно использовать переменные окружения через __ENV.
const BASE_URL = __ENV.BASE_URL;
const API_TOKEN = __ENV.API_TOKEN;
export default function () {
http.get(`${BASE_URL}/profile`, {
headers: { Authorization: `Bearer ${API_TOKEN}` },
});
}Запуск:
BASE_URL=https://staging.example.com API_TOKEN=secret k6 run test.jsТак сценарий не привязан к конкретному окружению и не хранит секреты в репозитории.
Для тестовых данных можно использовать JSON-файлы:
import { SharedArray } from 'k6/data';
const users = new SharedArray('users', function () {
return JSON.parse(open('./users.json'));
});
export default function () {
const user = users[__VU % users.length];
// используем user.email и user.password
}SharedArray загружает данные один раз и разделяет их между VU, что снижает потребление памяти.
С тестовыми данными есть несколько типичных проблем.
1) Данные заканчиваются — если сценарий создаёт уникальные сущности, нужно заранее решить, как генерировать ID, очищать окружение и избегать конфликтов.
2) Тест загрязняет стенд — создание заказов, пользователей и файлов может быстро превратить staging в мусор. Нужны cleanup-джобы или отдельная тестовая база.
3) Сценарий упирается в внешние сервисы — платёжки, SMS, email, антифрод и CRM не всегда можно нагружать. Их лучше стабилизировать моками или sandbox-режимами.
4) Лимиты безопасности мешают тесту — WAF, rate limiting, captcha, антибот-защита могут заблокировать генератор нагрузки. Иногда это именно то, что нужно проверить, но часто такие механизмы искажают тест приложения. Если проверяете рейт-лимиты осознанно, полезен материал про алгоритмы rate limiting.
Запуск k6 в Docker и CI/CD
k6 удобно запускать в CI, потому что тесты — обычные файлы в репозитории, а результат можно превратить в pass/fail через thresholds.
Пример запуска в Docker:
docker run --rm \
-e BASE_URL=https://staging.example.com \
-v "$PWD/tests:/scripts" \
grafana/k6 run /scripts/load.jsВ GitHub Actions шаг может выглядеть так:
- name: Run k6 load test
uses: grafana/k6-action@v0.3.1
with:
filename: tests/load.js
env:
BASE_URL: https://staging.example.comНо включать полноценный нагрузочный тест на каждый коммит обычно не стоит: он может быть долгим, дорогим и шумным. Разумнее разделить уровни.
1) Pull request — короткий smoke-тест, который проверяет, что основные сценарии не сломались.
2) Merge в main — небольшой performance regression test на стабильном стенде.
3) Ночной запуск — нагрузочный профиль на несколько минут или часов, если есть ресурсы.
4) Перед релизом — ручной или полуавтоматический тест с согласованным окном.
5) После значимых изменений инфраструктуры — отдельный тест при изменении Nginx, балансировщика, пула соединений, базы, кэша или очередей.
CI/CD с нагрузочным тестированием требует стабильного окружения. Если стенд каждый раз разный, результаты будут прыгать. Если на том же окружении параллельно работают другие тесты, вы получите шум. Если база маленькая и не похожа на продакшен, проверка может дать ложную уверенность.
Подробнее про пайплайны и автоматизацию релизов можно почитать в статье что такое CI/CD и как задеплоить проект с помощью GitHub Actions. k6 хорошо вписывается в такой процесс, если использовать его не как «стресс на каждом пуше», а как контролируемый quality gate.
Частые ошибки при нагрузочном тестировании с k6
k6 прост в запуске, но методология всё равно важна. Самые неприятные ошибки не в синтаксисе JavaScript, а в неверной постановке эксперимента.
1) Тестируют не то, что делают пользователи — один endpoint /health выдерживает огромный RPS, но это ничего не говорит о поиске, авторизации, корзине или личном кабинете. Сценарий должен отражать ключевые пользовательские пути.
2) Смотрят только на RPS — высокий RPS бесполезен, если p99 latency неприемлемый, растёт доля ошибок или система не успевает обрабатывать фоновые задачи.
3) Игнорируют базу данных — приложение может выглядеть быстрым на пустой базе и резко деградировать на данных, похожих на продакшен. Индексы, планы запросов, connection pooling и блокировки часто важнее CPU веб-сервера.
4) Запускают генератор нагрузки рядом с тестируемым сервисом — если k6 и приложение делят один хост, CPU и сеть, вы тестируете конкуренцию за ресурсы. Генератор лучше выносить отдельно.
5) Не проверяют ошибки бизнес-логики — статус 200 не гарантирует, что операция успешна. Проверяйте тело ответа, обязательные поля, состояние созданных сущностей.
6) Слишком резко начинают тест — мгновенный скачок до максимальной нагрузки может быть нужен для spike-теста, но для обычного load-теста лучше использовать плавный ramp-up.
7) Нет мониторинга во время теста — без метрик сервера вы увидите, что «стало медленно», но не поймёте почему. Подготовьте дашборды и логи заранее.
8) Нет плана остановки — особенно при тестировании продакшена: нужен ответственный, согласованное окно работ и понятный критерий, когда тест немедленно прекращается.
После теста полезно оформить краткий отчёт: профиль нагрузки, окружение, версия приложения, данные, thresholds, основные графики, найденные узкие места, решения и повторный результат. Это помогает не спорить по памяти и отслеживать динамику производительности от релиза к релизу.
FAQ
Можно ли использовать k6 для тестирования фронтенда в браузере?
Базовый k6 тестирует протоколы и HTTP-запросы, а не полноценный браузерный рендеринг. Для браузерных сценариев есть browser-модуль k6, но для оценки UX фронтенда часто нужны отдельные инструменты.
Чем k6 лучше wrk или ab?
wrk и ab хороши для быстрых бенчмарков одного endpoint. k6 удобнее, когда нужны сценарии, проверки, разные профили нагрузки, thresholds и интеграция в CI.
Можно ли запускать k6 по продакшену?
Можно, но осторожно: с согласованным окном, ограничениями нагрузки, мониторингом и планом остановки. Часто безопаснее использовать staging, максимально похожий на продакшен.
Нужно ли хранить k6-тесты в репозитории проекта?
Да, если они проверяют поведение конкретного сервиса. Так сценарии проходят ревью, обновляются вместе с API и могут запускаться в CI/CD.
k6 полезен не как разовая «пушка по серверу», а как инженерный инструмент проверки производительности. Начните со smoke-сценариев, добавьте реалистичные пользовательские пути, задайте thresholds и сопоставляйте результаты с метриками приложения. А после релиза не забывайте про внешний мониторинг доступности — он покажет, если что-то пошло не так уже в реальной эксплуатации.
Похожие статьи

Нагрузочное тестирование с wrk и ab: быстрый старт
Практический гид по быстрой проверке сайта и API под нагрузкой с помощью консольных утилит ab и wrk.
2 ноября 202610 мин

Как использовать hping3 для тестирования сетевой безопасности и DDoS-симуляции
Изучаем функционал hping3 — мощного инструмента для генерации сетевых пакетов и имитации атак. Как с его помощью проводить тесты безопасности и анализировать сетевую нагрузку.
11 августа 20257 мин

Server-Sent Events (SSE) vs WebSockets. Что выбрать для realtime
Подробно разбираем отличия SSE и WebSockets, особенности realtime-соединений, нагрузку, масштабирование и практические сценарии использования.
26 мая 20267 мин
Настроить мониторинг за 30 секунд
Надёжные уведомления о даунтаймах. Без ложных срабатываний