Jmeter: запуск тестов в kubernetes используя Jenkins

Описание

Процес установки Grafana, Influxdb, Jmeter в kubernetes описывать не стану, так как взял уже готовый чарт https://github.com/kaarolch/kubernetes-jmeter, который установил используя Jenkins.

В нем есть:

  • Dockerfile c Jmeter
  • скрипт запуска тестов, helm-chart, Grafana, Influxdb которые можно модифицировать под себя.
  • добавлен dashboard который импортируется при установк
  • добавил несколько плагинов для JmeterMaster в Dockerfile.

Задание состоит в том, чтоб автоматизировать запуск нагрузочного тестирования, так как сейчас делаем все руками. На основе собранной информации от команд прикинул приблизительный план реализации:

  • Запуск скрипта
    Groovy cкрипт будем запускать в docker образе, в котором установлен kubectl, aws-cli, helm, helm-secret, sops;
  • Создание и Удаление ресурсов
    Должна быть возможность создавать и удалять RDS Cluster, который будет использовать тестовая апка. Так как после создания нужно заливать данные на созданный кластер, а это очень долгий процесс, такой вариант не подходит. Оптимальным решением вижу восстанавливать кластер со снапшота.
    Добавим возможность создания и удаление только тогда когда нам это необходимо, используя Boolean Parameter. Стейдж создание назовем CREATE_RDS_CLUSTER и разместим его в начале скрипта, а удаление DELETE_RDS_CLUSTER разместив в конце.
  • Доступы
    Для доступа к AWS RDS, EKS, KMS нужен IAM пользователь с необходимыми правами.
    Данные пользователя передаем через параметры c именем AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY.
  • Хранение тест план файлов
    Хранить будем в отдельном GitHub репозитории. Файл JMX содержит токены и пароли которые нужны для тестов, их нужно убрать и передавать через Jenkins, но есть одно, но, так файл будет очень часто изменяться, будут добавляться новые параметры – передавать через Jenkins не выйдет (прийдется постоянно изменять пайплайн).
    По этому файл шифруем и храним в шифрованном виде.
    Активно используем helm-secret под капотом которого работает sops, по этому используем сам sops.  В данном случае нужно настраивать каждому QA aws profile с доступом к kms, чтоб они могли шифровать и дешифровать файлы.
  • Параметры
    Пароли, секреты будем передавать через password parameters, а остальные как string parameters. QA команда должна иметь возможность передавать их через Jenkins Job.
    Ниже список основных параметров которые будем использовать:
    • Репозиторий и ветку с тестовыми файлами – QA_REPO_URL, QA_REPO_BRANCH.
    • Выбор кластера и неймспейса в котором задеплоен jmeterAWS_EKS_CLUSTER, AWS_EKS_NAMESPACE.
    • Количество jmeter-slaves JMETER_SLAVES_COUNT.
    • Выбор нужного JMX файла – JMETER_TESTPLAN_FILE.
    • Уменьшение и увеличение jmeter-slave. Здесь должна быть возможность указывать нужное количество pod. Для реализации воспользуемся kubectl scale deployment которому через string parameter с именем JMETER_SLAVES_COUNT будем передавать нужное количество подов.
  • Подготовка и Запуск тестов
    Jmeter в нашем сетапе не имеет GUI, недоступный из вне, доступ только внутри кластера. Как запускать тесты? Оптимальным вариантом вижу использования kubectl.
    Напишем
    stage(“Copy FileJMX to Jmeter-Master”) который будет дешифровать файл, после чего копировать его используя kubectl cp в pod с jmeter-master, после в stage(“Scale Up Jmeter-Slaves”) наскейлим нужное количество подов и запустим тесты в stage(“Run LoadTesting”) используя kubectl exec.
  • Просмотр результатов
    Нужно выводить результат тестирования в конце задания. Результаты отображаются в Grafana, по этому в конце вижу смысл добавить stage(“ShowTestResult”) где будем выводить URL на Grafana Dashboard с указанным интервалом времени (начало и окончание тестования).
    За период времени в URL отвечает &from=***&to=***. Останется только придумать как подставлять время.

Приступим к практике. Полную версию пайплайна можно скачать здесь.

Написание groovy

Создание RDS Cluster

Создадим в Jenkins задание с типом pipeline назвав его jmeter-load-testing. Добавим в него Boolean Parameter чтоб при активации он восстанавливал базу со снапшота и добавлял CNAME в Route53:
jmeter-load-testing –> This project is parameterized –> Add Parameters –> Boolean Parameter, назовем параметр CREATE_RDS_CLUSTER и добавим описание:

Для доступа к ресурcам AWS нужно передавать AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, использовать их будем как переменною при запуске контейнера:

Переходим к скрипту.  Добавим условие создания RDS:

Когда условие будет true (поставлена галка в боксе), используя aws-cli будем восстанавливать RDS кластер со снапшота. При восстановлении кластера нужно придерживаться последовательности восстановления.

Сначала поднимаем сам кластер aws rds restore-db-cluster-from-snapshot а после создаем два инстанса (writer and reader) используя команды aws rds create-db-instanceВ конце добавим aws rds wait db-instance-available и будем ждать когда кластер будет доступный.

Для поиска последнего снапшота добавим функцию def rdsSnapshot(){} (функции нужно размещать над блоком node(”){}) которая будет выводить список всех snapshots, после чего сортировать по нужному имени и оставлять только самый последний убрав все лишнее, приступим:

добавляем скрипт в задание, сохраняем:

Запускаем Build with Parameters, ставим галку в боксе CREATE_RDS_CLUSTER, далее жмем Build:

В среднем создание(восстановление) RDS занимает 21-25 минуты может и больше, все зависит от размера кластера. Если создавать с нуля и накатывать дамп – будет намного дольше. Собственно результат выполнения смотрим ниже:

 

 

 

 

 

 

 

Проверяем RDS Cluster, AWS Console –> RDS:

запись в Route53:

подключение к базе:

Отлично базы все на месте, приступим к следующему этапу.

Подготовка к тестированию

Клонирование репозитория | генерация kube conf

Перед началом тестирования нужно выполнить подготовку. В подготовку входит:

  • клонирование репозитория.
  • генерация kubeconf файла для доступа к кластеру.
  • выбор нужного файла и его расшифровка.
  • копирование в под с jmeter-master.
  • увеличение количества подов jmeter-slave.

Сначала добавим stage(‘Clone Repository’) который будет клонировать репозиторий, а после генерировать kubeconfig файл  для доступа к кластеру, приступим.

Добавим параметры QA_REPO_URL, QA_REPO_BRANCH, AWS_EKS_CLUSTER:

затем stage(‘Clone Repository’):

Выбор файла, дешифрование, копирование

Теперь нужно выбирать JMX файл, дешифровать его (как пользоваться sops можно почитать kms-aws-profiles) и cкопировать в под jmeter-master. Здесь нужно добавить два параметра:

  • AWS_EKS_NAMESPACEчтоб указывать namespace в котором задеплоен jmeter.
  • JMETER_TESTPLAN_FILE для выбора нужного файла JMX (в описании будет список доступных файлов).

После чего напишем небольшую функцию getPod() которая будет выводить список всех подов в неймспейсе, фильтровать, оставлять только название пода jmeter-master. Результат данной функции будем подставлять как env в нужных нам местах. 

Добавляем параметры AWS_EKS_NAMESPACE и JMETER_TESTPLAN_FILE:

переходим к функции getPod() и stage(“Copy FileJMX to Jmeter-Master”):

хорошо, половину работы сделано. 

Увеличение подов jmeter-slaves

Следующим этапом нужно увеличить нужное количество jmeter-slaves. Для увеличения используем команду kubectl scale deployment а после kubectl wait, чтоб дождаться статус подов Ready.

Добавим параметр JMETER_SLAVES_COUNT:

и stage(“Scale Up Jmeter-Slaves”):

Запуск нагрузочного тестирования

Мы все подготовили для запуска тестирования. Тесты будем запускать с помощью скрипта run-test.sh.  Рассмотрим его детальней:

Конечно скрипт можно модифицировать или вообще избавиться от него (при сборке образа), но меня все устраивает. Команда которой будем запускать скрипт в поде выгладит так:

  • kubectl exec -i -n ${AWS_EKS_NAMESPACE} ${jmMaster} — sh -c ‘ONE_SHOT=true; /run-test.sh’.

Лучше после завершения тестирования ничего не оставлять=), будем удалять файл с пода и останавливать тесты на слейвах (для чего это нужно описано в конце поста).

Добавляем stage(“Run LoadTesting”) и описываем его:

Что ж, приступим к запуску. 
Жмем Build with Parameters, указываем количество jmerer-slave, выберем файл с тестами, после жмем Build:

смотрим output:

тесты запустились, ждем завершения, после  посмотрим всю последовательность прохождения:

и результат тестов в Grafana:

Отлично.

Результат Тестирования

Так как же нам вывести время? как добавить его в URL в нужное место?
Первое, что пришло на ум – использовать команду date, команда позволяет показать текущую дату и время в терминале.

Осталось найти нужный формат времени который использует Grafana. Поискав немного нашёл то что нужно: date and time YYYYMMDDTHHmmss from=20211029T154321 to=20211029T154620.

Проверим как работает команда date, главное чтоб в %Y%m%dT%H%M%S не было никаких разделителей кроме буквы T (для обозначения, что далее идет время), запустим ее:

Хорошо, напишем две функции.

  • первая – функция с именем  startTime(), она выводит время начала тестирования, ee будем вызывать в stage(“Run LoadTesting”)
  • вторая – функция с имене endTime(), которою вызываем в stage(“Scale Down Jmeter-Slaves”)  и она показывает время окончание.

Добавляем функции:

вызываем их в стейджах:

и добавим stage(“ShowTestResult”), указав переменные в from=${start} и to=${end}, таким образом результаты функций будут подставляться через переменную в URL и мы получим ссылку на результат за нужный период времени:

запускаем задание, ждем его завершения и ищем в Console Output stage(“ShowTestResult”):

переходим по ссылке:

как видим открыта борда с результатом за нужный период времени):

Удаление RDS Cluster

Когда тестировать больше ненужно, удаляем созданий кластер RDS. Для этого добавим stage(“Delete RDS Cluster”) и условие if (env.DELETE_RDS_CLUSTER.toBoolean())При запуске финального тестирования, будем ставить галочку в боксе DELETE_RDS_CLUSTER, после того как заданные выполнит все стейджи, в конце будет вызван стейдж удаления.

Для удаления используем aws-cli:

Добавим параметр DELETE_RDS_CLUSTER:

и условие if (env.DELETE_RDS_CLUSTER.toBoolean()) с stage(“RDS: Delete CLuster”):

Осталось произвести финальный запуск тестирования. Перед запуском посмотрим все параметры которые мы добавили, поставим галку DELETE_RDS_CLUSTER, запустим нажав Build:

 

Ошибки

Engine is busy – please try later

Задание после нескольких запусков перестало отрабатывать. Посмотрев логи увидел следующее:

Проблема возникает когда тест не остановлен, на jmeter-slave все еще запущен процесс с предвидущего тестирования.  Есть несколько решений:

  • 1. Можем после завершения stage(“Run LoadTesting”), заскейлить поды слейва в 0. При новом запуске тестов будет создаваться новый под и задание отработает без ошибок.
  • 2. Добавить команду, которая будет останавливать процессы тест плана на слейвах. Для этого необходимо запустить скрипт shutdown.sh на jmeter-master который находиться в директории /opt/jmeter/bin/, что мы и сделали выше.

Полезные ссылки

Click to rate this post!
[Total: 0 Average: 0]