Одна из проблем, с которой сталкиваются мультипродуктовые вендоры ПО, это дублирование компетенций инженеров разработчиков, тестировщиков и администраторов инфраструктуры в каждой команде. В том числе, это касается и дорогостоящих инженеров — специалистов в области нагрузочного тестирования. Вместо того, чтобы заниматься своими прямыми обязанностями и использовать свой уникальный опыт для выстраивания методологии проведения нагрузочного тестирования разрабатываемого продукта, подбирать оптимальные значения метрик, писать автотесты в соответствии с профилями нагрузки, инженерам зачастую приходится с нуля разворачивать тестовую инфраструктуру, настраивать инструменты нагрузки, самим встраивать их в CI-системы, настраивать мониторинг и публикацию отчётов.
Как решить некоторые проблемы с организацией тестирования вы можете прочитать в моей предыдущей статье: "Тестирование в общем сборочном конвейере: решение организационных проблем". В этой статье я расскажу про возможность интегрировать ваши нагрузочные тесты в общий CI-конвейер на GitLab CI используя концепцию "Нагрузочное тестирование как сервис" (Load testing as a service). Также вы узнаете: как и какие докер-образы источников нагрузки Apache JMeter и Yandex.Tank можно использовать в CI-конвейере; как подключить источники нагрузки в свой проект при помощи шаблона .gitlab-ci.yml; как выглядит демо-пайплайн для запуска нагрузочных тестов и публикации результатов. Статья будет полезна инженерам по тестированию ПО и инженерам-автоматизаторам.
План:
- Суть концепции
- Основные понятия и определения
- Основные оцениваемые метрики
- Принципиальная схема нагрузочного тестирования
- Подключение в шаблоне .gitlab-ci.yml источников нагрузки: Yandex.Tank и Apache JMeter
- Заключение
Суть концепции
Концепция "Load testing as a service" подразумевает возможность интегрировать инструменты нагрузки Apache JMeter, Яндекс.Танк и собственные фреймворки в систему continuous integration GitLab CI. Она позволяет реализовать централизованный сервис для проведения нагрузочного тестирования. Нагрузочные тесты запускаются в выделенных пулах агентов, публикация результатов происходит автоматически в общие GitLab Pages, Influx DB, Grafana или иные системы тест-репортинга (TestRail, ReportPortal etc). Автоматизация всего этого реализуется максимально просто — через добавление и параметризацию в CI-проекте обычного шаблона gitlab-ci.yml.
При этом вся CI-инфраструктура, нагрузочные агенты, докер-образы источников нагрузки пайплайны тестирования и публикация отчётов — поддерживаются силами выделенного отдела автоматизации (DevOps), а инженеры по нагрузочному тестированию могут сосредоточить свои усилия на разработке тестов и анализе их результатов.
Будем считать, что целевое тестируемое приложение или сервер были развёрнуты и настроены заранее (для этого могут быть использованы автоматизированные сценарии на Python, SaltStack, Ansibe etc). Тогда вся концепция нагрузочного тестирования как сервиса укладывается в три этапа: подготовка, тестирование, публикация отчётов. Подробнее на схеме (картинки кликабельны):
Основные понятия и определения
Нагрузочный агент (load agent) — виртуальная машина, на которой будет запущено приложение источник нагрузки (Apache JMeter, Яндекс.Танк или самописный нагрузочный модуль).
Цель тестирования (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 (в Mb или Gb) — "железные" характеристики нагрузочного агента. | CPU, Memory, Disk usage — динамика загрузки процессора, памяти и диска в процессе тестирования. Обычно измеряется в % от максимально доступных значений. |
Network throughput (load agent) — пропускная способность сетевого интерфейса на сервере, где установлен нагрузочный агент. Обычно измеряется в байтах в секунду (bps). | Network throughput (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-й перцентиль. Минимальные и максимальные показания показывают крайние состояния производительности системы. 90-й перцентиль используется наиболее часто, поскольку он показывает большинство пользователей, комфортно работающих на пороге производительности системы.
| |
Transactions per second (minute) — количество полных транзакций в секунду (минуту), то есть сколько приложение смогло принять и обработать запросов и выдать ответов. Фактически, это пропускная способность системы. | |
Transactions status, Passed / Failed / Total — количество успешных, неуспешных и общее количество транзакций.
Для реальных пользователей неуспешная транзакция фактически будет означать невозможность работы с системой под нагрузкой.
|
Принципиальная схема нагрузочного тестирования
Принципиальная схема нагрузочного тестирования очень простая и состоит из трёх основных этапов, о которых я уже упоминал: Prepare — Test — Report, то есть подготовка целей тестирования и задание параметров для источников нагрузки, затем непосредственное выполнение нагрузочных тестов, и, в конце, формирование и публикация отчёта по тестированию.
На схеме:
- QA.Tester — эксперт по нагрузочному тестированию
- Target — целевое приложение для которого нужно узнать его поведение под нагрузкой
Этапы и шаги на схеме
Этапы и шаги | Что происходит на этапе или шаге | Что на входе | Что на выходе |
---|---|---|---|
Prepare: этап подготовки к тестированию | |||
LoadParameters | Задание и инициализация пользователем параметров нагрузки, выбор метрик и подготовка тест-плана (профиля нагрузки). | Пользовательские параметры для инициализации агента нагрузки. Тест-план. Цель тестирования. | |
VM | Развёртывание в облаке (vSphere или внешние) виртуальной машины с требуемыми характеристиками. | Параметры ВМ для агента нагрузки. Скрипты автоматизации для создания ВМ. | Настроенная ВМ в облаке. |
Env | Настройка ОС и подготовка окружения для работы нагрузочного агента. | Параметры окружения для агента нагрузки. Скрипты автоматизации для настройки окружения. | Подготовленное окружение: ОС, сервисы и приложения, необходимые для работы агента нагрузки. |
LoadAgents | Установка, настройка и параметризация нагрузочного агента. Либо скачивание докер-образа с преднастроенным приложением источником нагрузки. | Докер-образ источника нагрузки (ЯТ, JM или самописный фреймворк). Параметры настройки агента нагрузки. | Настроенный и готовый к работе агент нагрузки. |
Test: этап выполнения нагрузочных тестов. Источниками являются агенты нагрузки, размещённые в выделенных пулах агентов для GitLab CI | |||
Load | Запуск параметризованного агента нагрузки с выбранным тест-планом. | Пользовательские параметры для инициализации агента нагрузки. Тест-план. Цель тестирования. | Логи выполнения нагрузочных тестов. Системные логи. Динамика изменения метрик цели и нагрузочного агента. |
RunAgents | Исполнение агентом нагрузки тестовых сценариев в соответствии с профилем нагрузки. Взаимодействие агента нагрузки с целью тестирования. | Тест-план. Цель тестирования. | |
Logs | Сбор "сырых" логов в процессе нагрузочного тестирования: о действиях агента нагрузки, состоянии цели тестирования и ВМ, на которой запущен агент. | Логи выполнения нагрузочных тестов. Системные логи. | |
Metrics | Сбор "сырых" метрик в процессе тестирования. | Динамика изменения метрик цели и нагрузочного агента. | |
Report: этап подготовки отчёта по тестированию | |||
Generator | Обработка собранных нагрузочной системой и системой мониторинга, "сырых" метрик и логов. Формирование отчёта в человеко-читаемом виде, возможно, с элементами аналитики. | Логи выполнения нагрузочных тестов. Системные логи. Динамика изменения метрик цели и нагрузочного агента. | Обработанные "сырые" логи в формате, пригодном для выгрузки во внешние хранилища. Статический отчёт по нагрузке, пригодный для анализа человеком. |
Publish | Публикация отчёта по нагрузочному тестированию во внешнем сервисе. | Обработанные "сырые" логи в формате, пригодном для выгрузки во внешние хранилища. | Сохранённые во внешнем долговременном хранилище отчёты по нагрузке, пригодные к анализу человеком. |
Подключение в шаблоне .gitlab-ci.yml источников нагрузки: Yandex.Tank и Apache JMeter
Давайте перейдём к практической части. Я попытаюсь рассказать про то, как примерно реализована концепция нагрузочного тестирования как сервиса внутри нашей компании Positive Technologies.
Сначала силами нашего отдела автоматизации (DevOps) в GitLab CI был создан выделенный пул агентов, которые используются только для запуска нагрузочных тестов. Чтобы не путать их в шаблонах с другими, например, сборочными пулами, мы протегировали эти агенты, tags: load. Можно использовать любые другие понятные теги. Они задаются во время регистрации GitLab CI Runners.
Характеристики нагрузочных агентов, достаточное количество vCPU, RAM и Disk было рассчитано исходя из того, что на агенте должны быть запущены:
- Docker,
- Python (для Яндекс.Танка),
- агент GitLab CI,
- Java (для Apache JMeter).
Про Java для JMeter также есть рекомендации использовать минимум 512Mb RAM и, в качестве верхнего предела, 80% доступной памяти ("Just increase the maximum heap size to ~80% of your total available physical RAM. For example, if you want to set the maximum heap size to 4 gigabytes, you’ll need to change the line to: HEAP="-Xms512m -Xmx4096m").
У нас, в основном, используются два источника нагрузки: докер-образы Apache JMeter и Yandex.Tank.
Yandex.Tank — это опенсорс-инструмент для проведения нагрузочного тестирования от компании Yandex. В основе его модульной архитектуры — высокопроизводительный асинхронный hit-based-генератор HTTP-запросов Phantom. Танк имеет встроенный мониторинг ресурсов тестируемого сервера по протоколу SSH, может автоматически остановить тест по заданным условиям, умеет выводить результаты в консоль и в виде графиков, а также к нему можно подключить свои модули для расширения функциональности. Мы уже применяли когда-то этот инструмент для проведения нагрузочного тестирования одного из продуктов компании: PT Appllication Firewall. Подробнее читайте об этом в статье: "Автоматизация нагрузочного тестирования при помощи инструмента Яндекс.Танк" (или на Хабре).
Apache JMeter — это опенсорс-инструмент для проведения нагрузочного тестирования от компании Apache. Он может использоваться одинаково хорошо как для тестирования статических, так и динамических веб-приложений. JMeter поддерживает огромное количество протоколов и способов взаимодействия с приложениями: HTTP, HTTPS (Java, NodeJS, PHP, ASP.NET etc), SOAP / REST Webservices, FTP, TCP, LDAP, SMTP(S), POP3(S) и IMAP(S), базы данных через JDBC, умеет исполнять shell-команды и работать с Java-объектами. Инструмент обладает полнофункциональной IDE для создания, отладки и исполнения тест-планов. Также имеется возможность работы через CLI в командной строке в любой совместимой с Java ОС (Linux, Windows, Mac OS X). JMeter умеет генерировать динамический html-отчет по результатам тестирования.
Для удобства использования внутри компании и возможности изменять или добавлять окружение для этих инструментов самими тестировщиками, мы сделали простые сборочные pipelines докер-образов на 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 [""]
Как устроена наша система Continuous Integration вы можете прочитать в статье "Как мы в Positive Technologies внедряли идеи DevOps" (или на Хабре).
Шаблон и пайплайн
Демо-пример шаблона GitLab CI для проведения нагрузочных тестов, аналогичный используемым у нас, доступен в проекте по ссылке: demo-load. В README-файле можно прочитать инструкцию по доработке и использованию шаблона. В самом шаблоне .gitlab-ci.yml есть примечания, за что отвечает тот или иной шаг.
Шаблон очень простой и демонстрирует три этапа нагрузочного тестирования, описанных выше на схеме: подготовка, тестирование и публикация отчётов. За это отвечают stages: Prepare, Test и Report в шаблоне.
- Этап Prepare можно использовать для предварительной настройки целей тестирования или проверки их доступности. Окружение для источников нагрузки настраивать не потребуется, если они уже были собраны как докер-образы и выложены в локальный docker registry: достаточно указать нужную версию на этапе Test.
- Этап Test в шаблоне используется для выбора источника нагрузки и сохранения артефактов тестирования. Можно указать любой источник нагрузки: Yandex.Tank, Apache JMeter, свой или все вместе. Также можно отключить ненужные источники, просто закомментировав или удалив job-у.
Параметры запуска Yandex.Tank отредактируйте в файле ./tests/yandextank.sh, а параметры запуска Apache JMeter отредактируйте в файле ./tests/jmeter.sh.
Примечание: шаблон сборочной конфигурации предполагает настройку взаимодействия с CI-системой, а способы запуска тестов и формирование отчётов вынесены во внешние файлы и тестовые сценарии. В шаблоне указывается точка входа, где находится управляющий скрипт, а его логика и тестовые сценарии должны быть реализованы QA-инженерами. В нашем демо-примере для обоих источников нагрузки в качестве простейшего теста используется реквест основной странички Яндекса. Сценарии и настройки для JMeter и Танка лежат в каталоге ./tests.
- Этап Report описывает способы публикации результатов тестирования, сформированных на этапе Test, во внешние хранилища: GitLab Pages или иные системы отчётности.
Для GitLab Pages требуется, чтобы после окончания тестов каталог ./public был не пустой и содержал, как минимум, файл index.html. О нюансах работы GitLab Pages вы можете почитать по ссылке.
Примеры, как экспортировать данные:
- от JMeter в GitLab Pages,
- от Яндекс.Танка в InfluxDB и Grafana.
Инструкции по публикации:
- html-статики в GitLab Pages,
- в InfluxDB и оттуда в Grafana.
В нашем примере отработавший пайплайн с нагрузочными тестами для разных источников нагрузки выглядит так:
Apache JMeter умеет сам формировать html-отчёт, поэтому его выгоднее запушить в GitLab Pages штатными средствами. Пример, как выглядит отчёт Apache JMeter на GitLab Pages:
Yandex.Tank из коробки поддерживает публикацию в InfluxDB и Grafana, поэтому в демо-примере на GitLab Pages мы разместили лишь фейковый текстовый отчёт. Yandex.Tank умеет сам в процессе тестирования сохранять результаты в базу InfluxDB, а оттуда его можно отобразить, например, в Grafana (настройки выполняются в файле ./tests/example-yandextank-test.yml). Пример, как отчёт Танка выглядит в Grafana:
Заключение
Из статьи вы узнали, что концепция "Нагрузочное тестирование как сервис" (Load testing as a service) подразумевает возможность интеграции различных источников нагрузки в GitLab CI. Для этого используется инфраструктура преднастроенных пулов нагрузочных агентов, докер-образы источников нагрузки, системы отчётности и объединяющий их пайплайн в GitLab CI на простом шаблоне .gitlab-ci.yml. Всё это поддерживается силами выделенной команды инженеров-автоматизаторов (DevOps) и тиражируется по запросу команд инженеров по нагрузке.
На этом знакомство с концепцией будем считать завершённым. Надеюсь, это поможет вам в подготовке и реализации аналогичной схемы в вашей компании. Благодарю за внимание!