Shift-Left Performance Tests в CI/CD: k6, Gatling, Locust

Деградация обычно обнаруживается уже после релиза, когда с системой работают пользователи. Мы настраиваем раннее нагрузочное тестирование прямо в конвейере сборки: проверки идут на каждом изменении, до выкладки. Это сокращает поиск узких мест в разы и снимает риск отказов в пик нагрузки.
Часто задаваемые вопросы

Shift left performance testing: где ломается конвейер

Нагрузочный тест, запущенный за неделю до релиза, почти всегда показывает не то, что нужно. Профиль нагрузки снят с продакшена месячной давности, кэши холодные, объём данных меньше боевого — деградация производительности под пиком всплывает уже после выката, когда пользователи уходят, а не в отчёте.

Позднее обнаружение отказов стоит дорого не самой правкой, а цепочкой последствий: инцидент, откат, повторный прогон, сдвиг сроков. Чем позже этап, тем дороже исправление: на проектировании это одно решение в схеме, на приёмке — пересборка релиза и повторные согласования. Если узкое место нашлось на приёмке, архитектуру уже не переиграть — остаётся оптимизировать отдельные запросы и добавлять ресурсы, то есть лечить симптом вместо причины.

Отдельная проблема — узкое место сборки. Полный нагрузочный прогон занимает десятки минут и держит очередь релизов, поэтому его выносят за пределы конвейера. Нагрузочное тестирование в конвейере превращается в ручную операцию перед крупным релизом: регрессии копятся месяцами, а виноватым оказывается «медленный стенд».

Shift left performance testing — это перенос проверки производительности на этап, где цена исправления ещё низкая: короткие сценарии на каждое изменение, расширенный профиль на ветку, полный — по расписанию. Ниже разбираем, как это закрывается на практике.

Что даёт раннее нагрузочное тестирование

Нагрузочный прогон, запущенный за день до релиза, даёт вердикт, когда менять уже нечего: узкое место видно, а переписывать контур сбора данных некогда. Раннее нагрузочное тестирование переносит тот же прогон в контур сборки — например, сценарии на k6 — и отвечает на вопрос «сколько держит» на этапе, где правка стоит правки, а не переписывания.

  • Время обратной связи. Вердикт по нагрузочному профилю приходит в том же прогоне, что и модульные тесты: инженер видит регресс через минуты после отправки правки, а не через дни ожидания отдельного стенда.
  • Доля дефектов производительности, найденных до релиза. Утечки соединений, неограниченные выборки, блокировки при конкурентном доступе ловятся на тестовых данных и уходят в работу раньше промышленного контура. Целимся в 80% и выше.
  • Устойчивость под нагрузкой. Пиковый профиль — сезонный всплеск, ночная пакетная обработка, рассылка — прогоняется регулярно, поэтому поведение на пределе известно заранее, а не выясняется на инциденте.
  • Стоимость простоя. Дефект из контура сборки — это часы правки; тот же дефект в промышленном контуре — часы недоступности сервиса и разбор инцидента.
  • Регрессия по базовым линиям. Время ответа ключевых операций и доля ошибочных ответов хранятся рядом с кодом и сравниваются от сборки к сборке: деградация на 10–15% видна как упавшая проверка, а не как жалоба пользователя.
  • Пороговые значения как условие выпуска. Сценарий с порогом по времени ответа и доле ошибок останавливает сборку — правило одно для всех, толковать нечего.

Режимы запуска нагрузочных проверок: сравнение

Три режима нагрузочных проверок не конкурируют, а отвечают на разные вопросы: коммит — не появилась ли регрессия на горячем пути, расписание — куда уходит деградация за недели, предрелиз — выдержит ли система боевой профиль. Чем чаще прогон, тем короче профиль нагрузки и уже набор сценариев, иначе проверка перестаёт успевать за темпом сборок.

Режим Когда уместен Чем ограничен
Короткий профиль на каждый коммит Регрессия по горячим сценариям: порог по 95-му перцентилю, доля ошибок, пропускная способность Единицы минут бюджета сборки, малый объём данных и виртуальных пользователей; не покажет утечки и деградацию на длинной серии
Прогоны по расписанию Тренды и стабильность: серия раз в сутки, поиск утечек, сравнение с прошлыми результатами Обратная связь приходит через часы; результаты надо где-то хранить и сравнивать между собой
Предрелизные замеры Ёмкость перед выкаткой: длительный и ступенчатый профиль, данные, близкие к боевым Дорого по стенду и времени, нужна изоляция; не ловит регрессию отдельного коммита

Режимы дополняют друг друга: сценарий переносится между k6, Gatling и Locust без переписывания логики, отличаются только профиль нагрузки и правила остановки сборки. Провал порогового значения на коммите блокирует слияние, ночной прогон заводит задачу на разбор, предрелизный замер останавливает выкатку до выяснения причин.

Этапы внедрения нагрузочных проверок в конвейер

Нагрузочные проверки в конвейере не появляются одним коммитом: сначала нужно понять, что именно и в каком режиме нагружать. Ниже — последовательность шагов, по которой мы ведём внедрение: она даёт первые достоверные замеры до того, как проверки станут блокирующими для релиза.

  1. Аудит контура. Разбираем, из каких сервисов состоит система, какие внешние зависимости участвуют в запросе и где в конвейере точка, куда встраивается запуск. На выходе — карта контура и список сценариев, которые имеет смысл нагружать.
  2. Снятие базового профиля нагрузки. Собираем статистику реальных обращений: соотношение чтения и записи, пиковые окна, распределение по эндпоинтам. Без этого профиля результат прогона не с чем сравнить.
  3. Проектирование схемы проверок. Фиксируем, что запускается на каждый коммит, а что — перед выкладкой, задаём бюджет времени и пороги, за которыми сборка останавливается.
  4. Настройка стенда и сценариев. Разворачиваем изолированное окружение, сопоставимое с продуктивом, описываем сценарии на k6, настраиваем подачу данных и сбор метрик.
  5. Проверка на реальном релизе. Прогоняем схему на боевом изменении, сверяем прогноз с фактическим поведением и калибруем пороги, чтобы проверки не срабатывали на естественных колебаниях.
  6. Передача и сопровождение. Отдаём конфигурации, панели метрик, регламент реакции на падение и порядок обновления сценариев при изменении продукта.

Артефакты внедрения: конфигурации, схемы, регламенты

Внедрение нагрузочных проверок даёт эффект, когда результат остаётся в репозитории и базе знаний, а не в переписке и головах. Ниже — минимальный комплект, который мы передаём команде: по нему новый инженер воспроизводит прогон без устных пояснений, а дежурный понимает, что делать с упавшей сборкой.

  • Сценарии нагрузочных тестов — версионируются рядом с кодом сервиса: пользовательские пути, подготовка данных, ожидаемые ответы. Сценарий привязан к ветке и запускается на изменения в затронутых модулях, а не «когда-нибудь перед релизом».
  • Профили нагрузки — ступенчатый разгон, стабильная полка, пиковый всплеск; для каждого заданы длительность, интенсивность и условия досрочной остановки.
  • Пороговые значения в виде кода — доля ошибок, время ответа на медленном хвосте запросов, пропускная способность. Сборка падает по формальному признаку, а не по трактовке дежурного.
  • Регламент реагирования — кто смотрит отчёт и в каком окне, что делает при деградации: блокирует слияние, откатывает релиз или заводит разбор с приложенным профилем.
  • Документация контура — схема стендов, источники тестовых данных, чем нагрузочное окружение отличается от продуктивного и какие проверки на нём недостоверны.
  • Инструкция по запуску — прогон локально, в конвейере и на выделенном стенде: какие параметры задать, где смотреть отчёт, как повторить проблемный запуск.
  • Базовые линии производительности — зафиксированные результаты прошлых релизов и правило их обновления, чтобы сравнение всегда было с чем вести.
  • Хранение отчётов и артефактов — срок жизни, привязка к сборке и коммиту.

Дальше комплект живёт как обычный код: правки проходят ревью, а устаревшие сценарии удаляются вместе с выведенными из эксплуатации путями.

Кейс: убрали деградацию перед пиковым релизом

В одном из проектов — сервис с плановыми пиками нагрузки — регресс производительности регулярно всплывал уже после выпуска: узкое место в изменённом запросе находили на проде, когда часть заказов переставала оформляться. Разбор инцидента и ручной откат съедали ночь, а следующая попытка релиза сдвигалась на сутки.

Что перестроили

Нагрузочный контур перенесли в основную сборку: сценарии на k6 проходят на каждом изменении ветки, а пороги по времени ответа и доле ошибок зашиты в сам сценарий и роняют сборку при выходе за них. Профиль нагрузки приблизили к боевому — те же соотношения чтения и записи, те же пиковые окна, только на меньшем объёме данных. Релизный артефакт стал неизменяемым: каждая сборка получает свой тег в реестре, предыдущая версия остаётся готовой к переключению.

Метрика До После
Прогон нагрузочного контура раз в сутки, вручную на каждом изменении ветки
Дефекты производительности, найденные до релиза около 30% более 90%
Окно отката при провале релиза часы ручной работы минуты, переключение тега
Длительность прогона росла вместе с числом сценариев укладывается в бюджет сборки за счёт параллельных исполнителей

Деградации теперь ловятся на этапе сборки, когда правка стоит минуты. Окно отката сжалось до переключения тега — провалившийся релиз перестал быть событием с ночной переработкой и переносом сроков.

Чем shift left отличается от предрелизных замеров?

Разница в первую очередь в том, где стоит точка контроля в конвейере. Подход shift left переносит её в самое начало — на изменение, которое ещё не слито в основную ветку: сценарии нагрузки прогоняются на коротком наборе операций против сборки ветки, и результат видит автор правки. Предрелизные замеры стоят у выхода: стенд собирается из уже принятого кода, полный прогон идёт перед выпуском.

Из этого следует, кто первым получает сигнал о деградации. В сдвинутом влево конвейере это разработчик — в том же обсуждении изменения, пока он помнит, что и зачем менял, и пока разбор стоит одной правки. При предрелизных замерах сигнал приходит релизному инженеру или тестировщику уже за день-два до выпуска, и дальше начинается поиск виновника по всей накопленной истории.

Поведение при провале порога тоже разное. Если сценарий вышел за границы по перцентилю времени ответа или по пропускной способности, слияние блокируется: деградация физически не попадает в основную ветку, а базовая линия пересматривается осознанно, отдельным решением. На предрелизном стенде выбор беднее — либо сдвигать дату выпуска, либо выпускать с известным отклонением и закрывать его в следующем релизе.

Практический эффект — в сокращении окна между появлением деградации и её обнаружением: с дней до минут, а объём кода для разбора падает до одной правки вместо релизной сборки. Обратная сторона — ложные срабатывания на нестабильном окружении, поэтому пороги считают по перцентилям и держат стенд предсказуемым: иначе проверке перестают доверять и её отключают.

При этом сдвинутые влево проверки не заменяют предрелизные. Короткие прогоны не покажут поведение под длительной нагрузкой, накопление памяти и работу с реальным профилем данных. Рабочая схема — два уровня: быстрый контроль на каждом изменении и полный прогон перед выпуском. Ниже разбираем, как это устроено и по каким признакам выбирать пороги.

Частые технические сомнения и ответы

Сомнения на старте обычно одни и те же: не станет ли контур источником ложных падений сборки, хватит ли ресурсов стенда, что делать с тестовыми данными и придётся ли ломать то, что уже развёрнуто своими силами. Сдвиг нагрузочных проверок влево (shift-left) упирается не в инструменты, а именно в эти вопросы, поэтому отвечаем по существу.

Ложные срабатывания не начнут блокировать сборку?

Пороги ставим на p95 и p99, а не на среднее время отклика, и требуем повторяемости отклонения в двух прогонах подряд. Деградация отделяется от разового выброса на уровне данных — сборка падает только на воспроизводимом регрессе.

Что делать с нестабильным стендом?

Если окружение делят несколько команд, замеры несопоставимы. Либо выносим нагрузочный профиль на изолированный контур, либо фиксируем базовую линию и сравниваем только внутри неё — при той же конфигурации и том же наборе данных.

Как готовятся тестовые данные?

Набор версионируем вместе с профилем нагрузки и наполняем до прогона, а не в его процессе. Состав и объём фиксированы, персональные данные обезличены — прогоны воспроизводимы и не задевают рабочие записи.

У нас уже развёрнут свой контур — всё придётся менять?

Нет. Подключаемся к тому, что есть, — k6, Gatling или Locust, — и достраиваем недостающее: пороги, отчёты, гейт в сборочной линии и связь результата с конкретным изменением в коде.

Что будет после передачи?

Остаётся не только документация: регламент разбора падений, инструкции по обновлению профилей, обучение команды и разбор инцидентов в переходный период. Дальше — сопровождение или точечные доработки по договорённости.

Обсудим вашу задачу и вернёмся с планом

Чтобы оценка контура опиралась на факты, а не на догадки, пришлите то, что у вас уже есть: конфигурации сборки и проверок, список сервисов с внешними зависимостями, заметки о том, где сейчас упирается процесс — время сборки, ожидание окружений, ручные прогоны перед выкаткой. Готовить вычищенные отчёты и презентации не нужно: чем ближе материал к реальному состоянию репозитория, тем точнее получится план внедрения проверок.

Разбираем присланное и возвращаемся с конкретикой:

  • разбор задачи — что именно ограничивает скорость и где предел текущей схемы;
  • схема проверок — какие сценарии производительности ставим на этап сборки, какие оставляем перед выкаткой, какие уводим в ночной прогон;
  • карта окружений — где поднимаются нагрузочные стенды и как они изолируются от рабочих данных;
  • пороги и правила отказов — по каким метрикам сборка останавливается, а по каким только помечает результат;
  • перечень изменений — что правим в конфигурациях, что добавляем, что выводим из процесса;
  • этапы и сроки — последовательность работ с точками, где вы видите результат, а не только отчёт.

Опишите задачу своими словами — обсудим её и вернёмся с оценкой контура, планом внедрения проверок и сроками по каждому этапу.

Наши компетенции:

Последние работы

  • Разработка сайта компании B2B ADVANCE
    Разработка сайта компании B2B ADVANCE
    1542
  • Разработка веб-приложения для компании FEEDME
    Разработка веб-приложения для компании FEEDME
    1393
  • Разработка веб-сайта для компании БЕЛФИНГРУПП
    Разработка веб-сайта для компании БЕЛФИНГРУПП
    1081
  • Разработка интернет магазина для компании FURNORO
    Разработка интернет магазина для компании FURNORO
    1368
  • Разработка логотипа компании B2B Advance
    Разработка логотипа компании B2B Advance
    815
  • Разработка веб-приложения для компании Enviok
    Разработка веб-приложения для компании Enviok
    1139