Всем привет!

Меня зовут Дмитрий Винокуров и я работаю инженером по нагрузочному тестированию в Miro. Я хочу рассказать о личном опыте и опыте нашей команды в развитии направления нагрузочного тестирования (для краткости НТ). В статье я расскажу самые основы НТ, как на эти основы ложится наш процесс и на какие конкретные шаги он делится. Наш опыт местами может быть специфическим, но по большей части он будет применим ко многим компаниям, разрабатывающим веб-приложения и не только.

Новичкам статья полезна будет тем, что содержит введение в нагрузочное тестирование и базовый план действий, а опытным людям покажет пример того, как делают НТ в другой компании, и даст возможность почерпнуть что-то новое для себя или наоборот даст повод поделиться своим опытом, поэтому буду рад комментариям и примерам из вашего опыта.

Описываемый подход основывается на нашем опыте и множестве просмотренных и прочитанных материалов. Подобный доклад делался на конференции DUMP в Екатеринбурге 14 мая 2021 г., эта статья представляет собой значительно дополненный и переработанный вариант выступления.

Автор арта на «обложке» — Orest Terremoto.

Содержание


  1. О Miro и об авторе
  2. Что такое нагрузочное тестирование?
  3. Что нужно для нагрузочного тестирования?
  4. Этапы нагрузочного тестирования
  5. С чего начинали нагрузочное тестирование в Miro
  6. С какими препятствиями столкнулись при развитии нагрузочного тестирования в Miro
  7. Роли, которые в Miro решили воспитывать в инженерах команды нагрузочного тестирования
  8. Некоторые общие принципы работы QA в Miro
  9. Распределённая команда нагрузочного тестирования в Miro
  10. Процесс нагрузочного тестирования в Miro
    1. Детализация требований
    2. Подготовка тестового окружения
    3. Подготовка сценария
    4. Запуск теста и измерения
    5. Анализ
    6. Отчёт
  11. Результаты внедрения нагрузочного тестирования в Miro
  12. Планы в отношении нагрузочного тестирования в Miro
  13. Пожелания читателям
  14. Полезные ссылки

О Miro и об авторе




Наша компания разрабатывает веб-приложение Miro — онлайн платформу для совместной работы с множеством интеграций. В основе используется бесконечная интерактивная доска, на которую можно добавлять текст, схемы, изображения, стикеры, таблицы, эмоджи, майндмапы, связывать с тикетами в Jira, создавать канбаны и многое другое. Приложение содержит удобные инструменты для проведения мозговых штурмов, воркшопов, планирования проектов, дизайна новых продуктов и сервисов, фасилитации Agile встреч, презентаций и многого другого.



Цель Miro — дать возможность распределённым командам работать так эффективно, как будто они находятся в одном помещении.

Что особенно важно относительно темы статьи — Miro поддерживает совместную работу в реальном времени большого числа пользователей, в этом самая основа нашей платформы. Это причина, почему нам особенно нужно нагрузочное тестирование. Когда люди собираются у одной оффлайн доски в офисе — они не испытывают никаких задержек в коммуникации и в работе с доской, так же и у нас, только в намного больших масштабах.

В прошлом году внезапно множество людей перешло на удалёнку по известным причинам и им понадобились инструменты для удалённой работы, подобные нашему. Количество зарегистрированных пользователей в Miro с начала 2020 до весны 2021 выросло в 8 раз, другие метрики росли схожим образом. Поэтому нам понадобилось срочно развивать все имеющиеся наработки по нагрузочному тестированию. Причём это не краткосрочная задача, а, учитывая перспективы рынка, весьма долгоиграющая. Что получилось и что ещё предстоит — об этом и будет статья.

Сам я уже около 12 лет занимаюсь разработкой ПО и последние несколько лет — разработкой инструментов автоматизации тестирования. Ещё я активно участвую в работе над публичной базой знаний по НТ вместе с соответствующим сообществом, ссылки про это и многое другое будут в конце статьи.

Что такое нагрузочное тестирование?


Нагрузочное тестирование — это тип тестирования, в котором мы проверяем, соответствует ли наша система поставленным нефункциональным требованиям к производительности при работе под высокой нагрузкой в различных сценариях. Это намного сложнее, чем unit тестирование, где всё просто работает или нет. При нагрузочном тестировании снимается целый ряд метрик, и даже задача проверки на соответствие требованиям уже является непростой. Существенные трудности состоят также в том, как воспроизвести поведение пользователей с прода, как воспроизвести сам прод, в том числе базу, и как поставить тестирование так, чтобы ручная работа была минимальной.

Тестирование вообще — это способ узнать об ошибках не от конечных пользователей, а раньше, и тем самым избежать потерь, репутационных и прочих. Нагрузочное тестирование — это способ подготовиться к высоким нагрузкам заранее и сделать так, чтобы большое число запросов от множества пользователей было благом, а не головной болью. Причин для бурного роста может быть множество и отказываться от подготовки к таким сценариям значит существенно повышать риск выхода из строя вашей системы в тот момент, когда она стала особенно нужна людям.

Что нужно для нагрузочного тестирования?


Итак, мы определились, НТ нам нужно, теперь перед его проведением необходимо наличие ряда ресурсов:

  1. Общие требования. Нам нужно понимать, чего мы ожидаем от нашей системы в контексте нагрузки и производительности, хотя бы в общих чертах. Это может звучать просто, но не всегда у людей есть ясное представление о таких требованиях. «Система должна отвечать быстро под высокой нагрузкой» — это не требование, это пожелание. Я покажу примеры удачнее дальше в статье.
  2. Специалисты. Для проведения НТ нужны инженеры. Поскольку серьёзное тестирование всегда требует автоматизации, нужны инженеры с навыками тестирования и разработки, а также аналитики и девопсы.
  3. Платформа для тестового окружения. Если вы делаете НТ в первый раз — вам определённо понадобится подготовить инфраструктуру. Хорошо если у вас внедрён подход IaC (англ. infrastructure as a code, инфраструктура как код), он очень пригодится для создания окружения, подобного проду. Если нет — надо внедрять или придётся страдать даже на небольших конфигурациях. И в подавляющем большинстве случаев тестирование на проде — плохая идея. Но есть и исключения, можете погуглить «Netflix testing in production».
  4. Время. НТ — это очень затратный по времени процесс, особенно когда вы делаете его впервые. Автоматизация экономит много времени, но всё равно нужно быть готовым потратить на подготовку и проведение НТ несколько дней или существенно больше, зависит от масштабов прода, используемого стэка, сценариев использования и поставленных задач.

Этапы нагрузочного тестирования


Мы собрали то, что необходимо для НТ. Перейдём к плану действий.

  1. Детализация требований — что требуется от системы в терминах нагрузки и производительности, какой тип теста проводим и какие метрики необходимо собрать для проверки требований. Как я писал ранее, общие требования нужны ещё до тестирования, а когда процесс начат, нужно их декомпозировать в предельно конкретные и технические требования. Также нужно выбрать тип теста, реализуемого в тестовом сценарии, и составить список метрик, на сбор которых нужно настроить приложение, сценарий и инфраструктуру. Это аналитическая задача.
  2. Подготовка тестового окружения — где будем моделировать прод и пользователей. Необходимо выбрать одно из доступных или создать новое тестовое окружение под разработанные требования и настроить сбор метрик, нужных для оценки степени соответствия требованиям. Это задача DevOps специалистов.
  3. Выбор инструмента тестирования и подготовка сценария — как будем моделировать поведение пользователей. В сценарии тоже нужно запрограммировать сбор метрик. Это задача разработки.
  4. Проведение теста и замеры — каковы значения метрик. Это может звучать просто, но весьма редко всё работает с первого раза. Бывает нужно откатиться на один из предыдущих шагов и что-то поправить. На данном этапе проводится анализ некоторых ключевых показателей, чтобы убедиться, что нет критических ошибок теста. Этот шаг — задача тестирования. Основная работа по анализу будет вестись на следующем этапе.
  5. Анализ — соответствует ли система требованиям, есть ли проблемы. Если собираются правильные метрики, всё должно быть относительно просто, хотя и трудоёмко, а если нет — опять может понадобиться откатиться вплоть до первого шага. Здесь, в отличие от предыдущего шага, мы глубоко погружаемся в большое количество метрик чтобы выявить все возможные значимые факты. Это аналитическая задача.
  6. Подготовка отчёта — какие знания мы получили?.. Это самый важный этап. НТ — процесс очень затратный по ресурсам и вы явно захотите получить максимум знаний от него. Снова аналитическая задача.

Есть ещё один этап, опциональный — план улучшения системы, то есть ответ на вопрос «как можно исправить обнаруженные недостатки?». Это не относится напрямую к нагрузочному тестированию, но упомянуто здесь, потому что результаты НТ в большинстве случаев должны быть «actionable», побуждать к действиям. Как видно из списка выше, нужны разные роли для проведения всего процесса НТ и эти роли должны сочетать в себе или одни люди, или нужно привлекать разных специалистов.

С чего начинали нагрузочное тестирование в Miro


К росту нагрузки мы начали готовиться задолго до пандемии и вот что у нас было:

  1. Несколько человек занимались НТ часть своего времени.
  2. Мы использовали JMeter как наиболее популярное решение. У нас был свой плагин для WebSocket, так как ключевой функционал Miro связан с работой с досками и требует очень быстрого клиент-серверного взаимодействия, как в играх. Кстати движок, с которого много лет назад начинала компания, был как раз игровым. Это с самого начала дало высокую интерактивность и скорость отклика.
  3. Наш кластер был prod-like. К счастью, у нас был IaC. Нельзя сказать, что IaC — это серебряная пуля, сложности с ним тоже есть, но без него сложности с ростом масштаба становятся фатальными препятствиями.
  4. Один достаточно объёмный и сложный WebSocket&HTTP API тест.

Ссылки на подробности про предыдущие наработки и многое другое будут в конце статьи.

С какими препятствиями столкнулись при развитии нагрузочного тестирования в Miro


НТ, особенно для сложной системы, — задача непростая. Чем глубже мои коллеги погружались в неё, тем больше возникало сложностей.

Технические препятствия:

  1. JMeter перестал нас устраивать. В нём было очень неудобно программировать и отлаживать. Сложные сценарии — это почти всегда разработка, а не заполнение форм или написание отдельных небольших скриптов. Сценарий в JMeter — это не программа в обычном понимании, а сочетание инструкций, заполненных в формах, с встроенным кодом скриптов. Также в JMeter плохо с контролем версий, т.к. сценарий хранится в огромном XML. А у нас большое количество WebSocket взаимодействия со своим бинарным протоколом и достаточно сложные сценарии, над которыми работает несколько человек. Не хотелось изобретать обходные пути вокруг «особенностей» JMeter.
  2. Много ручных действий для каждого запуска НТ на тестовом окружении. TODO лист состоял из 16 пунктов для каждого запуска, которые надо было проделывать вручную… И это была боль… А боль — это сильный стимул что-то поменять.
  3. Не хватало инструментов для генерации разных распределений тестовых данных. А сценарии тестирования для разных компонентов возможны разные и нужно было уметь быстро создать нужное распределение. Идеально использовать базу максимально близкую проду и откатывать после каждого теста, но не всегда это возможно: это могут быть огромные объёмы и надо чистить сенситивные данные пользователей, а воспроизводить синтетические данные в нужном объёме — тоже сложная задача.

Организационные препятствия:

  1. Много задач.
  2. Недостаточно опыта.
  3. Недостаточно доступных людей.

Роли, которые в Miro решили воспитывать в инженерах команды нагрузочного тестирования


Итак, мы столкнулись с рядом трудностей в проведении НТ и конечно можно было подумать «а давайте просто наймём больше QA инженеров» и действительно надо было нанимать людей специально для НТ, но вопрос на самом деле шире. QA инженеров нанять недостаточно, учитывая сложность этого направления необходимо было строить многопрофильную команду. Причём часть ролей отводилась будущим сотрудникам, а часть — имеющимся.

Так почему нам было недостаточно просто QA? Главная причина — прогнозируемый уровень сложности задач по НТ. У нас несколько десятков команд, все разрабатывают компоненты, которые должны работать в соответствии с требованиями по производительности в составе сложной системы. Но у каждой компании свой масштаб разрабатываемых систем. Кому-то может хватить просто одного человека, который умеет в JMeter, и может даже особо большого кластера для подачи нагрузки не понадобится. Кому-то нужно будет значительно больше, чем надо было нам. Всё зависит от уровня сложности системы.

С ростом скорее всего происходит более узкая специализация, но у нас для инженеров НТ было решено развивать следующие роли:

  1. QA. Должен уметь искать проблемные места и выстраивать процесс обеспечения качества.
  2. Аналитик. Должен уметь выявлять причинно-следственные связи, исследовать паттерны поведения пользователей и разбираться в сложных системах.
  3. Разработчик. Должен уметь проектировать и программировать средства автоматизации и тестовые сценарии.
  4. Преподаватель*. Должен уметь помогать учиться специалистам из других команд и готовить базу знаний, причём здесь речь не только про документацию.
  5. DevOps*. Должен ориентироваться в архитектуре тестового окружения и уметь её модифицировать.

* 4-я и 5-я роли только для участников «центра» НТ команды, а не для всех НТ инженеров, о конкретной структуре я расскажу чуть позже.

Некоторые общие принципы работы QA в Miro


В нашей компании есть QA гильдия, объединяющая QA инженеров из разных функциональных команд и инженеров, разрабатывающих и отвечающих за общие инструменты обеспечения качества. И раз НТ у нас — это часть QA, а для QA уже были сформулированы и применялись ряд принципов, то и к НТ они были применены.

Вот ключевые из этих принципов:

  1. Ответственность за качество лежит на всей команде. Не только на QA инженерах.
  2. Тесты всех уровней пишут QA и разработчики. Чтобы не было пинг-понга «сделал-проверь-поправь-проверь-поправь-проверь-вроденорм».
  3. Минимизация эффекта «единой точки отказа». Чтобы не было так, что QA в отпуске, а критичные баги просочились на прод.
  4. Главный фокус QA инженеров — построение процесса разработки с точки зрения качества. Не столько искать ошибки, сколько предотвращать. Уровень задач становится выше, чем просто тестирование.

Распределённая команда нагрузочного тестирования в Miro


Если ранее я рассказывал об опыте коллег, то в описываемом далее я принимал уже непосредственное участие. В компанию вместе со мной были взяты люди, которые занимались уже только НТ. И вот какую команду мы строили и какими задачами занимались.

  1. «Центр» — команда НТ — центр компетенций и поддержки. Зоны ответственности:

    1. Разработка инструментария (особое внимание к качеству!). У функциональных команд заказчик — клиенты Miro, у нашей команды заказчик — функциональные команды. Внутренние инструменты не должны быть поделками, сделанными «на коленке». Это должны быть полноценные и качественные продукты с должным уровнем поддержки всех видов. Функциональные команды и так загружены и повышать их уровень стресса плохими инструментами — недопустимо. Поэтому у нас есть:

      1. Мастер-классы.
      2. Miro доски-презентации.
      3. Confluence документация.
      4. Версии, changelog, детальная отладочная информация в ходе работы инструмента.
      5. Общий репозиторий с примерами кода и всеми НТ сценариями, чтобы было единое место, где собрана вся практика НТ.
      6. Отдельные чаты на каждый инструмент для поддержки, сообщений о релизах, багрепортов и фичереквестов.
    2. Помощь в обучении и консультирование коллег из функциональных команд, не только QA инженеров.
    3. Проведение больших тестов, когда нужна проверка суммы функционала от множества команд.
    4. Поддержка тестовых окружений совместно с QA Automation Team и DevOps Team.
  2. «Агенты» — QA инженеры и разработчики в функциональных командах — проводят более узконаправленные тесты.

Процесс нагрузочного тестирования в Miro


Шаг 1/6. Детализация требований


Мы рассмотрели, что такое НТ и посмотрели на то, как устроена распределённая команда НТ в Miro. Теперь пройдёмся по этапам НТ, наложенным на наш процесс. Начнём с самого важного этапа.

Ещё раз подчеркну, что к формулировке требований стоит подойти особенно ответственно. Без чёткого понимания, чего вы хотите от тестируемого приложения, дальше двигаться нельзя.

Источники требований


  1. OKR, цели и ключевые результаты. Часть из них может относиться к поддерживаемой нагрузке и производительности для всех многопользовательских систем.
  2. Соглашения о качестве работы системы. Все, кто работают с клиентами, должны обеспечивать определённый уровень качества, формально описанный или неформально принятый. И это лучшее место для спецификации таких нефункциональных требований, как требования по производительности.
  3. Аналитика сайта. Нужна не напрямую для формулировки требований, а для определения текущего профиля использования, который может служить основанием для сценария воспроизведения поведения пользователей и как основа для прогнозов.
  4. Пользовательские сценарии. Мы должны понимать, как наши клиенты используют нашу систему, чтобы понимать, каким частям системы уделить внимание.
  5. Прогнозы. Мы должны примерно представлять, что будет в будущем с показателями использования системы и всегда иметь запас прочности.
  6. Знания и опыт продакт-менеджеров. Не всегда всё нужное зафиксировано в документах и мониторинге.

Остановлюсь подробнее на паре используемых нами инструментов:

  1. Looker — инструмент для сбора и анализа бизнес-показателей. Мы сформулировали требования, далее в ходе составления сценария в большинстве случаев нам нужно опираться на множество текущих показателей работы прода. Здесь могут помочь такие бизнес-показатели, как данные о том, какой диапазон размера досок используется каким количеством пользователей.
  2. Grafana — примерно то же самое, но для системных метрик. Например, мы хотим проверить требование «Miro должен держать 500К онлайн пользователей с такими-то показателями скорости ответа по критичным компонентам». Чтобы смоделировать это, нужно знать, как при существующем уровне нагрузки используется сайт, какими функциями и с какой частотой пользуются клиенты, чтобы в итоге перевести абстрактных «500К пользователей» в RPS по основных типам запросов. В получении RPS как раз помогает Grafana.

Выбор модели нагрузки


Различают две модели нагрузки:

  1. Закрытая. В генераторе нагрузки есть очередь определённого размера: если она заполнена и ответы от сервера ещё не пришли, то новые запросы не отправляются до тех пор, пока не поступят ответы на старые запросы. Серверу стало плохо? Подождём, пусть разгребёт.
  2. Открытая. Генератор нагрузки отправляет запросы, не дожидаясь ответов сервера и не обращая внимания на его состояние. Серверу стало плохо? Это его проблемы, жизнь такая, продолжаем грузить.

Подробнее по отличиям этих моделей нагрузки и про сценарии использования каждой можно посмотреть в видео из школы Яндекса — «Нагрузочное тестирование типичного интернет-сервиса» от Андрея Похилько.

Выбор типа теста


Для разных требований нужны разные тесты. Вот основные примеры типов тестов:

  1. Smoke-тесты — проверка всех составляющих НТ на малом масштабе.
  2. Тесты на базовый уровень — снимаем текущие метрики работы системы.
  3. Тесты на объём — выдержит ли система повышенную нагрузку при «нормальном» сценарии использования?
  4. Тесты на лимиты — до какого уровня можно грузить систему, прежде чем она начнёт деградировать?
  5. Тесты на стабильность — как долго система сможет держать определённый уровень нагрузки?
  6. Тесты на масштабируемость — ведёт ли добавление новых серверов к поддержке большего числа запросов?
  7. Тесты на пиковые нагрузки — выдержит ли система резкое повышение нагрузки?
  8. Тесты на восстанавливаемость — достаточно ли быстро система восстанавливается после сбоя?
  9. Тесты на взаимодействие — эффективно ли взаимодействуют компоненты системы?
  10. Деградационные тесты — один или несколько тестов, описанных ранее, запускаются автоматизированно и регулярно, с построением отчётов с отображением динамики за длительный период.

Эти тесты могут быть совмещены и не все могут быть нужны.

Декомпозиция требований


«Cистема должна отвечать быстро при высокой нагрузке» – это плохое требование, слишком расплывчатое и оставляющее больше вопросов, чем определённости в том, как его проверять.

Более конкретное требование — «система должна обеспечивать столь же комфортный опыт использования как сейчас при росте нагрузке в X3 пользователей». Здесь указано, что такое «высокая нагрузка» — указано «X3 пользователей» и указано что опыт работы с системой должны быть «столь же комфортным как сейчас». Здесь есть на что опираться — на текущие показатели работы с системой.

Детализация означает конвертацию пользователей в RPS, основываясь на существующем и ожидаемом профилях использования. Такая конвертация уместна только для Stateless протоколов, как самые распространённые HTTP и HTTP, а для Stateful число подключенных клиентов может быть очень важно, как например для FTP, протокола работы с MySQL или некоторых банковских протоколов. Ещё один важный момент — не доверяйте средним значениям времён ответа, используйте персентили, т.к. среднее зачастую является плохой метрикой.

Вот примеры конкретных требований, которые могли быть получены из высокоуровнего и других источников данных:

  1. RT1. Запрос типа #1 при нагрузке X1 RPS должен иметь p(50) в A1 ms, p(95) в B1 ms …
  2. RT2. Запрос типа #2 при нагрузке X2 RPS должен иметь p(50) в A2 ms, p(95) в B2 ms …
  3. ER1. Процент ошибок для критичных сервисов не должен превышать N%
  4. ER2. Процент ошибок для некритичных сервисов не должен превышать M%
  5. IS1. Нагрузка CPU не должна превышать P%
  6. IS2. Система должна иметь резерв для обработки +10% нагрузки

Метрики


Есть очень хорошая книга по SRE от Google, где среди прочего описаны «4 золотых сигнала»:

  1. Время ответа. Именно то, что заметит пользователь.
  2. Трафик. RPS, число запросов в секунду, всё просто.
  3. Ошибки. Об их максимально допустимом проценте могут быть свои требования.
  4. Насыщение. Если даже требования по остальным показателям выполняются, но при этом сервера загружены по полной — это должно наводить на соответствующие выводы. Всегда нужно иметь запас.

И даже если попытаться, сложно придумать другие необходимые категории метрик. Все 4 необходимы и достаточны.

Естественно, метрики всех четырёх типов тесно взаимосвязаны. Повышение трафика и соответственно насыщения со временем приведёт к росту времени ответа и ошибок. Резко упавшее время ответа может говорить о том, что у нас отказала БД и время ответа упало из-за того, что все запросы просто быстро завершаются с ошибкой.

Некоторые добавляют и другие показатели. Например, стабильность, которая показывает как долго система может работать без остановки и как быстро восстанавливается после проблем. Это сложный показатель и скорее его стоит отнести не к типам метрик, а к выводу отчёта после целого теста на стабильность. Подобная ситуация с доступностью и масштабируемостью. Есть ещё понятие критического ресурса, как компонента, от которого сильнее всего зависит качество работы всей системы.

APDEX


Индекс производительности приложений Application Performance inDEX (APDEX) может быть использован для преобразования ряда времён ответов в одно число, если определены диапазоны удовлетворительных, допустимых и недопустимых времён.



Значения APDEX легки в сравнении, но тяжелы в интерпретации.

Шаг 2/6. Подготовка тестового окружения


Сценарий готов, далее нужно подготовить целевую систему, которую тестовый сценарий будет проверять.

Тестовое окружение состоит из двух частей:

  1. Целевая система (имитация прода).
  2. Кластер подачи нагрузки (имитация пользователей).

Для их реализации у нас используются:

  1. IaC

    1. AWS. Когда счёт используемых серверов идёт на десятки и сотни, без автоматизации никак. У нас был давно принят выбор перейти от арендованного железа в облако. Вопрос выбора конкретного провайдера (AWS, Google Cloud, Microsoft Azure) — тема отдельного доклада. Я лишь скажу, что в своих инструментах я активно использую AWS API и это на самом деле удобно, не знаю как можно было бы жить без этого или аналогичного инструмента.
    2. Terraform, Terragrunt (дефакто стандарт для IaC)
    3. Ansible. Систем управления конфигурациями много и многие по-своему хороши, тут надо девопсов спрашивать почему Ansible, хотя мне как в основном Python-разработчику нравится этот выбор :)
  2. Свой управляющий инструмент (консольная Python утилита с множеством команд, опций и конфигом), который выполняется в части подготовки тестового окружения (он же ещё используется при запуске сценария)

    1. Запуск кластеров целевой системы и подачи нагрузки.
    2. Деплой версий и запуск приложений на серверах приложений.
    3. Создание серверов подачи нагрузки.

Шаг 3/6. Подготовка сценария


Мы определились с тем, что требуется от системы и как пользователи с ней работают, теперь это поведение нужно смоделировать в тестовом сценарии.

Эскиз на человеческом языке


Перед началом разработки хорошей практикой является создание эскиза сценария на «человеческом языке». Это поможет выразить, какое поведение пользователей планируется моделировать без траты времени на погружение в детали инструментов НТ. И инструмент тоже поможет подобрать, если такой вопрос стоит. А ещё, при составлении отчёта, такой эскиз поможет ревьюерам понять моделируемое поведение пользователей, не погружаясь в код.

Как было показано ранее, на этапе детализации требований мы разделяем активность пользователей на отдельные RPS. Иногда бывает нужно ещё объединять запросы в подобие подсценария.

Вот пример такого эскиза сценария:

  1. Подсценарий #1 — получение уведомлений (X RPS)
  2. Подсценарий #2 — получение статуса серверов (Y RPS)
  3. Подсценарий #3 (Z SPS)

    1. Логин
    2. Открытие дэшбоарда
    3. Открытие доски с W1 виджетов для U1 доли пользователей, W2 для U2 и так далее
    4. Движение курсором
    5. Добавление нескольких виджетов
    6. Закрытие доски


Выбор инструмента


В данный момент у нас используется три инструмента для НТ:

  1. Свой инструмент для моделирования работы с досками.
  2. k6. Для него мы в своём управляющем инструменте реализовали обёртку для кластерного запуска и сбора результатов.
  3. JMeter (legacy, практически не используется).

Вообще говоря, с инструментами НТ вопрос сложный. Их множество и коллегами был проведён детальный разбор наиболее популярных, чтобы понять, что сможет справиться в первую очередь с ключевым функционалом Miro — работой с досками. У нас используется бинарный протокол, работающий через WebSocket, требуемый для высокой скорости работы с досками. Основы, необходимой для его реализации, не было найдено и между доработкой какого-то из имеющихся решений и разработкой собственного узкоспециализированного решения выбор был в пользу второго варианта, разработки своего инструмента. Насчёт остальных направлений НТ и выбора инструментов — мы решили не ограничивать команды, а реализовывать поддержку в нашем управляющем инструменте для каждого инструмента, который коллегам показался подходящим. Под управляющим инструментом я имею ввиду средство управления тестовой средой и запуска НТ, об этом будет позже. Интеграция в него дополнительного инструмента НТ — задача в принципе несложная, поэтому мы можем позволить командам пользоваться тем, чем им мы удобно. Так, например, несколько человек экспериментируют с Locust. А мы наблюдаем за успешностью использования инструментов и на базе этого формируем проверенные практикой советы по выбору инструмента НТ для тех, кто только начинает этим заниматься.

Исследование


Ещё один шаг перед написанием кода — исследование того, как реальный фронтенд взаимодействует с бэкендом с помощью инструментов разработчика в браузере и попробовать несколько простых взаимодействий вручную, например через Postman.

Но иногда проще начать сразу с кода.

Данные


Было бы идеально тестировать на базе, подобной проду, и сбрасывать её перед каждым тестом, но это очень редко возможно из-за колоссальных объёмов данных и необходимости использования сложных инструментов чистки реальных данных. Поэтому у нас есть свой инструмент генерации данных, который умеет создавать в нужном объёме и в разумный срок основные бизнес-сущности: пользователей, команды, организации, доски разного размера, проекты и строить связи между ними. Этот генератор использует то же API, что и основное веб-приложение, и каждый сеанс генерации данных представляет собой немного хаотичный пример НТ. На выходе получается подробный лог и набор файлов, которые можно подключать к НТ сценариям.

Советы по коду


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

  1. Сценарий нужно разрабатывать насколько возможно настраиваемым, чтобы иметь возможность например запустить его на других RPS без изменения кода, поэтому хардкод подобных параметров должен быть минимальным.
  2. Не стоит пренебрегать документацией по инструменту НТ. Не изобретайте колесо, инструменты НТ разрабатываются на основе большого опыта и в большинстве случаев то, что вам нужно, уже существует.
  3. Мониторинг чаще всего придётся доделывать на основе базовых компонентов, гораздо реже можно найти то, что идеально подойдёт.
  4. Помните, что чем больше статистики, мониторинга и логирование — тем выше побочная нагрузка, нужно уметь найти баланс.
  5. Никогда не используйте код подобный `… request(); sleep(N); request() ...` для моделирования 60/N RPM, это не работает, найдите компоненты для вашего инструмента НТ, реализующие требуемую частоту запросов, или хотя бы используйте асинхронный код.

Шаг 4/6. Запуск теста и измерения


Целевая система готова, переходим к самому интересному — запуску теста и снятию метрик. Как я говорил ранее, НТ — это не функциональное тестирование, поэтому мы не просто снимаем ряд булевых показателей типа работает/не работает, а получаем целый ряд разнообразных метрик. Как разбираться в них — большая тема, здесь я затрону только некоторые моменты из неё.

Какие метрики снимать — есть базовый набор и есть специальные для каждого теста, это надо решать ещё при подготовке сценария и тестового окружения.

В ходе собственно выполнения теста у нас используются:

  1. Свой управляющий инструмент. Функции:

    1. Деплой тестового сценария и данных (поддерживается несколько сценариев и инструментов НТ).
    2. Запуск теста.
    3. Сбор результатов и логов инструмента НТ.
  2. Универсальные средства мониторинга и сбора логов.

Здесь ещё можно упомянуть Open Source проект Taurus, который, как и наше внутреннее решение, предназначен для запуска разных инструментов НТ и поддерживает более 20 инструментов тестирования. Ещё есть тоже открытый Яндекс.Танк. Свой инструмент я решил разрабатывать, потому что задумывалась тесная интеграция с управлением тестовым окружением и инструмент больше про это. Сделать просто распределённый запуск инструмента НТ — задача не такая уж сложная.

И пара советов:

  1. Не пытайтесь сразу запустить полный тест, начните с нескольких небольших smoky-тестов. Проверьте, что сценарий выполняется как надо, RPS воспроизводятся и все нужные метрики собираются.
  2. Не стоит полагаться на сценарий и метрики полностью. Попробуйте поработать с системой вручную.

Шаг 5/6. Анализ


Паттерны


Давайте предположим, что сценарий выполнился и у нас множество метрик. На что стоит обратить внимание в первую очередь? Вот некоторые примеры. Первые три могут говорить или не говорить о неполадках, а последние вероятнее всего говорят о проблеме:

  1. «Пила». Значимые скачки показателя вверх/вниз с достаточно большими промежутками между ними могут говорить, например, о насыщении какого-то ресурса до предела и падении ряда агентов, поднятии новых и повторе ситуации. Или такой может быть внутренняя логика работы приложения.
  2. «Полка». Может говорить о том, что мы достигли предела и дальше система не справляется, хотя и не падает. Здесь надо смотреть состояние генераторов нагрузки, возможно больше этого предела они и не грузят и это не ошибка, а просто достижение необходимого уровня. Ещё это может быть предельное значение, выше которого подняться в принципе невозможно — например, загрузка CPU под 100%. Такие случаи требуют особого внимания.
  3. «Всплеск». Резкий скачок, причину которого надо изучать. Это может быть, например, внезапно запустившаяся сборка мусора. Но если это график ошибок, то очевидны проблемы.
  4. Резкое падение. Например, в начале теста при постепенном увеличении нагрузки может умереть один из воркеров, генерирующих трафик. Или это может быть график общего числа RPS для всего кластера, что может говорить об отказе одного из серверов.
  5. Плавная деградация. Это может быть, например, график RPS на сервере, которые начали проседать при стабильной нагрузке. Или обратная ситуация, нагрузка тоже стабильна, а на графике использование оперативной памяти в течение продолжительного срока. Тоже однозначно нужно обратить внимание.
  6. Линейный/нелинейный рост связанных показателей. Если некоторый входной параметр растёт линейно, важно чтобы уровень нагрузки подсистем тоже рос хотя бы линейно, но никак не быстрее на несколько степеней. Здесь самое время вспомнить про сложность алгоритмов: O(n), O(log n), O(n^2) и подобное. Чем сильнее нелинейность — тем меньше будет толку от новых и новых серверов.

На графиках ниже показаны синтетические примеры описанных выше паттернов:


Пример


Разберём простейший пример, который может научить нескольким полезным вещам:

  1. Проблема: система отвечает слишком медленно на некоторый сценарий. Пусть 95-я персентиль времени выполнения стала 15 с вместо 2 с.
  2. Анализ:

    1. Найти узкое место, которое тормозит сценарий. Пусть это будет некоторый API запрос.
    2. Проверить насыщение API серверов. CPU, RAM, IO, очереди сообщений и прочее.
    3. Проверить зависимости. Допустим, в нашей системе используется другой компонент системы, который отвечает слишком медленно.
    4. Проверить сервер, на котором работает этот другой компонент. Допустим там ~100% CPU.
  3. Действие — добавить результаты разбора в отчёт. Какие действия предпринимать, чтобы исправить проблему — это не такой простой вопрос. Изменить тип инстанса на больший — это просто, но это не всегда правильное решение. Если используется алгоритм с сильно нелинейной сложностью, то даже увеличение мощности CPU сервера ненадолго поможет.
  4. Броки:

    1. Наблюдайте за всем, за чем можно, помня про побочную нагрузку от замеров.
    2. Ищите подлинные причины проблем.
    3. Простой вывод — не всегда правильный.

Шаг 6/6. Отчёт


Самое главное сделано. Остаётся подвести итоги и зафиксировать полученные в ходе нагрузочного тестирования знания. Особо хочу отметить, что НТ — это операция, требующая весьма существенных затрат труда, времени и прочих ресурсов. Поэтому вы явно захотите получить максимум пользы, потратив всё это.

Чтобы извлечь максимум пользы из проведённого НТ — нужен хороший отчёт. Каковы же критерии хорошего отчёта? НТ делается НТ инженерами не только и не столько для себя, а много для кого и для разных людей польза от отчёта зависит от наличия развёрнутых ответов на соответствующие вопросы:

  1. Для менеджера — насколько система соответствует требованиям, есть ли запас и какая ситуация может вызвать проблемы с системой?
  2. Для DevOps — есть ли слабые места системы со стороны инфраструктуры?
  3. Для разработчиков — есть ли слабые места системы со стороны кода?
  4. Для НТ инженера — какие инструменты НТ с какими настройками использовались, какой сценарий использовался, на какой конфигурации инфраструктуры проводился тест? Все эти знания в том числе для того, чтобы можно было воспроизвести сценарий при каких-либо изменениях кода или инфраструктуры для сравнения.

Нет отчёта — нагрузочное тестирование впустую!

Ещё один очень важный момент. Серьёзный подход требует регулярного НТ. Оно невозможно без автоматизации построения отчётов и степень этой автоматизации зависит от частоты проведения тестирования.

Результаты внедрения нагрузочного тестирования в Miro


Я рассказал о том, какие этапы НТ есть и как мы работаем на каждом из них.теперь перечислю, чего мы добились благодаря внедрению НТ в таком виде:

  1. Понятный и описанный процесс. Чётко описано, какие шаги нужно выполнить и что нужно делать на каждом из шагов.
  2. Большие тесты: 200K, 500K пользователей online при типичном сценарии использования. Каждый сложный тест зачастую проходит много итераций перед успешным выполнением, поэтому без автоматизации столько раз гонять большие тесты было бы чрезвычайно долгим и утомительным занятием, полным ошибок.
  3. Проверка компонентов перед выкаткой на прод.
  4. Обоснованный выбор параметров компонентов для поддержки планируемой нагрузки. То есть не просто с потолка брать «ну наверное хватит сотни воркеров», а выбирать параметры на основе результатов проверок.
  5. Автоматизация, упрощение и ускорение проведения НТ. Я говорил в начале про список из 16 пунктов, которые надо было пройти вручную при каждом запуске НТ на кластере. Теперь это один конфиг и одна команда. Больше автоматизации даёт больше надёжности и больше возможностей уделить время более творческим и сложным задачам.
  6. Деградационное тестирование (начало). Автоматическое выполнение тестов по реалистичному API сценарию на типичной нагрузке каждую ночь на последней версии сервера и построение графиков трендов для отслеживания динамики показателей скорости ответа и числа ошибок.

Планы в отношении нагрузочного тестирования в Miro


Несмотря на то, что было сделано многое, ещё больше предстоит. Вот планируемые направления развития:

  1. Деградационное тестирование (полноценная интеграция с CI/CD). В идеале постоянное НТ должно быть встроено в пайплайн и так же как функциональные тесты влиять на принятие решения о релизе.
  2. Тестирование микросервисов. Мы в процессе переезда с монолита, и всё больше работы появляется в этом направлении.
  3. Вовлечение большего числа команд. У нас их несколько десятков и пока не все включены в процесс.
  4. Улучшение инструментария. При бурном росте не всегда получается выдержать хорошую архитектуру, так что есть немного техдолга.
  5. Дополнение внутренней документации НТ примерами конкретных кейсов: как тестировали, что и как выявили, как доработали. Общая документация — это хорошо, но дополнение её солидным набором описаний конкретных кейсов — бесценно.
  6. Более исследовательская работа: профилирование, хаос-тестинг и т.д.

Пожелания читателям




Напоследок пожелаю следующее:

  1. Верьте в свой проект. Если не будете верить вы, пользователи и подавно не будут.
  2. Делайте НТ. Высокие нагрузки должны стать источником дополнительной прибыли или других благ, а не источником проблем.
  3. Делайте НТ заранее. Профилактика всегда лучше лечения.
  4. Делайте НТ правильно. Ресурсы у всех ограничены, и лучше тратить их с умом.
  5. Делайте НТ регулярно. Всё течёт, всё изменяется.
  6. Автоматизируйте. Ну это очевидно в 21 веке, я думаю.

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


В ходе работ по НТ мной и моими коллегами были в том числе использованы источники из базы знаний, создание которой было инициировано русскоязычным QA Load сообществом и в которой я один из мейнтейнеров, также мы многое почерпнули и из самого чата этого сообщества. Ссылки:

  1. Русскоязычное QA Load сообщество в Telegram
  2. База знаний QA Load сообщества

Предыдущие материалы о НТ в Miro:

  1. Достоверный нагрузочный тест c учетом непредвиденных нюансов (доклад с DUMP-2019)
  2. Достоверный нагрузочный тест с учётом непредвиденных нюансов
  3. Управление сотнями серверов для нагрузочного теста: автомасштабирование, кастомный мониторинг, DevOps культура

QA процесс в Miro:

  1. Качество — ответственность команды. Наш QA опыт
  2. QA-процесс в Miro: отказ от водопада и ручного тестирования, передача ответственности за качество всей команде

Прочие ссылки:

  1. Ликбез о том, кто такие QA инженеры и кто такие тестировщики (от Dodo Engineering) — «Кто ты, QA-инженер или тестировщик?»
  2. Глава про «4 золотых сигнала» в книге по SRE от Google

Комментарии (6)


  1. AlexunKo
    17.08.2021 14:53
    +1

    Интересно, спасибо. Удивляет на сколько старый кактус под названием JMeter захватил отрасль. Помнится, еще лет 10 назад работал с ним и это был кошмар кастомизаций. А его призрак все еще прогуливается по современным проектам.


    1. Gim6626 Автор
      17.08.2021 15:06
      +1

      Я примерно один сценарий пробовал делать тремя инструментами ради эксперимента. Просто опрос N эндпоинтов с разными RPS, с вариациями составляющих URL в зависимости от данных, со всякими пороговыми значениями и дополнительными метриками. JMeter вызвал больше всего боли и я там многое так и не сделал. На k6 написал за день-два и прям конфетка получилась. Не скажу, что я прямо хорошо знал JMeter, но и k6 я до этого почти не пользовался.


      1. AlexunKo
        17.08.2021 15:08

        k6 не пробовал, но однажды перешел с JMeter на Gatling. А он на Scala, что само по себе для тех еще гурманов. Но все равно лучше, хоть и не скажу что идеально.


        1. mvs
          20.08.2021 23:07
          -1

          Есть весьма неплохая пушка https://artillery.io/


  1. polarnik
    21.08.2021 01:04
    +1

    Спасибо за ссылку на сообщество и за вклад в базу знаний!

    Интересно, вы используете APDEX. Механизм расчета сделали свой кастомный?


    1. Gim6626 Автор
      30.08.2021 12:18
      +1

      Вам спасибо за то, что строите это сообщество!

      Прошу извинить, что долго отвечал, как-то упустил комментарий.

      По поводу APDEX - пока у меня двухступенчатая схема. В k6 я, используя встроенный компонент Counter, сделал подсчёт количества запросов по трём категориям из методики APDEX (удовлетворительные времена ответа, терпимые и неудовлетворительные), а потом отдельным скриптом после завершения k6 на основе его JSON report делается постобработка и уже вычисляется собственно APDEX. Как эту постобработку встроить в k6 и избавиться от дополнительного скрипта - пока не нашёл. Ещё в знаменателе APDEX формулы число неудовлетворительных времён ответа я умножил на два чтобы больше вклад в результат внести и плохие результаты были более заметными чтобы раньше на них реагировать. Но это всё пока в очень экспериментальном режиме.