Всем привет, я Никита Мунтян, QA Load Engineer в inDriver. Пришел в компанию в 2020 году развивать направление нагрузочного тестирования. В первую очередь познакомился с коллегами, выстроил план работы и начал планомерно создавать процессы и технические решения.
Тогда передо мной было пустое поле — делай как хочешь и как видишь, возможностей для творческой реализации много. Оставалось понять, что хотят увидеть от нагрузки, и какой подход использовать. Наша команда решила делать Load as a Service, об этом и расскажу под катом.
Начало
Все началось со встреч о том, что коллеги хотят увидеть от нагрузочного тестирования. В рамках этого этапа мы:
Сконфигурировали тестовый стенд.
Создали регулярные встречи с заинтересованными лицами.
Пришли к выводу, что хотим оптимизировать систему и создать доступный и несложный инструмент, легко поддерживаемый и многофункциональный.
Также мы ввели понятие исследовательского тестирования сервисов, которое использовали как для оценки целесообразности внедрения стороннего софта, так и для разработки собственных решений. Оно применяется для оценки работоспособности сервиса, выделения самых долгих методов и тестирования новых микросервисов, которые активно переезжают с монолита.
Кроме того, мы предложили вариант гибких запусков тестирования: изменения критически важных методов проходят нагрузочное тестирование по запросу от команд, которые уже переехали или собираются переезжать. Периодически мы снимаем профиль и разбираем проблемные места.
В итоге было решено пойти по пути развития нагрузочного тестирования как автоматизированного инструмента для любого заинтересованного лица. Модное-молодежное Load as a Service. Тестировщик или разработчик сможет запускать тесты от команды — красота же! Наша задача, как команды нагрузочного тестирования, предоставить такой сервис.
Для начала мы собрали профили нагрузки, определили критически важные модули и попытались ввести регламенты: при изменении каких методов требуется нагрузочное тестирование. А после этого сделали регламент заведения задач на нагрузочное тестирование.
Вот как выглядит наш шаблон для заведения задачи на нагрузочное тестирование:
Зачем мы хотим нагрузочное тестирование на конкретном проекте.
Название системы и четкое описание работы.
Предполагаемые узкие места.
Ожидаемая нагрузка на систему: какие запросы должны выполняться их RPS. RPS должны быть стандартными и пиковыми (если пока непонятно, можно написать предположительную нагрузку).
Верхнеуровневая архитектура приложения (по возможности).
Описание по шагам, что должен сделать сценарий.
Кто от команды будет заниматься нагрузкой со стороны разработки.
Прикрепить задачу на подготовку стенда и настройке конфигов (от команды).
Также необходимы описания:
Интерфейса, который необходимо нагрузить.
Зависимостей.
Какую загрузку ресурсов мы ожидаем при плановых и пиковых нагрузках.
Минимального периода тестирования (если это тестирование на стабильность).
Основной флоу работы теперь выглядел так:
1. Команда нагрузочного тестирования разрабатывает сценарии, поддерживает стенды, готовит профиль нагрузки и передает знания в команды, выделяя при этом ответственного внутри самой команды.
Например, мы просим техлида команды выделить человека, которого быстро обучат валидировать и обновлять сценарий JMeter. Запуски мы производим через TeamCity.
2. Создается job, который можно вызвать нажатием кнопки. Целью команды нагрузочного тестирования является создание центра экспертизы и помощь в разработке сценариев для команд.
CI-интеграция
Все сценарии хранятся в нашем GitHub-репозитории. Оттуда с помощью Dockerfile и Charts создается loadtests-base-image-контейнер с JMeter в Kubernetes и нужным набором плагинов. А также loadtests-jmeter-job, который использует базовый image для старта job'ы в TeamCity. К сожалению, ямлики с charts и teamplates показать не могу, но эти шаблоны спокойно гуглятся для TeamCity.
Рецепт Dockerfile:
FROM openjdk:8-jdk-slim
#Переменные прокинутые в JMeter
ARG JMETER_VERSION=5.4.2
ENV JMETER_COUNTER='0'
ENV JMETER_SCENARIO=
ENV JMETER_THREADS='50'
ENV JMETER_TIMELINE='10'
ENV JMETER_DELAY='1000'
ENV JMETER_CONTRACTOR='50'
ENV JMETER_CUSTOMER='50'
ENV JVM_ARGS='-Xms16384m -Xmx16384m'
WORKDIR /jmeter
ADD ./scenario/. /jmeter/
Install Jmeter
RUN apt-get clean && apt-get update && apt-get -qy install wget telnet iputils-ping unzip
RUN wget https://archive.apache.org/dist/jmeter/binaries/apache-jmeter-JMETER_VERSION.tgz -C ./ && rm apache-jmeter-JMETER_VERSION/* ./
Install PluginsManager
RUN wget -q -O /tmp/jpgc-casutg-2.9.zip https://jmeter-plugins.org/files/packages/jpgc-casutg-2.9.zip
&& unzip -n /tmp/jpgc-casutg-2.9.zip && rm /tmp/jpgc-casutg-2.9.zip
&& wget -q -O lib/cmdrunner-2.2.jar http://search.maven.org/remotecontent?filepath=kg/apc/cmdrunner/2.2/cmdrunner-2.2.jar
&& java -cp lib/ext/jmeter-plugins-manager-1.3.jar org.jmeterplugins.repository.PluginManagerCMDInstaller
&& bin/PluginsManagerCMD.sh install jpgc-graphs-basic=2.0,jpgc-autostop=0.1,jpgc-graphs-additional=2.0,blazemeter-debugger=0.6,jpgc-functions=2.1,jpgc-graphs-dist=2.0,jpgc-dummy=0.4,jpgc-ffw=2.0,jmeter-grpc-request=1.2.1,jpgc-json=2.7,jpgc-graphs-vs=2.0,jpgc-perfmon=2.1,jpgc-plugins-manager=1.6,bzm-random-csv=0.7,jpgc-redis=0.3,jpgc-tst=2.5,websocket-sampler=1.0.2-SNAPSHOT,websocket-samplers=1.2.8,jmeter-core=5.4.2,jmeter-ftp=5.4.2,jmeter-http=5.4.2,jmeter-jdbc=5.4.2,jmeter-jms=5.4.2,jmeter-junit=5.4.2,jmeter-java=5.4.2,jmeter-ldap=5.4.2,jmeter-mail=5.4.2,jmeter-mongodb=5.4.2,jmeter-native=5.4.2,jmeter-tcp=5.4.2,jmeter-components=5.4.2
RUN bin/PluginsManagerCMD.sh status
ENV PATH /jmeter/bin:" class="formula inline">PATH
COPY entrypoint.sh /
RUN ["chmod", "+x", "/entrypoint.sh"]
ENTRYPOINT ["/entrypoint.sh"]
Вид job, которым стартуют нагрузочные тесты:
Также мы параметризировали количество пользователей и время исполнения самого сценария, используя кастомные threadGroup. Из CI-системы можно было задать параметр, который передавался в JMeter и использовался там, где нужно:
Разработка сценариев
Решено было идти по пути наименьшего сопротивления — инструментом выбрали Apache JMeter. Мы же хотим обучать всех желающих :)
Причины выбора:
Низкий порог входа = актуализация сценариев внутри команды.
Доступность инструмента, огромное количество документации в интернете.
Легкая интеграция в CI.
У нас мобильное приложение, поэтому мы пошли по пути «эмулятор + сниффер для записи сценариев».
Расскажу, как мы создаем сценарии:
1. Открываем Android Studio. У меня их 2 — для записи в один файл действий водителя и пассажира (записать одновременно в оба сертификат не даст, нужно поочередно):
2. Открываем Proxyman. Идем во вкладку Certificate → Install Certificate on Android → Emulators:
Нажимаем на Override Emulator и записываем сертификат на оба устройства:
4. Если все успешно, мы увидим трафик с Remote Devices:
5. Работа с Android Studio + Proxyman:
Устанавливаем последнюю свежую или нужную сборку, и берем 2 устройства:
Возьмем водителя и пассажира, и увидем их запросы в сниффере. Выделяем нужные нам запросы и сохраняем в har file:
6. Используем конвектор har to jmx.
7. Конвертируем файл в jmx и получаем шаблон для работы в JMeter.
Итог
Подключили Grafana и смотрим там основные результаты (RPS, Responce Time и другие показатели).
Прикрутили дашборды на связке Influx + Grafana, подняли сервер, куда складываются стандартные html-отчеты JMeter.
Разместили стенд в Kubernetes, новые микросервисы, которые заходят в эксплуатацию, тестируются перед деплоем. Старые тестируются по запросу от команды при изменениях в определенных методах. Это необходимо для понимания, что будет с инстансом, а также для выявления узких мест. На тестовом стенде все интеграции прямые, с минимумом заглушек.
Собираем системные метрики с помощью Prometheus и Zabbix.
Проводим исследовательские тестирования производительности и идем к полной автоматизации тестов перед деплоем ветки релиз-кандидата в прод.
В конце концов, встал вопрос о том, как сделать процессы более зрелыми. Но об этом я расскажу в следующей статье. Она будет посвящена тому, как мы меняли подходы к тестированию и строили заборы с пайплайнами.