Нагрузочное тестирование как CI-сервис для разработчиков
Одна из проблем, с которыми часто сталкиваются мультипродуктовые вендоры ПО, это дублирование компетенций инженеров — разработчиков, тестировщиков и администраторов инфраструктуры — почти в каждой команде. Это касается и дорогостоящих инженеров — специалистов в области нагрузочного тестирования.
Вместо того, чтобы заниматься своими прямыми обязанностями и использовать свой уникальный опыт для выстраивания процесса нагрузочного тестирования, выбирать методологию, оптимальные значения метрик и писать автотесты в соответствии с профилями нагрузки, инженерам зачастую приходится с нуля разворачивать тестовую инфраструктуру, настраивать инструменты нагрузки, самим встраивать их в CI-системы, настраивать мониторинг и публикацию отчетов.
Решения некоторых организационных проблем в тестировании, которые мы применяем в Positive Technologies, вы можете найти в другой статье. А в этой я расскажу про возможность интеграции нагрузочных тестов в общий CI-конвейер с помощью концепции «нагрузочное тестирование как сервис» (load testing as a service). Вы узнаете, как и какие докер-образы источников нагрузки можно использовать в CI-конвейере; как подключить источники нагрузки в свой CI-проект при помощи сборочного шаблона; как выглядит демопайплайн для запуска нагрузочных тестов и публикации результатов. Статья может быть полезной инженерам по тестированию ПО и инженерам-автоматизаторам в CI, кто задумался об архитектуре своей нагрузочной системы.
Суть концепции
Концепция load testing as a service подразумевает возможность интегрировать инструменты нагрузки Apache JMeter, Yandex.Tank и собственные фреймворки в произвольную систему continuous integration. Демопример будет для GitLab CI, но принципы изложены общие для всех CI-систем.
Load testing as a service — это централизованный сервис для проведения нагрузочного тестирования. Нагрузочные тесты запускаются в выделенных пулах агентов, публикация результатов происходит автоматически в GitLab Pages, Influx DB и Grafana или в системы тест-репортинга (TestRail, ReportPortal и т. п.). Автоматизация и масштабирование реализуются максимально просто — через добавление и параметризацию в проекте GitLab CI обычного шаблона gitlab-ci.yml.
Преимущество подхода заключается в том, что вся CI-инфраструктура, нагрузочные агенты, докер-образы источников нагрузки, пайплайны тестирования и публикация отчетов — поддерживаются силами централизованного отдела автоматизации (DevOps-инженерами), а инженеры по нагрузочному тестированию могут сосредоточить свои усилия на разработке тестов и анализе их результатов, не занимаясь инфраструктурными вопросами.
Для простоты описания будем считать, что целевое тестируемое приложение или сервер уже развернуты и настроены заранее (для этого могут быть использованы автоматизированные сценарии на Python, SaltStack, Ansible и др.). Тогда вся концепция нагрузочного тестирования как сервиса укладывается в три этапа: подготовка, тестирование, публикация отчетов. Подробнее на схеме (все картинки кликабельны):
Основные понятия и определения в нагрузочном тестировании
При проведении нагрузочных испытаний мы стараемся придерживаться стандартов и методологии ISTQB, используем соответствующую терминологию и рекомендуемые метрики. Приведу краткий перечень основных понятий и определений в нагрузочном тестировании.
Нагрузочный агент (load agent) — виртуальная машина, на которой будет запущено приложение — источник нагрузки (Apache JMeter, Yandex.Tank или самописный нагрузочный модуль).
Цель тестирования (target) — сервер или приложение, установленное на сервере, которое будет подвергаться нагрузке.
Тестовый сценарий (test case) — набор параметризованных шагов: действия пользователей и ожидаемые реакции на эти действия, с зафиксированными сетевыми запросами и ответами, в зависимости от заданных параметров.
Профиль или план нагрузки (profile) — в методологии ISTQB (п. 4.2.4, стр. 43) профили нагрузки определяют критически важные для конкретного теста метрики и варианты изменения параметров нагрузки в течение теста. Примеры профилей вы можете видеть на рисунке.
Тест (test) — сценарий с заранее определенным набором параметров.
Тест-план (test-plan) — набор тестов и профиль нагрузки.
Тестран (testrun) — одна итерация запуска одного теста с полностью выполненным сценарием нагрузки и полученным отчетом.
Сетевой запрос (request) — HTTP-запрос, отправляемый от агента к цели.
Сетевой ответ (response) — HTTP-ответ, отправляемый от цели к агенту.
HTTP-код ответа (HTTP responses status) — стандартный код ответа от сервера приложений.
Транзакция (transaction) — полный цикл «запрос — ответ». Транзакция считается от начала отправки запроса (request) до завершения приема ответа (response).
Статус транзакции (transactions status) — удалось ли успешно завершить цикл «запрос – ответ». Если в этом цикле была любая ошибка, то вся транзакция считается неуспешной.
Время отклика (latency) — время от окончания отправки запроса (request) до начала приема ответа (response).
Метрики нагрузки (metrics) — определяемые в процессе нагрузочного тестирования характеристики нагружаемого сервиса и нагрузочного агента.
Основные метрики для измерения параметров нагрузки
Некоторые наиболее общеупотребительные и рекомендуемые в методологии ISTQB (стр. 36, 52) метрики приведены в таблице ниже. Схожие метрики для агента и цели указаны в одной строке.
Метрики для нагрузочного агента
Метрики целевой системы или приложения, тестируемых под нагрузкой
Количество vCPU и памяти RAM, Disk — «железные» характеристики нагрузочного агента CPU, Memory, Disk usage — динамика загрузки процессора, памяти и диска
в процессе тестирования. Обычно измеряется в процентах от
максимально доступных значений
Network throughput (on load agent) — пропускная способность
сетевого интерфейса на сервере,
где установлен нагрузочный агент.
Обычно измеряется в байтах в секунду (bps) Network throughput(on target) — пропускная способность сетевого интерфейса
на целевом сервере. Обычно измеряется в байтах в секунду (bps)
Virtual users— количество виртуальных пользователей,
реализующих сценарии нагрузки и
имитирующих реальные действия пользователей Virtual users status, Passed/Failed/Total — количество успешных и
неуспешных статусов работы виртуальных пользователей
для сценариев нагрузки, а также их общее количество.
Обычно ожидается, что все пользователи смогли выполнить
все свои задачи, указанные в профиле нагрузки.
Любая ошибка будет означать, что и реальный пользователь не сможет
решить свою задачу при работе с системой
Requests per second (minute)— количество сетевых запросов в секунду (или минуту).
Важная характеристика нагрузочного агента: сколько он может генерировать запросов.
Фактически это имитация обращения к приложению виртуальными пользователями Responses per second (minute)
— количество сетевых ответов в секунду (или минуту).
Важная характеристика целевого сервиса: сколько удалось
сгенерировать и отправить ответов на запросы с
нагрузочного агента
HTTP responses status— количество различных кодов ответа
от сервера приложения, полученных нагрузочным агентом.
Например, 200 OK означает успешное обращение,
а 404 — что ресурс не обнаружен
Latency (время отклика) — время от окончания
отправки запроса (request) до начала приема ответа (response).
Обычно измеряется в миллисекундах (ms)
Transaction response time— время одной полной транзакции,
завершение цикла «запрос — ответ».
Это время от начала отправки запроса (request)
до завершения приема ответа (response).
Время транзакции может измеряться в секундах (или минутах)
несколькими способами: считают минимальное,
максимальное, среднее и, например, 90-й перцентиль.
Минимальные и максимальные показания — это крайние
состояния производительности системы.
Девяностый перцентиль используется наиболее часто,
поскольку он показывает большинство пользователей,
комфортно работающих на пороге производительности системы
Transactions per second (minute) — количество полных
транзакций в секунду (минуту),
то есть сколько приложение смогло принять и
обработать запросов и выдать ответов.
Фактически это пропускная способность системы
Transactions status , Passed / Failed / Total — количество
успешных, неуспешных и общее количество транзакций.
Для реальных пользователей неуспешная
транзакция фактически будет означать
невозможность работать с системой под нагрузкой
Принципиальная схема нагрузочного тестирования
Принципиальная схема нагрузочного тестирования очень проста и состоит из трех основных этапов, о которых я уже упоминал: Prepare — Test — Report, то есть подготовка целей тестирования и задание параметров для источников нагрузки, затем выполнение нагрузочных тестов и, в конце, формирование и публикация отчета о тестировании.
Примечания к схеме:
QA.Tester — эксперт по нагрузочному тестированию,
Target — целевое приложение, для которого нужно узнать его поведение под нагрузкой.
Классификатор сущностей, этапов и шагов на схеме
Этапы и шаги
Что происходит
Что на входе
Что на выходе
Prepare: этап подготовки к тестированию
LoadParameters
Задание и инициализация
пользователем
параметров нагрузки,
выбор метрик и
подготовка тест-плана
(профиля нагрузки)
Пользовательские параметры для
инициализации агента нагрузки
Тест-план
Цель тестирования
VM
Развертывание в облаке
виртуальной машины с
требуемыми характеристиками
Параметры ВМ для агента нагрузки
Скрипты автоматизации для
создания ВМ
Настроенная ВМ в
облаке
Env
Настройка ОС и подготовка
окружения для
работы нагрузочного агента
Параметры окружения для
агента нагрузки
Скрипты автоматизации для
настройки окружения
Подготовленное окружение:
ОС, сервисы и приложения,
необходимые для работы
агента нагрузки
LoadAgents
Установка, настройка и параметризация
нагрузочного агента.
Или скачивание докер-образа с
преднастроенным источником нагрузки
Докер-образ источника нагрузки
(ЯТ, JM или самописный фреймворк)
Параметры настройки
агента нагрузки
Настроенный и готовый
к работе агент нагрузки
Test: этап выполнения нагрузочных тестов. Источниками являются агенты нагрузки, развернутые в выделенных пулах агентов для GitLab CI
Load
Запуск агента нагрузки
с выбранным тест-планом
и параметрами нагрузки
Пользовательские параметры
для инициализации
агента нагрузки
Тест-план
Цель тестирования
Логи выполнения
нагрузочных тестов
Системные журналы
Динамика изменения метрик цели и нагрузочного агента
RunAgents
Исполнение агентом
нагрузки тестовых сценариев
в соответствии с
профилем нагрузки
Взаимодействие агента нагрузки
с целью тестирования
Тест-план
Цель тестирования
Logs
Сбор «сырых» логов
в процессе нагрузочного тестирования:
записи о действиях агента нагрузки,
состоянии цели тестирования
и ВМ, на которой запущен агент
Логи выполнения
нагрузочных тестов
Системные журналы
Metrics
Сбор «сырых» метрик в процессе тестирования
Динамика изменения метрик цели
и нагрузочного агента
Report: этап подготовки отчета о тестировании
Generator
Обработка собранных
нагрузочной системой и
системой мониторинга «сырых»
метрик и логов
Формирование отчета в
человекочитаемом виде,
возможно с элементами
аналитики
Логи выполнения
нагрузочных тестов
Системные журналы
Динамика изменения метрик
цели и нагрузочного агента
Обработанные «сырые» логи
в формате, пригодном для
выгрузки во внешние хранилища
Статический отчет о нагрузке,
пригодный для анализа человеком
Publish
Публикация отчета
о нагрузочном
тестировании во внешнем
сервисе
Обработанные «сырые»
логи в формате, пригодном
для выгрузки во внешние
хранилища
Сохраненные во внешнем
хранилище отчеты о
нагрузке, пригодные
для анализа человеком
Подключение источников нагрузки в CI-шаблоне
Перейдем к практической части. Я хочу показать, как на некоторых проектах в компании Positive Technologies мы реализовали концепцию нагрузочного тестирования как сервиса.
Сначала силами наших DevOps-инженеров мы создали в GitLab CI выделенный пул агентов для запуска нагрузочных тестов. Чтобы не путать их в шаблонах с другими, например сборочными, пулами, мы добавили теги на эти агенты, tags: load. Можно использовать любые другие понятные теги. Они задаются во время регистрации GitLab CI Runners.
Как узнать требуемую мощность по «железу»? Характеристики нагрузочных агентов — достаточное количество vCPU, RAM и Disk — могут быть рассчитаны исходя из того, что на агенте должны быть запущены Docker, Python (для Yandex.Tank), агент GitLab CI, Java (для Apache JMeter). Для Java под JMeter также рекомендуют использовать минимум 512 МБ RAM и, в качестве верхнего предела, 80% доступной памяти.
Таким образом, исходя из нашего опыта, мы рекомендуем использовать для нагрузочных агентов как минимум: 4 vCPU, 4 ГБ RAM, 60 ГБ SSD. Пропускная способность сетевой карты определяется на основе требований профиля нагрузки.
У нас в основном используются два источника нагрузки — докер-образы Apache JMeter и Yandex.Tank.
Yandex.Tank — это опенсорсный инструмент компании Yandex для проведения нагрузочного тестирования. В основе его модульной архитектуры — высокопроизводительный асинхронный hit-based-генератор HTTP-запросов Phantom. Танк имеет встроенный мониторинг ресурсов тестируемого сервера по протоколу SSH, может автоматически остановить тест по заданным условиям, умеет выводить результаты как в консоль, так и в виде графиков, к нему можно подключить свои модули для расширения функциональности. Кстати, мы использовали Танк, когда это еще не было мейнстримом. В статье «Яндекс.Танк и автоматизация нагрузочного тестирования» можно прочитать историю, как в 2013-ом мы проводили с его помощью нагрузочное тестирование PT Appllication Firewall — одного из продуктов нашей компании.
Apache JMeter — это опенсорсный инструмент для проведения нагрузочного тестирования компании Apache. Он может одинаково хорошо использоваться как для тестирования статических, так и динамических веб-приложений. JMeter поддерживает огромное количество протоколов и способов взаимодействия с приложениями: HTTP, HTTPS (Java, NodeJS, PHP, ASP.NET и т. д.), SOAP / REST Webservices, FTP, TCP, LDAP, SMTP(S), POP3(S) и IMAP(S), базы данных через JDBC, умеет исполнять shell-команды и работать с Java-объектами. У JMeter есть IDE для создания, отладки и исполнения тест-планов. Также имеется CLI для работы в командной строке любой совместимой с Java ОС (Linux, Windows, Mac OS X). Инструмент умеет динамически генерировать HTML-отчет о тестировании.
Для удобства использования внутри нашей компании, для возможности самим тестировщикам изменять и добавлять окружение мы сделали сборки докер-образов источников нагрузки на GitLab CI с публикацией во внутренний докер-реджистри на Artifactory. Так получается быстрее и проще подключать их в пайплайнах для нагрузочных тестов. Как сделать docker push в registry через GitLab CI — смотрите в инструкции.
Базовый докер-файл для Yandex.Tank мы взяли этот:
Dockerfile
1 | FROM direvius/yandex-tank
2 | ENTRYPOINT [""]
А для Apache JMeter этот:
Dockerfile
1 | FROM vmarrazzo/jmeter
2 | ENTRYPOINT [""]
Пример шаблона для проведения нагрузочных тестов доступен в проекте demo-load. В readme-файле можно прочитать инструкцию по использованию шаблона. В самом шаблоне (файл .gitlab-ci.yml) есть примечания о том, за что отвечает тот или иной шаг.
Шаблон очень простой и демонстрирует три этапа нагрузочного тестирования, описанных выше на схеме: подготовка, тестирование и публикация отчетов. За это отвечают stages: Prepare, Test и Report.
Этап Prepare следует использовать для предварительной настройки целей тестирования или проверки их доступности. Окружение для источников нагрузки настраивать не требуется, они заранее собраны как докер-образы и выложены в docker registry: достаточно указать нужную версию на этапе Test. Но можно пересобрать их и сделать свои модифицированные образы.
Этап Test используется для указания источника нагрузки, запуска тестов и сохранения артефактов тестирования. Можно выбрать любой источник нагрузки: Yandex.Tank, Apache JMeter, свой или все вместе. Для отключения ненужных источников достаточно закомментировать или удалить job-у. Точки входа для источников нагрузки:
параметры запуска Apache JMeter указываются в файле ./tests/jmeter.sh.
Примечание: шаблон сборочной конфигурации используется для настройки взаимодействия с CI-системой и не предполагает размещения в нем логики тестов. Для тестов указывается точка входа, где находится управляющий bash-скрипт. Способ запуска тестов, формирование отчетов и сами тестовые сценарии — должны быть реализованы QA-инженерами. В демопримере для обоих источников нагрузки в качестве простейшего теста используется реквест основной страницы Яндекса. Сценарии и параметры тестов лежат в каталоге ./tests.
На этапе Report нужно описать способы публикации результатов тестирования, полученных на этапе Test, во внешние хранилища, например в GitLab Pages или специальные системы отчетности. Для GitLab Pages требуется, чтобы после окончания тестов каталог ./public был непуст и содержал как минимум файл index.html. О нюансах работы сервиса GitLab Pages вы можете прочитать по ссылке.
В демопримере пайплайн с нагрузочными тестами и двумя источниками нагрузки (можно отключить ненужный) выглядит так:
Apache JMeter умеет сам формировать HTML-отчет, поэтому его выгоднее сохранять в GitLab Pages штатными средствами. Вот так выглядит отчет Apache JMeter:
В демопримере для Yandex.Tank вы увидите лишь фейковый текстовый отчет в разделе для GitLab Pages. В процессе тестирования Танк умеет сохранять результаты в базу InfluxDB, а оттуда их можно отобразить, например, в Grafana (настройка выполняется в файле ./tests/example-yandextank-test.yml). Вот так отчет Танка выглядит в Grafana:
Резюме
В статье я рассказал про концепцию «нагрузочное тестирование как сервис» (load testing as a service). Основная идея состоит в использовании инфраструктуры преднастроенных пулов нагрузочных агентов, докер-образов источников нагрузки, систем отчетности и объединяющего их пайплайна в GitLab CI на базе простого шаблона .gitlab-ci.yml (пример по ссылке). Все это поддерживается силами небольшой команды инженеров-автоматизаторов и тиражируется по запросу продуктовых команд. Надеюсь, это поможет вам в подготовке и реализации аналогичной схемы в вашей компании. Благодарю за внимание!
P. S. Хочу сказать большое спасибо моим коллегам, Сергею Курбанову и Николаю Юсеву, за техническую помощь с реализацией концепции load testing as a service в нашей компании.
Автор: Тимур Гильмуллин — зам. руководителя отдела технологий и процессов разработки (DevOps) компании Positive Technologies