нагрузочное тестирование сервера приложений
Открытые бенчмарки для нагрузочного тестирования серверов и веб-приложений
Это — подборка утилит, составленная на основе рекомендаций резидентов Hacker News и GitHub. В список вошли: Locust, Vegeta, Slow_cooker, k6 и Siege. Ими пользуются инженеры из DICE, EA и Buoyant, а также разработчики Kubernetes и Load Impact. Расскажем об этих инструментах.
Locust.io
Инструмент для нагрузочного тестирования сайтов. Все сценарии пишутся на Python. Специальный веб-интерфейс, построенный на Flask, позволяет мониторить результаты в реальном времени. Авторы Locust — швейцарские инженеры, среди которых числятся сотрудники компаний DICE и EA, занимающихся разработкой и изданием компьютерных игр.
В основу инструмента заложена интересная концепция: Locust («саранча») эмулирует поведение целого роя насекомых (виртуальных пользователей), «атакующих» сайт во время теста. Запросы формируют с помощью сетевой библиотеки для организации параллельных вычислений — gevent. Вот пример простого теста, который приведен на официальном сайте проекта:
Locust задействует библиотеку requests. Эта надстройка над стандартными средствами Python упрощает работу с HTTP и SSL и делает код более наглядным. К слову, документацию requests можно использовать в качестве шпаргалки для отладки тестов на Locust.
Этот инструмент для нагрузочного тестирования существует уже более семи лет. За это время вокруг него сформировалось обширное комьюнити — на GitHub более 10 тыс. звезд. Locust использовали при оценке работоспособности сети Battlelog для серии игр Battlefield. Об инструменте положительно отозвался Армин Ронахер (Armin Ronacher), автор фреймворка Flask.
Среди недостатков Locust выделяют довольно низкую производительность и периодические ошибки при оценке времени ответа сайтов. Также инструмент не умеет строить графики, но проблема решается выгрузкой результатов в виде CSV-файлов и отрисовкой графиков в редакторе таблиц.
Если вы хотите поближе познакомиться с Locust, то стоит обратить внимание на документацию инструмента. Также можно рекомендовать выступление Алексея Романова из Wargaming на Python Meetup. Он рассказывает, как писать сценарии, эмулирующие поведение пользователей.
Vegeta
Утилита командной строки для тестирования HTTP-сервисов, написанная на Go. Её можно подключить как библиотеку для создания своих инструментов нагрузочного тестирования. Разработчиком Vegeta выступил один из авторов отрытой платформы Sourcegraph — это движок для рецензирования и навигации по исходному коду, который используют в Lyft, Uber и Yelp.
Vegeta оценивает возможности сетевых ресурсов, «бомбардируя» их запросами с установленной частотой. Например, для проверки localhost достаточно прописать следующую команду:
По умолчанию Vegeta работает со стандартным потоком чтения команд (stdin), поэтому ресурс для тестирования передается через echo. Параметр duration указывает продолжительность теста. Репорт будет сгенерирован в файл results.bin. Отчеты Vegeta генерирует в текстовом формате, но при этом умеет рисовать графики. Сгенерировать их можно следующей командой:
Вокруг Vegeta сформировалось крупное сообщество — 12 тыс. звезд на GitHub. Инструмент даже использовали разработчики Kubernetes для оценки производительности своей платформы — тогда Vegeta генерировала около 10 млн запросов в секунду к кластеру из тысячи узлов.
Документация с описанием всех функций и флагов для тестов Vegeta есть в репозитории на GitHub. Там же можно найти предварительно скомпилированные исполняемые файлы.
Slow_cooker
Это — инструмент для нагрузочного тестирования серверов, написанный на Go. Его разработали инженеры из компании Buoyant, которая создает сервисную сеть для Kubernetes — Linkerd. Она является частью Cloud Native Computing Foundation и считается конкурентом Google Istio.
Фото — Joshua Aragon — Unsplash
Обычно утилиты для нагрузочного тестирования проверяют возможности сервера, направляя ему как можно больше запросов за короткое время. Авторы slow_cooker говорят, что их инструмент позволяет оценить работу железа под предсказуемой нагрузкой в течение длительного времени.
Специалисты из Buoyant используют свою разработку для тестирования Linkerd и других сервисов, например nginx. Инструмент довольно молодой — ему около трех — поэтому пока он не обзавелся большим комьюнити. Но в будущем ситуация может измениться, например, его репозиторий уже форкнули в Skysсanner, международном сервисе для поиска авиабилетов.
Исходный код вы можете найти на GitHub.
Инструмент для нагрузочного и регрессионного тестирования микросервисов, контейнеров и сайтов, размещенных в облаке. Он написан на Go и JavaScript разработчиками из Load Impact — это приложение для тестирования «стойкости» сайтов.
Работа с k6 строится по модели everything as code, когда логика тестов и все настройки пишутся на JavaScript. В скриптах отдельные шаги можно объединять в группы (group), что может быть удобно для тех, кто привык следовать принципам BDD. Вот пример такой группы:
Также инструмент дает возможность записывать скрипты и строить графики — последняя функция реализована на InfluxDB и Grafana. И у него есть интеграции с CI-системами вроде Jenkins, Circle CI, Team City и GitLab.
Пользователи говорят, что k6 не требователен к ресурсам, и у него удобный API. Но есть и несколько недостатков, в частности, k6 не поддерживает websocket и не умеет проводить тесты на распределенных системах. Хотя разработчики k6 в тематическом треде на Hacker News рассказали, что эти функции появятся в будущем.
Если вы хотите познакомиться с возможностями k6 самостоятельно, резиденты HN рекомендуют начать с технической документации — она подробная и с примерами. Если будут возникать какие-либо вопросы, можно обратиться на официальный форум.
Siege
Siege позволяет провести нагрузочное тестирование веб-серверов. Утилиту создал инженер Джефф Фалмер (Jeff Fulmer), чтобы разработчики могли проверить ресурсоемкость своего кода в условиях, приближенных к боевым. Siege эмулирует непрерывный поток обращений к сайту от множества пользователей, как бы удерживая сервер «под осадой» — отсюда и название инструмента.
После теста утилита показывает: время проверки, число транзакций за секунду, пропускную способность, количество удачных и неудачных запросов, а также их число с ответным кодом 200. Вот пример отчета, генерируемого Siege.
Siege получил довольно широкое распространение в ИТ-сообществе. Например, нагрузочному тестированию с его помощью посвящен целый раздел в книге «NGINX High Performance». Также его используют некоторые облачные провайдеры.
Из недостатков Siege можно выделить нестандартный синтаксис и неочевидные методы подсчета параметров тестирования — например, редиректы считаются успешными транзакциями, поэтому их количество может превышать общее число запросов. Если вы хотите опробовать Siege на практике, изучите онлайн-руководство — там разобраны некоторые «странности» системы.
Дополнительное чтение в блоге 1cloud.ru:
Что нового в Linux kernel 5.3 — графические драйверы, виртуализация и другие обновления
Почему разработчики мейнстрим-браузера снова отказались от отображения поддомена
Почему Apple изменила требования к разработчикам приложений
Нагрузочное тестирование веб-проекта — без купюр
Друзья, добрый день!
Продолжаем серию публикаций «без купюр» о проектах, связанных с разработкой, часто с приставкой «веб». Поговорим сегодня о нагрузочном тестировании. Проблема в том, что часто ни клиент, ни руководитель проекта не понимают, зачем оно нужно, какие риски оно позволяет снизить, как его организовать и как, а это самое, думаю, сложное, интерпретировать его результаты с пользой для бизнеса. Наливаем кофе и поехали…
Зачем нужно нагрузочное тестирование веб-проекта?
Дело в том, что если для удержания качества в некоторых веб-проектах еще пишут автотесты, то контролем производительности на стадии разработки мало кто занимается в принципе. Увидеть веб-проект и с автотестами и с бенчмарками кода — большая редкость. Гораздо чаще и по разумным причинам при разработке придерживаются следующих эвристик, обладающих хорошим соотношением польза-стоимость:
Возьмем кеширование. При разработке часто некогда задумываться над тем, как часто кэш может перестраиваться. А зря. Если перестройка кэша, скажем, каталога товаров, занимает длительное время и кэш сбрасывается при добавлении одного товара, то от кэширования будет больше вреда, чем пользы.
Именно поэтому, кстати, не рекомендуется использовать встроенный кэш запросов MySQL, страдающий от похожей проблемы: при изменении хотя бы одной записи таблицы кэш таблицы полностью сбрасывается (представим таблицу из 100к строк и абсурдность ситуации становится очевидна).
Аналогичная ситуация с запросами к MySQL. Если запросы выполняются по индексам, то, в общем случае, запросы будут выполняться… «быстрее». Можно верить, что время выполнения таких запросов логарифмически зависит от объема данных (O(log(n))). Но на практике часто оказывается, что одни запросы влияют на другие, используя одновременно общие подсистемы БД (сортировка на диске, который начинает тормозить) и сразу предвидеть это — нельзя.
Также часто при нагрузке выявляются любопытные особенности операционной системы, в частности, переполнение диапазона исходящих клиентских портов TCP/IP, при интенсивной работе с memcached. Или apache забивается запросами на обработку картинок, т.к. при конфигурации забыли настроить их обработку кэширующим прокси-сервером nginx.
Иногда забывают установить в MySQL путь для временных таблиц на диск, отображающий данные в оперативную память («/dev/shm»), из-за чего при возрастании нагрузки сервер БД ложится от интенсивных сортировок.
Также, при добавлении в веб-проект данных, в объеме, приближенном к боевому, запросы и алгоритмы начинают агрессивно проявлять свою «О-нотацию»: если cartesian для небольшого объема данных незаметен, то при появлении боевого объема сервер БД от напряжения становится красным.
Примеров можно привести еще массу, остановимся пока на этом. Главное понять, что нагрузочное тестирование — необходимо. Потому что заранее предусмотреть все возможные варианты «торможения» веб-системы среднего размера очень дорого, очень долго и экономически нецелесообразно.
Как определить целевые показатели нагрузочного тестирования?
Тут важно понять, что на самом деле покажет и вам и клиенту уровень качества веб-системы при нагрузочном тестировании. Нет ничего лучше конкретных примеров целевых показателей нагрузочного тестирования, плохих и хороших:
Все просто! Сейчас нарисую и покажу в прекрасной среде для анализа данных: Jupyter notebook/Python.
Допустим, на веб-сайт сделали 10 хитов с таким временем в миллисекундах:
Теперь отсортируем время выполнения хитов по возрастанию:
Мы в шаге от понимания медианы, 25 и 75 перцентелей. Все просто — разделим график пополам и в середине будет «медиана» (цифра 1 на графике). Первая четверть графика будет соответствовать 25 перцентилю (цифра 2 на графике) и третья четверть будет соответствовать 75 перцентилю (цифра 3 на графике). Соответственно получаются и другие перцентили (или, как их еще называют, квантили) — 90, 95, 99 и т.п.:
А так будет выглядеть распределение (гистограмма) по времени выполнения указанных выше хитов. Как видим, все очень наглядно и просто:
А вот так можно быстро построить распределение (гистограмму) по логу запросов нагрузочного тестирования. Модифицируйте под свой формат лога:
И получится примерно такая картина:
Надеюсь теперь все стало ясно и на свои места. Если нет, спрашивайте в комментариях.
Время проведения нагрузочного тестирования
Часто спрашивают — сколько времени должно продолжаться нагрузочное тестирование веб-проекта? Тут простая эвристика — в операционной системе нередко раз в сутки выполняются запланированные задания: бэкапы, ротация логов и т.п., поэтому время проведения нагрузочного тестирования должно быть не меньше, правильно, суток. Если веб-проект на Битрикс, то в платформе также выполняется немало запланированных в расписание заданий и желательно нагружать веб-систему не меньше суток.
Планирование распределения нагрузки
Если уже есть эксплуатируемый веб-сайт, то можно, да, взять логи посещения оттуда и нагружать новую веб-систему, используя их. Но часто решают задачу нагрузки только разрабатываемой веб-системы. Для планирования распределения нагрузки часто хорошо подходит модель разделения предполагаемых цепочек посещения сайта на доли. Например:
Расчет интервалов и нагрузочных потоков несложно сделать в Excel или на листике карандашом.
Структура нагрузочной цепочки
Тут важно учесть особенности жизненного цикла пользователя веб-системы. Часто пользователи авторизуются, а потом ходят по веб-сайту. Для этого в начало нагрузочной цепочки нужно поместить действия, приводящие к авторизации:
Коню ясно, что нельзя при нагрузочном тестировании дергать только одну детальную страницу каталога, поэтому полезно считывать и ротировать их список из CSV-файла:
Между хитами, разумеется, нужно делать случайные паузы — так мы ближе приблизимся к нагрузке, создаваемой реальными пользователями. Не забываем также о сохранении и возвращении на сервер значений cookies:
Глобальные переменные нагрузочных цепочек, в том числе их число потоков, настраиваются просто. Определенные глобальные переменные можно использовать затем в разных местах нагрузочных цепочек:
Как сделать так, чтобы нагрузочное тестирование благополучно закончилось?
На практике, почти всегда, нагрузочное тестирование в первые минуты-часы обрушивает веб-систему, все начинает дымиться, затем гореть, сайт не открывается, MySQL падает в своп и не дает к себе подключиться, LA на серверах приближается к 100, разработчики начинают бегать со словами «это не должно было произойти», а сисадмины с ухмылкой обычно отвечают «справедливость в жизни есть!» и начинают пить пиво в серверной.
Но чтобы понять, почему все упало и что чинить, чтобы через сутки показать клиенту результаты «успешного» нагрузочного тестирования, необходимо предварительно включить запись основных метрик жизнедеятельности операционной системы — это легко сделать в бесплатных продуктах класса munun/cacti.
Перечислю, что происходит при коллапсе веб-системы чаще всего и как это можно исправить.
Прежде всего «забивается» запросами веб-сервер apache или php-fpm:
Чаще всего это происходит из-за коллапса MySQL — вырастает число висящих потоков запросов:
Чем это обусловлено? Часто сверху забывают забивают ограничить число apache или потоков запросов к МySQL, что вызывает выпадение приложений из оперативной памяти в медленный своп с конвульсиями:
Тут видна внезапная активность при работе со свопом, нужно разбираться, кто выпал в своп и откуда:
Однако, иногда проблема оказывается на стороне медленной дисковой подсистемы. В этом случае резко вырастает LA и процент утилизации диска приближается к 100 (правый нижний график):
Очевидно, что я вскрыл только часть самого интересного, что может начаться с веб-проектом при нагрузочном тестировании. Но ведь главное — задать верное направление и выстроить правильный процесс. Спрашивайте в комментариях, что повылазило у вас при нагрузке, постараюсь помочь.
Интерпретация результатов нагрузочного тестирования
Обычно после 5-10 перезапусков и корректировок, нагрузочное тестирование начинает свой полет и успешно завершается. В результате у вас должен быть набор примерно таких логов для дальнейшего анализа:
Имея эти артефакты, вы можете, используя простой awk-скрипт в начале поста, построить распределения (гистограммки) по этим логам и посчитать число и типы HTTP-ошибок. По сути, вы можете сформировать очень емкий и полезный для бизнеса и принятия решений отчет об успешности нагрузочного тестирования примерно такого содержания:
В течение суток сделан 1 млн. хитов. 25% хитов сделаны менее, чем за 50 мс, 50% хитов сделаны менее, чем за 0.5 сек (медиана), 75% хитов сделаны менее, чем за 1 сек, 95% хитов сделаны менее, чем за 5 сек, число ошибок HTTP — 0.01%. Тестовые данные: каталог, пользователи, новости, статьи были залиты в объеме, приближенном к ожидаемому.
Один разработчик — застрелился.
Главная — Новости — Детальная новости = 50%
Главная — Обзор каталога — Детальная каталога = 30%
Детальная каталога — Обзор каталога — Детальная каталога = 15%
Результаты поиска — Детальная каталога = 5%
Графики использования ресурсов серверов:
…
Это уже хороший и понятный отчет о нагрузочном тестировании веб-системы. Для любителей острой боли еще можно рекомендовать при нагрузочном тестировании включить ежеминутный импорт-экспорт данных на веб-сайт из систем класса SAP, 1C и т.п. и синхронные соединения по TCP/IP сокетам с внешними сервисами курсов, скажем, криптовалют 🙂
Но, скажу честно, если импорт-экспорт сделать аккуратно, по совести, то нагрузочное тестирование и при таких условиях покажет приемлемые для бизнеса цифры.
Откуда берутся ошибки при нагрузочном тестировании?
Кстати да, мы не осветили этот момент. Из банальных причин обычно всплывает отсутствие балансировки между nginx — apache — mysql воркерами. Т.е. воркеры сверху не ограничивают, в результате в apache может подняться сразу 500 воркеров (каждый иногда по 100 МБ) и на MySQL прийдут сразу 500 потоков с запросами — что вызовет всплеск HTTP 50х ошибок и возможный коллапс.
Тут рекомендуется ограничить число apache/php-fpm воркеров до числа, умещающегося в ОЗУ и, аналогично, ограничить число потоков на MySQL, для защиты от переполнения доступной оперативной памяти. Идея проста — пусть клиенты ждут перед nginx, немного может замедляясь на асинхронных и неблокирующих TCP/IP сокетах, чем «ломятся» сразу в apache/MySQL.
Из более неприятных причин тут может быть segfault PHP. В этом случае необходимо включить сбор coredump и с помощью gdb посмотреть, почему это происходит. В большинстве случаев через обновление/конфигурацию PHP проблему удается обойти.
Что осталось за кадром
Ходят упорные слухи, что современный фронтэнд для веба так активно зажил своей жизнью, что классическое нагрузочное тестирование бэкэнда, приведенное в данном посте, уже не закрывает всех возможных рисков зависания построения веб-страницы в «потрохах» Angular/React/Vue.js — поэтому не используйте тяжелый и непрозрачный, плохо тестируемый фронтэнд можно, при необходимости, адаптировать нагрузочные цепочки и к такой ситуации.
В любом случае, если результаты нагрузочного тестирования бэкэнда показали хорошие цифры, а веб-сайт продолжает тормозить в браузере, уже понятно, кого «бить по наглой рыжей морде» 🙂
Если серьезно, то в ближайших постах мы надеемся осветить и эту важную тему.
Итоги и выводы
Итого — нет ничего сложного в организации и проведении полезного для разработки и бизнеса нагрузочного тестирования веб-системы.
Для проведения нагрузочного тестирования важно привлекать не только разработчиков, но и экспертов по операционным системам и железу — опытных системных администраторов, и тогда проблемы «выпадения в своп» или «переполнения локального диапазона IP-адресов» не вызовут кровотечения из глаз и обмороков.
Удачи, друзья и задавайте вопросы в комментариях!
Нагрузочное тестирование: с чего начать и куда смотреть
Вы наверняка знаете, что есть большая разница между тем, как будет работать ваше приложение/сервис в зависимости от того, сколько пользователей его используют. То, что работало во время разработки, может развалиться, когда придут первые реальные пользователи со своим окружением, а то, что работало с сотней пользователей, может умереть, когда их станет 10 тысяч. Или бывает, что вы все потестили на искусственных данных, а потом ваша база начинает торзмозить из-за пользователя с именем İnari.
О том, как выживают баги, когда «включать» в проекте нагрузочные тесты, откуда брать для них данные и можно ли вообще не тестировать, вывалив результаты сразу в продакшн, мы поговорили с Алексеем Лавренюком («Яндекс») и Владимиром Ситниковым (Netcracker).
Пару слов о себе и своей работе. Как ваша работа связана с тестированием?
Алексей Лавренюк: Я разработчик в «Яндексе», в службе нагрузочного тестирования. Делаю инструменты и сервисы для тестирования производительности. Можно посмотреть на наши open-source инструменты для нагрузочного тестирования – Yandex.Tank и Pandora, и на наш сервис для нагрузочного тестирования – Overload. Сейчас открыт бесплатный доступ к его бета-версии.
Раньше мы умели тестировать только производительность серверных приложений, теперь осваиваем мобильные приложения – это новый тренд. Например, мы тестируем энергопотребление телефонов. Прочитать про это можно тут.
Владимир Ситников: Я уже 12 лет работаю в компании Netcracker. Сейчас я занимаю позицию инженера по производительности, т.е. я не тестирую продукты, но наблюдаю, как ведет себя система в реальной эксплуатации и в тестах. В сферу моей ответственности входит анализ того, как те или иные решения в сфере разработки и дизайна влияют на финальный результат. Поэтому я зачастую не просто смотрю на результаты тестирования, но и планирую его. В основном это относится к нагрузочному тестированию.
Когда-то я и сам занимался тестированием, но сейчас это осталось в прошлом.
— Давайте немного поговорим о теории. Корректно ли говорить, что проблемы на стадии тестирования появляются из-за разработчиков, не учитывающих какие-то параметры задачи или использующих неподходящее решение?
Алексей Лавренюк: Нельзя все предугадать заранее. В типичном сервисе очень много крутилок: для решения продуктовых задач можно выбирать разные алгоритмы, подбирать параметры этих алгоритмов, использовать библиотеки, фреймворки, настройки сервиса в продакшне, параметры железа. В сочетании эти крутилки дают миллиарды комбинаций, и без глубокого понимания того, как работает сервис, перебирать их можно очень долго. А понимание это нельзя получить, если у тебя нет инструмента для проведения экспериментов и анализа их результатов.
Нагрузочное тестирование — это не дубинка, которая бьет разработчиков по голове, когда они написали непроизводительный код. Это очень мощный измерительный прибор, по сути осциллограф для электронщика, позволяющий прощупать код и найти в нем узкие места. А потом после оптимизации увидеть (и продемонстрировать) результат в цифрах и графиках.
Владимир Ситников: Я бы хотел дополнить. «Откуда вообще возникают баги» — это вопрос тысячелетия. И он тесно связан с другим интересным вопросом — почему, несмотря на все тестирование, баги доживают до реальных систем и появляются только там. Почему в ходе тестирования мы их не находим? Неужели мы неправильно ставим задачу?
На мой взгляд, это связано с тем, что команды разработки и поддержки — разные. Одни люди делают решение и совсем другие — занимаются его поддержкой в ходе эксплуатации.
Половина всех проблем на продакшене — это сочетание 2-3 дурацких ошибок или допущений разработки: кто-то создал неправильный код, использовал дурацкий алгоритм и т.п. Каждая такая ошибка сама по себе не сказывается на производительности — современное железо довольно мощное, много чего переваривает. Но вместе две или три ошибки «выстреливают». А из-за разделения разработки и поддержки авторы этих ошибок о них и не узнают.
Редко когда по факту ищут авторов кода и говорят: «Пожалуйста, так больше не надо писать». Но чтобы гарантированно не совершать подобных ошибок в будущем, разработчику надо получить этот опыт. При поддержке своей собственной системы выводы из «набитых шишек» делались бы быстрее.
— Есть ли смысл в раннем нагрузочном тестировании (на этапе разработки)?
Алексей Лавренюк: Самые дорогие ошибки получаются тогда, когда нагрузочное тестирование притащили за уши на готовый проект (я уже не говорю о тех случаях, когда код тестируют в продакшене). Код написали, функционально он работает, но выясняется, что там, где требуется 10 ответов в секунду (всего-то), сервис может переварить только один, да и то со скрипом. Еще хуже, если в этом виноват фундамент – фреймворк, который выбрали, потому что «им все пользуются» или «ну он такой новый, прикольный». То есть придется переписывать вообще все.
Чем раньше отлавливаются проблемы, тем проще их решить.
— То есть начинаем разрабатывать и одновременно запускаем тестирование?
Владимир Ситников: И да, и нет. Иногда на ранней стадии надо совсем грубо посмотреть, что происходит. Это может иметь смысл. Но, как правило, вначале код не работает. А замерять производительность кода, который, допустим, возвращает неправильный ответ, — гиблое дело. Мы тратим время, ресурсы (в том числе машинные), а результат — неизвестный.
Однако запуск тестирования — история довольно длительная. Перед тем как что-то замерить, нужно понять, что именно замерять и на каких данных, закончатся ли у нас эти данные по ходу теста, как мы будем их возобновлять, за какими метриками будем следить, какие у нас есть ожидания относительно результатов и т.п. В совсем небольшом проекте все это можно провернуть за день-два, просто обсудив и приняв нужное решение. Но для более-менее крупного проекта подобные вопросы за один день не решаются, тем более тут требуется тесное взаимодействие с заказчиком.
Бывают случаи, когда заказчик приходит с определенными требованиями с самого начала, но чаще их приходится формулировать только на этапе «подключения» нагрузочного тестирования.
Поэтому думать о нагрузочном тестировании стоит с самого начала. И я бы не рассматривал нагрузочное тестирование как конечный продукт. Это некий процесс, которому надо следовать, чтобы избежать определенных ошибок. По мере создания решения (в процессе перехода непосредственно от разработки к тестированию и выводу на продакшн) меняется и назначение нагрузочного тестирования. Сначала оно используется для отладки, затем — для поиска ошибок, и в конце — как критерий того, что решение готово к продакшн.
Здесь мне кажется уместной аналогия с обычным тестированием. Когда его надо включать? Отнюдь не в конце разработки. То, что в конце разработки придут тестировщики и все исправят, — это миф. Тестирование не исправляет, а ищет ошибки. И нагрузочное тестирование — это инструмент поиска ошибок определенного рода. Однако в отличие от обычного тестирования, проверяющего по сути только возможность прохождения программы по ветке алгоритма до определенной точки, нагрузочное представляет собой бета-тест продакшна, что ведет к отличиям в данных, нагрузке, соотношении тестов в миксе и т.п.
Чтобы успешно находить ошибки, надо иметь критерии поиска. Поэтому как процесс нагрузочное тестирование запускается ближе к началу разработки, а по факту оно проходит условно после первого цикла тестирования (автоматического или ручного), когда кто-то дает отмашку, что в целом система ведет себя правильно.
— Понятно, что абсолютно все не протестировать. Какие моменты необходимо тестировать в первую очередь (в ракурсе нагрузочного тестирования)?
Алексей Лавренюк: Тестировать в первую очередь надо критический сценарий — то есть тот, который приносит деньги. И провести как минимум два вида тестов: на разладку, чтобы определить пределы производительности, и на измерение таймингов, чтобы убедиться, что сервис укладывается в SLA. То есть сервис обязательно нужно «добить» и померить тайминги на том уровне нагрузки, который предполагается в продакшн.
— С чего необходимо начинать нагрузочное тестирование?
Алексей Лавренюк: Перед тем, как начинать нагрузочное тестирование, нужно убедиться, что перед этим провели функциональное и поправили все баги. Причем именно на вашем стенде. Удостоверьтесь, что в середине вашей стрельбы к вам на стенд никто не придет, чтобы скачать пару сотен гигабайт. В общем, подготовьте удобное тестовое окружение, в котором вам никто не будет мешать.
Владимир Ситников: Само тестирование стоит начинать с формулирования нефункциональных требований — т.е. требований по производительности и стабильности.
Наиболее типичные нефункциональные требования (важнейшие количественные оценки приложения) — размер обрабатываемых данных, время их обработки и частота запусков. Какая-либо из этих метрик встречается почти в каждом проекте.
Понятие «нефункциональные требования» обширно. В разных проектах их составляющие могут иметь различный приоритет. Например, «система должна быть способна отработать 3 дня без перезагрузки» или «в случае пропадания и восстановления связи с базой данных приложение должно вернуться к нормальной работе не более чем за 2 минуты» — это тоже нефункциональные требования.
К слову, понятие «нефункциональные требования» формирует представление, что это нечто самостоятельное. Но по факту эти требования, описывающие, как должен работать функционал. Т.е. без нефункциональных требований и функциональные теряют смысл, равно как невозможно приложить NFR к любому случайно выбранному функциональному требованию.
— Существуют ли какие-то общие рекомендации по проведению нагрузочного тестирования?
Владимир Ситников: В целом нагрузочное тестирование похоже на обычное. Мы берем наиболее важные сценарии или те, за которые мы боимся больше всего. Поэтому кто-то с пониманием проекта должен прийти и указать на наиболее опасные сценарии. Дальше мы уже занимаемся автоматизацией и проводим замеры.
— Насколько большую роль играет интерпретация результатов нагрузочного тестирования?
Владимир Ситников: Огромную. Важны не цифры, а понимание, почему они именно такие. Если у нас получилось 42, это не значит, что результат — хороший. Заказчик просил, чтобы работало не дольше минуты, а у нас получилось полминуты. Мы молодцы, расходимся? Нет! Важно понимать, почему мы не могли сделать быстрее, во что мы упираемся. А еще надо быть уверенным в том, что отчет — реален.
Был такой пример: мы замеряли, как влияет виртуальная машина на производительность приложения, т.е. сравнивали производительность приложения, работающего на железе, и приложения, работающего на виртуальной машине. Сделали замеры, получили хорошие цифры (близкие к ожидаемым) — расхождение в производительности в несколько десятых процента. На этом все могло закончиться. Но кто-то внимательнее посмотрел на результаты и понял, что у нас вместо запуска приложения возвращалась страница с ошибкой логина. В тестах мы не прошли по реальному приложению. Вместо этого под видом каждого из шагов мы проверили скорость логина, который на неправильном пароле выдал отказ.
О чем говорит этот пример? О том, что без анализа того, что творилось внутри во время тестов, у нас возник неправильный вывод. Поэтому с точки зрения нагрузочного тестирования важны не полученные цифры, а понимание того, чем эти цифры объясняются.
Алексей Лавренюк: Очень часто люди недооценивают важность графиков, смотрят только на итоговые статистики. Если вы тоже так делаете, рекомендую погуглить «квартет Энскомба» и посмотреть вот эту статью от Autodesk.
Многие популярные в интернете нагрузочные инструменты, например ab, дают только итоговые статистики и ложную уверенность в работоспособности сервиса под нагрузкой. Они скрывают детали, провалы на графиках. Такой провал может стоить денег (покупатель ушел), а исправить его очень просто (поправить параметры garbage collector).
Кроме того, многие популярные и дорогие нагрузочные инструменты архитектурно устроены неправильно и лгут вам. Об этом написано в этой статье.
— Какие есть особенности нагрузочного тестирования веб-сервиса? По идее тестировать надо не только код, но и окружение, как это делается?
Алексей Лавренюк: Тестировать надо все, что вас интересует. Если вы подозреваете, что производительность приложения зависит от пробок в Москве, найдите способ это протестировать. Я не шучу, наши геосервисы запускают автоматически нагрузочные тесты на данных о пробках, когда их уровень достигает 7 баллов.
Еще у нас на тесте было мобильное приложение, которое активизировалось только в том случае, если телефон приходил в движение. То есть телефон во время теста нужно было шатать. Поэтому эти телефоны мы носили с собой. Была даже идея построить специальную шаталку телефонов, но приложение отправили на доработку, и необходимость в автоматизации пока отпала.
— Откуда берутся данные для нагрузочного тестирования вашего типичного проекта? Используются ли моки или данные с продакшн?
Владимир Ситников: Я бы не сказал, что к данным есть какой-то типичный подход. Проекты все-таки разные. Есть такие, где системы вообще не хранят данные, а являются промежуточными звеньями в некой цепочке. Для их тестирования мы просто генерируем необходимую информацию. Иногда, наоборот, системы что-то хранят. Если это, например, история записей, проблем, вроде как, нет. Но если системы хранят какое-то состояние (БД и т.д.), то здесь проявляются нюансы.
На начальном этапе, когда у нас нет никакого дампа продакшена, данные приходится генерировать (обсуждать бизнес-структуру и генерировать нужное количество подходящего сорта данных). К сожалению, я бы не сказал, что это успешно работает. Сгенерированные данные позволяют пережить какое-то время, но, к сожалению, получить таким образом данные, похожие на правду и подходящие для разных сценариев, тяжело. И начинаются игры, когда мы генерируем часть данных для одного типа запросов, часть — для другого, часть — для третьего. Нам удобнее генерить частями, но при этом получаются перекосы: либо данных слишком много (потому что мы по каждому типу сценариев хотим иметь запас), либо мы пропускаем какие-то сценарии. Из-за этого рано или поздно мы переходим на нечто более-менее похожее на дампы — импорт из внешних систем или продакшн с маскированием.
С импортом тоже есть свои проблемы. Мы работаем в телеком-сегменте, где дампы продакшн-систем нельзя копировать направо-налево. Там присутствуют определенного типа данные, которые просто так копировать нельзя. Поэтому приходится изобретать, как сделать так, чтобы система вроде бы работала, но данные были заменены на звездочки и т.д.
— Не проще тестировать сразу на продакшене, подключая какую-то долю пользователей?
Алексей Лавренюк: В нашем случае это не тестирование, это аккуратный релиз. По-хорошему, всегда, даже после тестирования, нужно выкатывать на долю пользователей, потому что в продакшне может пойти все не так, как в тестинге.
Чем подключение доли пользователей отличается от нагрузочного тестирования? В этом случае у вас нет возможности полностью управлять нагрузкой, вы не можете довести сервис до предела (пользователи просто 500 ошибку начнут получать), вы не можете безнаказанно крутить на нем ручки и замониторить столько, сколько могли бы на тестовых серверах.
С другой стороны, можно исхитриться: часть трафика с продакшн направить на ваши тестовые сервера и там использовать нагрузочные инструменты. Вот это уже нагрузочное тестирование, просто исходные данные берутся в реалтайме с продакшн окружения.
Иными словами, если вас устраивает ответ «держим/не держим» (причем с вероятностью разочаровать какую-то долю своих пользователей), то можно и не тестировать нагрузочно. Если же вы хотите знать пределы производительности, узкие места, в какие мониторинги лучше смотреть, за какие крутилки в первую очередь хвататься и к кому бежать – то все-таки сделайте нагрузочное тестирование.
Владимир Ситников: Это не наш случай. Существуют компании, которые без проблем тестируют в продакшене. У них есть часть клиентов, которая может быть перенаправлена на новую версию кода. В нашем случае речь идет о системе, важной для бизнеса наших заказчиков. Если она не работает, заказчик теряет деньги. Поэтому и нагрузочные тесты проводим до попадания кода в эксплуатацию.
— Есть ли у вас какой-то излюбленный инструментарий для проведения нагрузочного тестирования?
Владимир Ситников: Конечно. У нас 3 основных инструмента: для тестирования серверной части мы используем Apache JMeter, для тестирования браузера мы используем Selenium и для тестирования Java — JMH. Эти инструменты закрывают подавляющее большинство потребностей.
То, какие инструменты для тестирования предпочитает использовать Алексей Лавренюк, вы можете узнать у него лично на конференции по тестированию Гейзенбаг 2017 Piter. Перед тем, как побеседовать со всеми желающими в дискуссионной зоне, Алексей выступит с докладом о нагрузочном тестировании web-проектов, где в режиме real time show будет «обстреливать» демонстрационный web-сервис на Python Tornado, специально написанный так, чтобы проявились проблемы производительности, анализировать отчеты нагрузочных тестов, исправлять «узкие места», а затем сравнит производительность «до» и «после».
Описание этого и других докладов, которые ожидают вас на Гейзенбаг 2017 Piter, смотрите на сайте конференции.