
Всем привет! Меня зовут Николай, я ведущий инженер по производительности в Т-Банке, более 15 лет работаю с различными утилитами для нагрузочного тестирования. Мы с командой выстраиваем процессы проведения тестов производительности.
Раньше наша команда помогала разрабатывать скрипты НТ и проводить анализ результатов их выполнения. Но поддерживать высокий уровень сервиса и постоянную доступность силами небольшой команды невозможно. Полтора года назад мы решили передать разработку скриптов в команды разработки продуктов.
Как и все молодые специалисты, команды начали из раза в раз допускать ошибки. Спустя 1,5 года я собрал наиболее популярные и хочу поделиться ими. Начинающим специалистам это поможет понять, как лучше выстраивать процесс, и значительно сократить время на разработку и внедрение НТ.
Ошибка 1. Неправильное место НТ в жизненном цикле
Мы давно привыкли к тому, что НТ двигают на более поздние этапы разработки. Множество статей и курсов учат нас, что нагрузка должна выполняться только после проведения функционального тестирования и не раньше.
Существует такая практика — Shift-Left Testing. Многие успешно применяют ее для функциональных тестов и полностью игнорируют для нагрузочных. Хотя ее также можно применять и внедрять.
До начала разработки скриптов уже можно озаботиться описанием методики нагрузочного тестирования, подготовить тестовые данные, необходимые заглушки для сервисов, составить профиль нагрузочного тестирования, настроить мониторинги и многое другое.
К сожалению, до сих пор ряд команд пытаются оттягивать этапы подготовки на более поздний период, а когда приходит время, сталкиваются с рядом проблем:
Недостаточно тестовых данных. Для функционального теста хватило бы и 10 записей в базе данных, но вот для нагрузочного их надо сотни или тысячи. В результате мы тратим время на ожидание того, пока все необходимые данные «прольют» через все системы.
Отсутствие необходимых заглушек, которые мы используем для эмуляции работы сторонних систем, когда нет возможности использовать их напрямую. Например, система еще не существует или это внешний сервис. В результате тратим время на их разработку.
Сложно правильно выстроить скрипт, потому что не определен профиль нагрузочного тестирования.
Нет понимания, что считать успешным тестом, потому что не поставили нефункциональные требования.
Эти сложности встречаются у команд наиболее часто. Есть и другие, но все они зависят от компании, команды и сервиса.

Всегда есть этапы, с которыми можно работать еще до наличия функциональности. И если определить их заранее, время на проведение НТ сократится в разы. Более того, сами скрипты можно уже начать писать, когда команда приступила к юнит-тестам или автотестам для определенной части фичи.
Когда функциональность уже будет полностью готова, останется дописать скрипты для последних API — и можно будет перейти к запуску. Не придется судорожно писать скрипты даже для тех методов, которые сделаны еще несколько недель назад. А возможно, при проведении тестов на ранних этапах определится недостаточная производительность и необходимо будет пересматривать архитектуру приложения или подходы к коммуникации внутри системы.
При соблюдении простых правил практики Shift-Left Testing фича будет выпускаться в срок и с очень хорошим качеством. Уменьшается риск затягивания сроков релиза, а значит, и риск дополнительных расходов.
Ошибка 2. Поверхностная методика нагрузочного тестирования
Когда мы откладываем НТ на потом, в результате часть пунктов стараемся пропустить. Один из таких пунктов — описание методики нагрузочного тестирования. Этот процесс мало кому нравится, так как заставляет вовлекать большое количество людей из разных областей для корректного составления документа. Зачастую под конец цикла разработки принимают решение его пропустить и не описывать. Быстро составляют скрипт, проводят нагрузку и отдают в прод.
Но, когда начинаются проблемы в проде, пытаются определить, почему и что пошло не так, и, как правило, смотря на скрипт НТ, не понимают, почему он именно такой и ничего общего не имеет с реальными вызовами, которые делаются пользователями при работе с системой.
Мы с командой проанализировали свой опыт и собрали список того, что должно быть в методике:
Описание самой системы: что это и зачем она нужна.
Архитектура системы, чтобы понимать зависимости модулей и что именно будем нагружать.
Какой профиль нагрузочного тестирования использовать. Какие методы будут подвергнуты нагрузке и в каком объеме.
Какие тестовые данные использовать и в каком объеме.
Какие типы тестов применить при проверке работоспособности системы.
Какие нефункциональные требования по работоспособности системы надо установить. Это могут быть времена ответов на запросы, количество допустимых ошибок, уровень потребления памяти и процессора и другие, которые вы посчитаете нужным указать.
Какие метрики снимать и на какие мониторинги смотреть для принятия решения о результатах тестирования.
Информация о заглушках. Какие будут использованы и их характеристики.
Список лиц, вовлеченных в разработку: разработчики, тестировщики, аналитики, продакт-менеджеры. Это позволит быстрее найти контактное лицо для решения той или иной проблемы.
Методика — это не просто документ для галочки. Он должен быть составлен и согласован со всеми, кто примет в этом участие. К сожалению, один человек не в состоянии определить все необходимые параметры и принять решение в одиночку — это командная работа.

Часть пунктов из документа будет закрываться автоматически при хорошо организованном цикле разработки программного обеспечения в компании.
Например, описание, архитектура, заинтересованные лица будут вам уже известны, если вы будете подключаться на самых ранних стадиях планирования. Вы сможете намного раньше указывать на нюансы, которые могут иметь место в архитектуре приложения. И как следствие, стоимость исправления ошибки будет ниже, а также часть пунктов методики будет заполнена.
Ошибка 3. Недостаточность тестовых данных
Сейчас во многих системах для обеспечения надежности используют сложные системы авторизации, которые не позволяют одним и тем же пользователям заходить под разными сессиями. Для функциональных тестов, как правило, не требуется большого количества тестовых пользователей, и их создают ограниченное количество, которого недостаточно для нагрузочного тестирования.
Например, можно сделать 5—10 пользователей, и они покроют весь необходимый набор для ручных тестов. Другая ситуация складывается, когда команда делает НТ и они хотят получить сотни, а то и тысячи уникальных пользователей во время НТ.
Когда дело доходит до разработки скрипта, приходит осознание, что у нас всего несколько пользователей. В результате бегаем по командам и ищем решение. А можно подумать об этом заранее, изучить опыт других команд и заложить в код системы на этапе тестирования необходимое поведение. Одним из возможных вариантов может быть допуск использования разных сессий для одного и того же пользователя. Но все это должно быть актуально только для тестовой системы и не присутствовать в прод-среде.
Другой пример. Когда разработали тест, подумали над тестовыми данными и создали необходимое количество пользователей. Но не учли, что система будет развиваться и пользоваться популярностью. В результате пришел запрос на проведение теста с 40 000 уникальных пользователей, а в тестовой системе было только 3 000. И как показала практика, чтобы создать еще 37 000 пользователей, потребовалось две недели. А это простой в разработке и срыв сроков релиза.
Тестовые данные всегда должны быть в достаточном количестве с возможным расширением на будущее и релевантные пользовательским. В идеале здорово иметь какие-то скрипты, которые сгенерируют необходимый объем в течение нескольких часов.
Можно вовсе использовать только динамические данные. При этом они должны быть репрезентативными, то есть подходить по формату и хотя бы по смыслу. Мы в Т-Банке разработали несколько утилит для разных подсистем, которые могут генерировать нам имя, фамилию, отчество клиента, ИНН, номер телефона, дату рождения и другой набор данных.
У нас есть четкое понимание, что сгенерированные данные — это полностью сопоставимые с реальными значения, но в то же время это тестовые данные. Важным фактом во время проведения нагрузочных тестов остается и то, что используемые в тестах данные должны соответствовать объему, качеству, консистентности и другим параметрам продовых данных.
Иногда, стремясь сократить себе работу, мы можем в качестве ФИО сформировать значение из каких-то трех букв, но при генерации нескольких тысяч таких пользователей мы получим объем базы данных значительно меньше продовой. Если по количеству записей в базе у нас будет полный порядок, сам объем базы будет гораздо меньше, а значит, и скорость операций с базой данных не будет соответствовать скорости работы продовой базы.
За тестовыми данными нужно тщательно следить и периодически сверять их с продовыми не только по количеству записей, но и по используемому объему. Если на проде данные были обогащены, например, новыми столбцами, и с тестовой базой надо сделать то же самое. Если в реальной базе данные удалили, тестовая должна быть модифицирована соответствующим образом.
Ошибка 4. Неправильно организованное окружение для выполнения тестов
Многие команды считают, что допустимо как можно ближе располагать генератор нагрузки и тестовую систему. Например, использовать один дата-центр, чтобы компьютеры физически находились в одном здании, что позволит сократить сетевые задержки до минимума. Потом на подобном окружении выполнить скрипт и интерпретировать его результаты как возможности по выдерживанию данной нагрузки системы в проде. К сожалению, такую экстраполяцию результатов лучше не выполнять.
Сейчас для доступа к публичным системам используются не только стационарные компьютеры с высокоскоростным подключением, но и мобильные устройства, у которых скорость подключения в дальних уголках нашей страны может быть медленной и нестабильной. Кроме того, есть еще те, кто находится в разных уголках нашей большой страны, в путешествиях и может получать доступ за пределами основного региона расположения инфраструктуры. Получается, что при медленном подключении запросы выполняются в разы дольше, а подключение дольше остается активным и нагрузка выше.
Тесты, выполненные в тестовой среде, валидные, но не окончательные. Идеальным вариантом было бы выполнение какого-то финального теста на окружении, схожем с продом. Это могут быть как сами серверы, где располагается система, так и приложения, которые получают к ней доступ.
Можно задействовать облачную инфраструктуру и использовать генераторы нагрузки из разных областей страны или вовсе клиентов по всему миру (сейчас очень много возможностей аренды облачных мощностей). В некоторых утилитах при выполнении тестирования производительности есть вариант выставить пропускную способность на группу пользователей, агента или сценарий.
Использование агентов с разной скоростью подключения к системе даст полноту картины по работе системы в боевых условиях — при ее использовании в разных точках нашей планеты и с устройств, которые не обладают высокой скоростью. Объем обрабатываемой информации напрямую зависит от пропускной способности канала соединения между клиентом и системой. Но если такой возможности нет, выяснение пределов работы приложения — хорошая практика.
Ошибка 5. Плохая эмуляция работы сервисов
При текущих темпах развития облачных систем и различных утилит, которые ими управляют, многие команды привыкли к тому, что можно развернуть полное тестовое окружение, куда будет входить как тестовая система, так и все ее зависимости. Сделать это можно в пару кликов, хотя это дорогостоящая операция, особенно когда у вас десятки и сотни зависимых систем.
Разворачивание тестовой системы со всеми нужными данными может затянуться на несколько часов. Может не хватить времени, когда необходимо выполнить тест, получить его результаты и провести анализ в кратчайшие сроки. В таком случае идеальным вариантом будет использование заглушек для сторонних систем.
Как правило, заглушки легче и разворачиваются намного быстрее, чем сама система. Мы можем использовать только 10% функциональности всей системы. И тогда проще и дешевле задействовать эмулятор, а не полностью разворачивать всю систему.
Команды до сих пор обращаются за помощью в вопросе разработки заглушек: что заглушать, какие утилиты использовать, как лучше их описывать. Мы специально внутри компании написали статью, где указаны требования, которым должна удовлетворять заглушка.
Хорошая заглушка должна:
Эмулировать динамические задержки. Зависимая система может быть также под нагрузкой, и ответ от нее при одинаковом запросе может быть разной длительности. Нельзя полностью полагаться на то, что время ответа от сторонней системы будет статическое.
Тело ответа всегда должно быть динамическим: это позволит избежать использования кэшей на уровне как системы виртуализации, так и генератора нагрузки.
Хорошо, если утилита для разворачивания заглушек может эмулировать скорость работы сети, то есть не всегда пересылать трафик со скоростью 1Gb/s, но и давать возможность использовать скорости, которые актуальны как для EDGE, так и для 5G-сетей.
Ваша заглушка должна уметь периодически отвечать с кодом ответа, которого вы не ожидаете, на один и тот же пакет данных и с произвольной вероятностью.
Хорошо спроектированная и реализованная заглушка даст возможность видеть работу системы аналогично проду. А значит, качество проведенного тестирования будет выше и лучше прогнозы поведения системы с реальными интеграциями.
Ошибка 6. Стандартный мониторинг не покрывает НТ
Иногда команды поздно вспоминают о том, что тестируемая система, генераторы нагрузки, зависимые системы, инфраструктура и другие сущности во время теста должны быть покрыты мониторингами. А именно тогда, когда стандартный отчет утилиты не показывает необходимые результаты для проведения полноценного анализа.
Начинается поиск узкого места и выясняется, что мы мониторили только CPU, RAM и еще несколько базовых метрик только у основной системы. И полностью забыли об остальных, которые задействованы в работе.
Однажды на одном из проектов во время нагрузочного тестирования мы выявили, что на систему подавалась нагрузка, которая не превышала 500 RPS. При этом анализ потребления ресурсов тестовой системой не выявил значительных деградаций, да и потребление ресурсов не превышало 15%.
Анализ и добавление мониторингов на большее количество систем выявило проблему. Она заключалась именно в генераторе нагрузки. По какой-то причине во время теста на генераторе нагрузки было задействовано всего 1 ядро из 8 доступных. Фактически мы уперлись в ресурсы самого генератора, а не тестируемой системы.
Обновление утилиты генератора нагрузки устранило данный баг, и процессор уже использовался на полную мощность, что позволило получить 2000 RPS с одного генератора.

Как показывает практика, иногда избыточное количество метрик приводит к тому, что приходится тратить чуть больше времени на их анализ. Но это позволяет не только подтверждать, что наш тест соответствует нефункциональным требованиям к системе, но и дает шанс на выполнение большого количества экспериментов.
Например, можно проводить тесты по выяснению, на каком количестве пользователей у нас закончится доступный пул соединений к базе данных. Или при каком объеме данных Kafka перестанет справляться обработкой поступающей информации при допустимых параметрах. Или когда у нас полностью забьется сеть и многое другое.
Стандартных метрик может быть недостаточно, что будет приводить выявление деградации в долгосрочной перспективе к более длительному анализу результатов и необходимости многократно повторять один и тот же тест. Наилучший вариант — добавить специфические метрики для всех систем, которые участвуют в процессе работы, а не только метрики по использованию процессора, памяти, системы хранениях данных и использования сети.
В интернете можно найти множество примеров методов по использованию метрик, таких как RED и USE. И очень много информации по специфическим метрикам по Kafka, AMQP, базам данных и другим системам. Использование дашбордов с данными по необходимым метрикам делает анализ быстрее и качественнее.
Ошибка 7. НТ не встроено в CI/CD
Мы давно привыкли к тому, что unit-, системные и интеграционные тесты должны быть встроены в процесс CI/CD и выполняться на постоянной основе. Какие-то — на каждый коммит, какие-то — на pull/merge request. А к нагрузочным тестам относятся как к smoke- или e2e-тестам. То есть те, что выполняются 1—2 раза перед самым релизом. Мы в Т-Банке считаем, что такой подход не верный и даже тесты производительности необходимо включать в процесс CI/CD.
Моя коллега в статье делилась тем путем, который мы выбрали:
Расскажу об одном из проектов, когда подобный тест выполнялся только перед релизом. Это было около 10 лет назад для обычного приложения на Windows. Тогда релизы делались не так часто и период выпуска новой версии составлял от 4 до 6 месяцев.
При очередной проверке релиза выяснилось, что тест, который работал 5 месяцев назад, сейчас не отработал и выдал ошибку. Это был блокер для релиза. Так как между прогонами прошло 5 месяцев и была написана не одна тысяча строк, с ходу понять причину возникновения проблемы не удалось. Длительность теста составляла 12 часов, только по истечении этого времени проявлялся баг. В результате пришлось делением пополам искать сборку, где проблема проявилась.
Деление пополам — когда берем какую-то сборку из середины, проверяем наличие бага и в зависимости от его наличия переходим к делению справа или слева. А за 5 месяцев сборки собирались по 3—6 билдов ежедневно. На поиск билда, где проблема появилась, мы потратили около двух недель. Потом еще неделя на исправление и повторный релиз. Задержка релиза составила около трех недель. Представляете, сколько финансовых потерь можно получить сейчас при таком увеличении сроков.

В Т-Банке мы строго придерживаемся правила включать НТ в процесс CI/CD. Именно по этой причине мы не полагаемся на оригинальные отчеты от генератора нагрузки. Мы создали свою систему для хранения, которая позволяет не только видеть результат выполнения, но и отслеживать тренды запусков. Мы можем выполнять сравнения между любыми запусками до тех пор, пока результаты хранятся. Более того, наша система самостоятельно проводит сравнительный анализ текущего результата с эталонным.
Наряду с другими типами тестирования необходимо включать в систему CI/CD и нагрузочное тестирование.
Ошибка 8. Неподходящая утилита для НТ
Чаще всего мы делаем выбор утилиты для нагрузочного тестирования, опираясь не только на ее функциональные возможности, но и на количество людей в комьюнити, прочитав отзывы в интернете, по совету коллег. Данные варианты не всегда правильные и в последующем могут привести к неожиданным последствиям.
Несколько лет назад на одном из проектов нам предоставили полную свободу действий в выборе утилиты. В то время мы, как и многие, решили поискать ее среди популярных и с большим сообществом. Спустя несколько дней поисков поняли, что надо более подробно изучить то приложение, на которое будет подаваться трафик.
В своем поиске мы определили ряд параметров:
поддерживаемые протоколы;
наличие и возможность создавать плагины;
встроенная система отчетности;
удобство работы с учетом того, что проект надо будет передавать на поддержку другим людям, которые незнакомы с НТ;
системные требования к генератору;
используемый язык программирования (если необходим);
нюансы работы (потребление RAM/CPU, необходимость специальной платформы для запуска и др.).
По параметрам мы выбрали несколько утилит — как бесплатных, так и платных — и решили, что необходимо сделать какой-то набор предполагаемых тестов. То есть проанализировали приложение и определили критически важные аспекты его работы:
используемые типы запросов;
объем данных при отправке запроса;
объем данных при получении ответа.
Мы создали сервис с запросами, которые были сопоставимы с реальными (тогда еще приложение не было готово к тестам). Разработали в каждой из утилит тестовый сценарий и прогнали их с требуемым RPS и длительностью теста.
В результате получили отчеты от каждой утилиты и сравнили их между собой. Кроме того, во время теста выполняли мониторинг самого генератора нагрузки: какие ресурсы системы и в каком объеме он потреблял во время длительного теста.
Только после проведения ряда испытаний сделали выбор утилиты, которая наилучшим образом подходила для того проекта по необходимым параметрам и предоставляемой отчетности. С учетом того, что после нас кто-то другой будет следить за актуальностью сценария и проводить его обновление.
Иногда приходится делать выбор не только в пользу одной утилиты. Например, в Т-Банке мы используем две: Gatling и K6. Это позволяет закрыть 95% нужд наших команд по разработке скриптов нагрузочных тестов. Данные утилиты можно быстро расширять необходимой для нас функциональностью через разработку новых плагинов.
Не всегда следует слепо полагаться на то, что выбирает большинство, и там, где сообщество большое. Да, они могут вам помочь при необходимости. Но надо понимать, для каких целей и какой системы нужна утилита и насколько она будет полезна в долгосрочной перспективе.
Заключение
Описанный список ошибок — это не какой-то эталон, который есть у всех и должен исправляться всегда и везде. Я подсветил те ошибки, с которыми столкнулся наш отдел. С подобными же ошибками к нам приходят команды за помощью и сейчас, когда начинают свой путь в тестировании производительности.
Если мы хотим, чтобы наши системы были производительны, покоряли вершины и выпускались ровно в срок, без задержек двигаясь к светлому будущему, необходимо быть проактивными, искать в своих процессах узкие места и устранять их.
Мы с радостью помогаем командам избежать тех ошибок, которые были освещены. И всегда приятно слышать слова благодарности, когда проект зарелизили в срок и его качество соответствует всем требованиям.
Если у вас есть свои истории или ошибки, которые я не описал, — добро пожаловать в комментарии!
Uo_Asakura
Отличная статья!
Скажите пожалуйста, под какие нужды вы используете K6.
Потому что как было правильно подмечено, инструментов много, у каждого из них своё по количеству и направлению сообщество.
Но что касается K6, какие запросы бизнеса им лучше всего закрывать по вашему мнению?
nmirontsev Автор
Добрый день. Как я уже писал, в нашей компании команды пишут скрипты самостоятельно. Мы используем несколько утилит для обеспечения потребностей в различных языках программирования. Например, K6 предпочитают те команды разработчиков, которые работают с JS или TS, так как это для них удобнее.
Ещё одно значительное преимущество данной утилиты — её легковесность. В отличие от Gatling или Apache, для работы с K6 не требуется установленной JDK/JRE, а также меньше кэши нужных библиотек. Благодаря этому K6 потребляет меньше системных ресурсов.