Как сделать инструмент для запуска нагрузки одной кнопкой: показываем наш Pangoloader
Привет, Хабр! Меня зовут Дмитрий Королёв, я инженер по нагрузочному тестированию Platform V Pangolin — целевой СУБД в Сбере и не только. Эту статью я написал вместе с моим коллегой Алексеем Хорохориным @AlexeyHorohorin. Наш продукт — специальная сборка PostgreSQL с доработками (крупных больше 30, а всего уже больше 70) в области безопасности, производительности, отказоустойчивости.
Условия для нас таковы: мы тестируем продукт целиком, в разных версиях, на разных ядрах. У нас работает три версии в параллель для восьми разных ОС, и тестировать нам нужно при использовании и неиспользовании разных фич. А теперь представьте, сколько тут интеграций…
Автоматизировать нагрузку помогают разные инструменты. Но в них нам не хватало автоматизации прогона нагрузочных сценариев и составления итогового отчёта по завершении тестов. Поэтому мы сделали и развиваем свой нагрузчик — Pangoloader. В этой статье расскажем, как он устроен, что умеет и как мы собираемся доработать его в будущем.
Как устроено нагрузочное тестирование очередного релиза Pangolin
Сначала тестируем максимум TPS (транзакции в секунду), подтверждаем его двухчасовым тестом. В тестировании максимума есть ступеньки: поднимаем нагрузку до стабильной ступеньки и гоняем два часа, чтобы убедиться в том, что это точно максимум. Потом 24-часово тест стабильности: смотрим утечки памяти и прочие проблемы, которые могут произойти. После него тест отказоустойчивости (failover). Если отказывает мастер (выходит из строя), то проверяем, насколько успешно и как долго происходит его смена, как реплика становится мастером (реплика — это как запасной парашют).
Раньше мы делали нагрузку самописными скриптами на Bash. Это простой и доступный способ, но он не позволял нам реализовать универсальный механизм для одновременного запуска множества сценариев тестирования. Что ещё важнее, с ним сложно сформировать итоговый отчёт по завершении тестирования. А собирать такой отчёт вручную долго, и ещё нужно не забыть включить всю информацию…
Поэтому в конце 2022 года у нас появился свой инструмент для нагрузки.
Что представляет из себя наш Pangoloader
Это связка из двух инструментов:
Benchbase — open source-инструмент для проведения нагрузочных тестов для различных СУБД. Его мы доработали под наши требования.
Pangoloader — инструмент на Python. Он позволяет подготавливать и запускать нагрузочные инструменты, такие как Benchbase, отслеживать их статус работы и собирать нам красивый итоговый отчёт на HTML-странице, куда выгружаются настройки СУБД, флаги сборки, графики за время тестирования, бизнес-метрики и системные метрики.
Эта статья посвящена Pangoloader, но здесь коротко ответим на возможные вопросы про Benchbase и выбор первого решения в связке.
Какие еще инструменты можно было использовать:
Pgbench содержит базовый сценарий нагрузки, но нам хотелось чего-то посложнее. К тому же pgbench — это встроенный бенчмарк для тестирования PostgreSQL, не получилось бы сравнить производительность на одном сценарии на разных СУБД/
Jmeter — у нас не было опыта работы с ним, а всю логику нагрузочного сценария и генерирования данных для него пришлось бы реализовывать на нём, что заняло бы довольно много времени.
HammerDB может нагружать различные СУБД для сравнения производительности, но у него всего два сценария нагрузки.
Выбрали Benchbase, потому что:
он может нагрузить разные СУБД;
у него много подготовленных сценариев;
реализован на Java, у нас в ней есть опыт, можно внести свои доработки;
готовая база для создания своих сценариев нагрузки.
Вернёмся к Pangoloader. Вот как всё устроено. Перед запуском утилиты готовим файл конфигурации в формате YAML:
stand:
stand_1:
ssh_user: pprb_dev
host: 65.32.1.29
postgresql:
port: 5433
pgbouncer:
port: 6544
benchmark:
benchbase:
host: 65.32.1.30
config: /tmp/tpcc_config.xml
command: java -jar /home/pprb_dev/benchbase-postgres/benchbase.jar
scalefactor: 2500
terminals: 250
time_load: 300
rate_load: 100
test_port: 6544
Прописываем серверы, на которых расположен тестовый стенд с СУБД, и хост с утилитой нагрузки:
ssh_user:
pprb_dev:
name: pprb_dev
password: pprb_dev_pass
postgres:
name: postgres
password: postgres_pass
sql_role:
postgres:
name: postgres
password: P@ssword
pangoloader:
name: postgres
password: P@ssword
pgbouncer:
name: pgbouncer
password: pgbpass
Вот секция для определения SSH- и SQL-пользователей. Они участвуют в тестировании:
grafana:
host: 65.32.1.5
port: 3000
api_key: eyJrIjoiTTVhTHpSd...
render_params:
theme: light
width: 1280
height: 720
var-interval: 2s
render_processes: 4
prometheus:
host: 65.32.1.4
port: 9090
Потом указываем хосты для утилит мониторинга Grafana и Prometheus. Выбираем некоторые параметры для рендера картинок с графиками, которые потом попадают в итоговый отчёт.
Главное требование к инструменту — возможность сохранять подготовленные файлы сценариев и впоследствии запускать их.
Сценарий нагрузки выглядит так:
class tpcc_load(Scenario):
dbname = 'First_db'
time = 7200
rate = 1260
test_port = 'pgbouncer'
test_generate_report = True
Scenario.description = """Тест подтверждения максимальной нагрузки.
Тест проводится на ступени нагрузки, предшествующей L0 (или на уровне нагрузки 90% от L0). Длительность стабильной нагрузки не менее 1 часа."""
Это класс, который представляет из себя сценарий тестирования. Здесь объявляются нужные в этом тесте параметры. Например:
dbname
— имя тестовой базы данных в СУБД;time
— время нагрузки;rate
— интенсивность нагрузки;test_port
определяет, к какому порту будут подключаться тестовые клиенты;test_generate_report
определяет, нужно ли генерировать отчёт по завершении сценария тестирования;Scenario.description
— текстовое описание сценария и того, что в нём происходит. Потом это описание попадает в отчёт.
Класс наследуется от класса Scenario
. Это абстрактный базовый класс, в нём содержится три метода:
setup
— служит, например, для предварительной настройки тестируемого объекта. Установка корректных параметров, перезапуск базы, генерация тестовых данных и так далее.execute
— сам сценарий теста. Здесь запускается нагрузка, с которой нам надо получить результаты в виде итоговой производительности, метрик системных ресурсов и прочего. Отчёт будет содержать данные только в интервале работы этого метода.teardown
— выполняется по завершении нагрузки. Он восстанавливает параметры до исходных значений, чтобы вернуть тестируемый объект в исходное состояние.
Так выглядит метод execute
для примера:
def execute(self):
benchmark = utils.Benchmark()
scalefactor = benchmark.scalefactor
terminals = benchmark.terminals
if tpcc_load.test_port == 'postgres':
conn_string = ','.join(f'{pg.get_host()}:{pg.get_port()}' for pg in config.postgresql_list())
elif tpcc_load.test_port == 'pgbouncer':
conn_string = ','.join(f'{pg.get_host()}:{pg.get_port()}' for pg in config.pgbouncer_list())
bench_cfg = benchmark.read_config('/home/pprb_dev/benchbase-postgres/config/postgres/tpcc_load.xml')
bench_cfg.find('url').text = f'jdbc:postgresql://{conn_string}/{tpcc_load.dbname}?ApplicationName=tpcc&reWriteBatchedInserts=true&prepareThreshold=0&targetServerType=primary'
bench_cfg.find('username').text = config.SQLRole('pangoloader').name
bench_cfg.find('password').text = config.SQLRole('pangoloader').password
bench_cfg.find('scalefactor').text = f'{scalefactor}'
bench_cfg.find('loaderThreads').text = f'{tpcc_load.loaderThreads}'
bench_cfg.find('terminals').text = f'{terminals}'
bench_cfg.find('works').find('work').find('time').text = f'{tpcc_load.time}'
bench_cfg.find('works').find('work').find('rate').text = f'{tpcc_load.rate}'
bench_cfg.find('works').find('work').find('weights').text = f'{tpcc_load.weights}'
benchmark.write_config('/tmp/tpcc_config.xml', bench_cfg)
benchmark.run(
"--bench tpcc",
"--config /tmp/tpcc_config.xml",
"--create=false",
"--clear=false",
"--load=false",
"--execute=true",
with_log = True,
)
В этом примере у нас уже есть тестовая база данных. Готовим конфигурацию для утилиты нагрузки с нужными параметрами и запускаем одной командой:
pangoloader --config config.yaml --report-template template/base.j2 --log-file pangoloader.log tests.tpcc_load
А потом указываем файл конфигурации, шаблон для отчёта, файл для журналирования и перечисляем те сценарии, которые будут запущены в тесте.
Что получилось в итоге
Pangoloader заметно упростил нам работу. Теперь мы можем:
запускать нагрузку одной кнопкой;
настраивать мониторинг на ВМках;
менять конфигурацию окружения, где тестируем, конфигурацию сценариев;
запускать несколько сценариев подряд;
собирать результаты по каждому виду теста и формировать подробные и удобные отчёты.
Мы продумывали наполнение итогового отчёта, чтобы потом в любой момент можно было повторить тот же тестовый сценарий и проанализировать результаты теста целиком.
Итоговый отчёт
Фиксируем время теста, версию тестируемого объекта, хеш коммита, с которого он был собран. Здесь же выводится таблица с конфигурацией тестового стенда. Если будет несколько хостов, то они будут перечислены по порядку.
Ниже — информация по сценарию нагрузки: какой тестовый сценарий был запущен, с какими параметрами. Схема тестовой базы данных, размер таблиц и количество записей в них, размеры индексов для таблиц.
И вот информация с результатами в виде графиков мониторинга — тут представлены графики системных метрик (CPU, RAM, disk), статистика интенсивности нагрузки (TPS, latency). Вся эта подробная информация скрыта под ссылками и открывается на отдельных страницах.
Планы: что хотим добавить в наш нагрузчик
Собираемся и дальше развивать Pangoloader. Самая интересная и сложная фича, которую хотелось бы добавить, — автоматизированный анализ отчётов. Было бы здорово, если бы при открытии отчёта проблемные места автоматически подсвечивались красным цветом вверху страницы. Этого можно достичь, например, сравнив показатели с эталонными значениями и выделив несоответствия. Или сравнив результаты с предыдущими тестами. Но пока это лишь размышления, мы ещё не проверяли идею на практике. Начнём пробовать — будем понемногу рассказывать об этом в сообществе нашей команды, присоединяйтесь, если интересно.
А если у вас есть такой опыт в автоматизации нагрузки, будем благодарны за комментарии. Спасибо за внимание!