Нагрузочное тестирование как инженерный процесс

После каждого серьезного инцидента возникает один и тот же вопрос: почему проблему не удалось обнаружить заранее?
Часто причина оказывается довольно простой. Нагрузочное тестирование либо не проводилось вовсе, либо сводилось к запуску набора запросов без четких целей и критериев оценки.
В результате команда знает, что тест был выполнен, но не может ответить на несколько ключевых вопросов: выдержит ли система целевую нагрузку? При каком уровне нагрузки начинается деградация? Какой запас производительности остается? Нужно ли уже сейчас планировать оптимизацию или масштабирование?
Если после тестирования эти ответы не получены, значит, это было не полноценное нагрузочное тестирование, а эксперимент с неизвестным результатом.
В нашем департаменте мы столкнулись именно с этой проблемой. Разные команды использовали разные подходы, сценарии и инструменты, поэтому результаты было сложно сравнивать, а выводы зависели скорее от опыта конкретного инженера, чем от единого процесса.
Поэтому мы внедрили методологию нагрузочного тестирования.
Ее задача не заменить инструменты или изменить существующие практики, а сделать процесс единообразным и воспроизводимым: от подготовки к тестированию и сбора требований до анализа результатов и принятия инженерных решений.
В этой статье я не буду подробно разбирать каждый документ или шаблон методологии. Вместо этого покажу, какие задачи она решает и почему системный подход к нагрузочному тестированию оказывается значительно полезнее разовых запусков «для проверки».
Зачем нужно нагрузочное тестирование
Многим знакома следующая ситуация.
Система работает стабильно, маркетинговая кампания приводит новых пользователей, продажи растут. Внезапно что-то пошло не так, графики по нагрузке пошли сильно вверх, а графики по доступности резко вниз. Разбирались всю ночь, чинили, поднимали, потеряли много денег, много клиентов, много заказов. Такая ситуация знакома многим командам. В чем тогда проблема?

Проблема зачастую в том, что команды обладают ложной уверенностью в том, что все будет хорошо и система готова к ожидаемой нагрузке. На практике такая уверенность далеко не всегда подтверждается объективными данными. И это неправильно. Вначале разберемся, что же такое НТ.
Что такое нагрузочное тестирование
Нагрузочное тестирование не сводится к запуску большого количества запросов или проверке отдельных API. Его цель значительно шире.
НТ — это не про подергать какие-то ручки, пострелять в эндпойнты и посмотреть, что же все-таки там произойдет. Нагрузочное тестирование часто воспринимают как запуск большого количества запросов. На практике его главная задача совсем другая. Оно должно помочь команде принять инженерные решения.
НТ — это про проверку того, что система выдерживает свои нефункциональные требования под целевой нагрузкой. И это уже не просто про расстрел наших API и надежду, что все будет здорово.

Если после НТ команда не может ответить на несколько ключевых вопросов, значит, тестирование не выполнило свою задачу. Удерживает ли система целевую нагрузку? При какой нагрузке начинается деградация? Какой запас производительности остается? Нужно ли уже сейчас что-то менять?
Если тестирование не позволяет получить ответы на эти вопросы, то, скорее всего, речь идет не о полноценном нагрузочном тестировании.
Когда нагрузочное тестирование необходимо

Когда это становится уже не просто чем-то интересным и nice to have, а необходимой практикой?
В методологии мы выделили восемь таких случаев. Если обобщить, НТ особенно важно там, где отказ системы под нагрузкой может привести к существенным потерям: денег, клиентов, заказов или репутации. Если мы теряем деньги, клиентов, заказы, репутацию, если мы можем упасть из-за того, что есть рост нагрузки, то НТ нужно.
Почему тестирование «по наитию» не работает
Почему тестировать по наитию, просто пострелять в эндпоинт и так далее плохо? Потому что, зачастую, нет фиксированных требований, нет профиля, мы не эмулируем ту нагрузку, которую создают пользователи, а мы просто выдумываем что-то.
У нас нет критериев оценки результатов и понимания, какие инженерные решения должны последовать по итогам тестирования. В итоге такой тест превращается скорее в эксперимент, чем в полноценную проверку системы
Что делает методология с этой точки зрения? Она рассматривает процесс НТ как сквозной процесс, который должен происходить в каждой команде от подготовки до анализа результатов, повторяемо, детерминировано и прогнозируемо.

Вполне себе понятные шесть шагов, выполнив которые, получаем понятные результаты и решения, которые нам необходимо предпринимать относительно нашей системы.
Методология нагрузочного тестирования: этап 1. Подготовка
Подготовка к нагрузочному тестированию включает три основных направления: функциональную, интеграционную и организационно-техническую готовность.
Три простых направления. Нужно убедиться, что все готово с точки зрения функциональности. Это протестированный, понятный артефакт, который стоит на стенде и в нем нет дефектов.
Интеграционная готовность. Тоже важная история: нужно убедиться, что мы со всеми интегрированы, ни у кого нет никаких проблем и так далее.
Почему эти два направления важны? Потому что если мы в них не убедились, мы будем тестировать не систему под нагрузкой, а дефекты под нагрузкой. Результат получим не очень интересующий нас.
И организационно-техническая готовность — это история про то, что нам нужно убедиться в том, что у нас есть все репозитории, раннеры готовы, люди обучены, все знают, что делать и так далее.

Важный здесь тоже момент, о котором нужно не забывать, — это мониторинг. Потому что без мониторинга и достаточной наблюдаемости нагрузочное тестирование фактически проводится вслепую.
Мы что-то поделали, куда-то постреляли, а результатов получили не так уж много. Как раз мониторинг позволяет нам понять, какие события происходили в рамках нашей системы, на каких нагрузках мы получали те или иные события, ошибки и потери производительности. От них мы уже будем строить наши дальнейшие гипотезы и выстраивать решения.
Сейчас мы не говорим о конкретных метриках. На этом этапе важно другое: до начала нагрузочного тестирования необходимо обеспечить достаточную наблюдаемость системы.
Методология нагрузочного тестирования: этап 2. Работа с требованиями
Сбор требований является одним из самых важных и одновременно самым сложных этапов методологии.
Необходимо определить заказчиков системы. Это могут быть представители бизнеса, внутренние заказчики или другие заинтересованные стороны. Если требования еще не сформулированы, команде необходимо помочь в их подготовке.
Нужно определиться с SLR, создать, собрать SLI, индикаторы к ним, собрать целевые показатели индикаторов,а также понять ограничения. Целевая нагрузка или какие-то другие ограничения, которые потенциально могут быть в вашей системе. И все это фиксируется в паспорте НТ.

Почему это важно?
Потому что этот этап позволяет нам перестать разговаривать в формате «наша система должна быть быстрой, наша система должна быть без ошибок», а начать разговаривать в понятных терминах для всех.
Система должна отвечать в 95-м процентиле не более чем за 200 мс при нагрузке 100 запросов в секунду.
Это понятная метрика, к которой мы должны стремиться. Все понятно, все зафиксировано, все запомнили те требования, которые к системе предъявляются.Такие требования можно проверить, измерить и использовать при анализе результатов тестирования
Все собранные требования фиксируются в паспорте нагрузочного тестирования.
Требования определяют метрики
После того как требования сформированы, становится понятно, какие показатели необходимо контролировать во время тестирования. Как только мы понимаем наши требования, становится понятно, за какими показателями нужно следить.
Наиболее часто анализируются скорость и время ответа по 90, 95 и 99-м процентилям, уровень нагрузки, количество и процент ошибок. Смотрят на утилизацию CPU и RAM, и это важный момент, который нужно не забывать.
Менее частые показатели — это диск, сеть и различные специфические параметры с точки зрения JVM, очередей, базы данных. Тут уже команда сама смотрит на то, что ей нужно мониторить.
Итого, как только мы определились с требованиями, пора двигаться дальше и переходить к разработке методики.
Набор метрик определяется архитектурой системы и задачами конкретного нагрузочного тестирования.
Методология нагрузочного тестирования: этап 3. Разработка методики тестирования
Методика и методология звучат похоже, но в данном случае мы разделяем эти понятия. Методика определяет конкретную итерацию НТ. Она помогает нам понять, что мы будем делать именно сейчас, в текущий прогон. Она определяет цели, что мы хотим понять, виды тестов, которыми мы будем пользоваться, потому что их может быть больше, чем один.
Пока что мы пользуемся тестами на бою, но в дальнейшем мы, возможно, вырастем до состояния тестовых стендов для НТ и прочих вещей, но пока что это один вид тестов. В методике также фиксируются периодичность проведения тестов и профиль нагрузки.

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

Переходим к следующему важному артефакту, который нужен для хорошего НТ. Профиль нагрузки действительно сердце НТ. Это не просто какой-то набор ручек, он определяет методы, которые у нас используются в нашей системе, и их интенсивность.
Если профиль составлен неправильно или взят из головы, результат тестирования будет нерелевантным. Потому что именно благодаря ему мы эмулируем ту нагрузку, которую создают пользователи на нашу систему, и никакую другую.
Что обычно входит в профиль нагрузки? В первую очередь, высокоинтенсивные операции и тяжелые операции, которые могут выполняться относительно редко, но при этом существенно влиять на работу системы.
Например, если мы запускаем сложное вычисление, которое утилизирует 50% CPU, даже однократное выполнение такой операции важно учитывать при формировании профиля нагрузки: при длительном тестировании она может заметно влиять на общую производительность системы.
В профиль также могут входить прогнозируемые операции, которые пока не используются в боевом контуре. Например, если мы планируем вывести новую функциональность, имеет смысл заранее включить ее в профиль и проверить, как система будет вести себя при появлении новых операций и изменении структуры нагрузки.
Профиль нагрузки

Как собирается профиль?
Здесь все достаточно просто. В первую очередь мы берем данные мониторинга за определенный период, выбираем из него типичный день. Когда мы говорим «типичный день», не берем всплески, не берем какие-то аномальные состояния нашей системы, аномальное поведение пользователей.
Почему? Потому что оно не является стандартным для работы нашей системы. Мы берем именно стандартный, типичный день. Нам нужна статистика, распределение запросов по нашей системе, чтобы составить профиль и дальше уже нагружаться.
После того как мы поняли, какой день мы берем, мы обращаемся к команде эксплуатации, которая предоставляет статистику. В статистике будет рассказано о том, какие конкретно методы у нас вызывались, с какой частотой, количественно и в процентном соотношении. Это позволит нам в дальнейшем все посчитать. Эти данные становятся основой будущего профиля нагрузки.
Валидация методов
Далее валидируем эндпоинты. Это тоже важный момент, потому что потенциально у вас могут быть методы, которые невозможно протестировать.
Приведу банальный пример. Оплата заказа. Вряд ли кто-то будет выделять бюджет на покупку сотен тысяч пар кроссовок с оплатой картой. Это, наверное, будет слишком дорого для одной итерации НТ. Или какие-то другие методы, которые в целом не стоит вызывать.
Авторизации потенциально могут остаться за скобками. Какие-то внутренние сложные расчеты и прочие подобные вещи. Встречаются и менее очевидные ситуации.
Например, в одном из проектов было принято решение не выполнять финальное создание заказов, поскольку тестовые операции влияли на KPI смежных подразделений. Несмотря на последующую очистку данных, такие заказы учитывались в бизнес-метриках, поэтому команды договорились исключить этот этап из сценариев.
Такие вещи валидируем и убираем из профиля НТ. Это несколько снижает полноту профиля и релевантность тестирования, но в некоторых случаях такой компромисс неизбежен.
Методология нагрузочного тестирования: этап 4. От методов к пользовательским сценариям После формирования профиля нагрузки отдельные методы объединяются в операции. Операция представляет собой набор методов, которые вместе эмулируют конкретное действие пользователя.
Например, загрузка главной страницы может включать около 25 запросов к различным эндпоинтам: получение баннеров, изображений, данных для персонализации и других элементов страницы. Совокупность этих запросов и формирует одну операцию.
Из операций затем собираются пользовательские сценарии, которые воспроизводят последовательность действий пользователя в системе.
Таким образом, при реализации нагрузочного теста используется последовательная структура: сначала описываются отдельные методы, из них формируются операции, а затем операции объединяются в сценарии.
При этом важно учитывать и данные, с которыми выполняются методы и операции. Это могут быть параметры запросов, количество товаров на странице, значения для пагинации и другие данные, влияющие на поведение системы. Их также необходимо учитывать при подготовке сценариев, чтобы нагрузка максимально соответствовала реальному профилю использования системы.
Pacing и прогрев
Обсудим два важных поинта - это пейсинг и прогрев.
Пейсинг — это фиксированный шаг, время от начала одной итерации до начала следующей.
Зачем нужен? Затем, чтобы прогнозировать правильную интенсивность без различных хаотичных скачков. Приведем пример. Есть операция, в ней 10 эндпоинтов. Мы все знаем, что, к сожалению, наши системы работают не с точностью швейцарских часов. Сегодня, в один момент времени эндпойнты могут отработать за 50 миллисекунд, а в другой за 200. Если их 10, перемножим, получим, что в одном прогоне один раз операция выполняется всего за 500 миллисекунд, а в другой раз за 2 секунды.
Таким образом, если виртуальный пользователь как только закончил операцию, будет начинать сразу следующую, то в таком случае мы получим ситуацию, что они начинают хаотично прыгать. Нагрузка прыгает, и это не очень хорошо показывает релевантный тест.
Поэтому вводится пейсинг - фиксированный шаг. Если операция завершилась раньше заданного интервала, мы подождем и потом начнем следующую операцию.

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

Кроме того, необходимо учитывать внутренние механизмы системы, например кэширование, которым также требуется время для прогрева.
Все мы понимаем, как это все работает. Раз речь про сценарии и написание уже кода сценариев, поговорим чуть про инструмент.
Инструмент нагрузочного тестирования
Мы используем Gatling как некоторый стандарт в нашей компании. Почему?
Потому что в первую очередь, мы не хотим, чтобы каждая команда проводила свое внутреннее исследование, тратила кучу времени на то, что лучше, что хуже, почему ей нужно выбрать что-то такое или иное решение. Лучше принять фиксированный стандарт.

Вторая история. Мы можем обмениваться экспертизой. Это тоже отличная история. У нас есть один инструмент для всех. Мы можем спокойно уточнить у коллег, как они это делали, и дальше с ним жить.
Плюс Gatling покрывает все наши потребности, которые, по крайней мере, мы видим. Поэтому это единый стандарт в рамках методологии, которую мы предлагаем.
Методология нагрузочного тестирования: этап 5. Проведение нагрузочного тестирования
Самый сложный с точки зрения реализации этап — непосредственное проведение тестирования. Нам необходимо согласовать слот. Почему это важно?
Потому что есть куча дней, когда мы не можем проводить НТ: есть интеграционные истории, кто-то не может проводить тестирование, у кого-то что-то поломалось, кто-то не готов. Нужно понять, когда можно. Эксплуатация помогает понять, когда мы можем проводить тест.

Находим, например, слот ночью, запускаем тест, тестируем бой, наблюдаем, что все работает и получаем отчеты.
Здесь важно отметить: мы не чиним систему во время НТ, если вдруг она упала, не выйдя на целевую нагрузку. НТ — это не про ночные геройства, не про ночные переработки, не про какие-то вот эти вот истории.
Упал, что ж упал, получили тест, пришли утром, разобрались, починили, новый слот, новый запуск. Не нужно ночью пытаться разобраться.
Методология нагрузочного тестирования: этап 6. Интерпретация результатов
Собственно, следующий этап. Провели нагрузку, получили отчеты, с утра занимаемся интерпретацией результатов.
Получили все графики, отчеты, циферки, нужно на них посмотреть и понять: достигли ли целей?

Очень важно понять, где предел, сколько нагрузки система еще сможет выдержать сверх целевой. И нужно понять, что нужно делать дальше. Нужно ли что-то делать, или все хорошо? А у нас еще есть год в запасе спокойно, при текущем росте органической нагрузки. Или мы должны уже сегодня бежать и что-то чинить, потому что не выполняем цели.
Мы должны определить запас прочности, понять, сколько еще у нас есть ресурсов от целевой до точки отказа. А также должны понять, как быстро этот запас прочности сгорит.
Когда у нас есть уже какая-то ретроспективная история, можем посмотреть, как растет наша нагрузка, сколько прибавляем, условно, пользователей или RPS в месяц или в год. Понять, через сколько достигнем точки отказа: за неделю, за месяц, за год и так далее.
И составить то, что мы называли светофором, фактически привести эти два показателя в некоторый сигнал.

Если у нас запас прочности более 15%, а скорость сгорания более 6 месяцев, у нас все хорошо, сегодня можно не беспокоиться. Зеленый сигнал, спокойно живем.
Если запас прочности 15–5% и сгорим мы примерно за полгода, то уже пора как бы шевелиться, потому что полгода пройдут достаточно быстро.
Если же запас прочности уже 5% или меньше, или мы уже не выполняем цели, это значит, что пора бить тревогу и что-то делать, потому что как только придет целевая нагрузка, мы, вероятнее всего, упадем.
Итого
Хорошее нагрузочное тестирование заканчивается не графиками и не отчетом. Оно заканчивается пониманием того, выдерживает ли система целевую нагрузку, где начинается деградация, какой запас прочности остается и что необходимо сделать до того, как этот запас закончится.
Именно поэтому нагрузочное тестирование стоит рассматривать не как разовую проверку перед релизом, а как полноценный инженерный процесс с понятными этапами, критериями оценки и воспроизводимыми результатами.
KioskNews shows a cleaned-up reading view extracted from the publisher’s page — the original always lives on their site, not ours.