Тестирование в Яндексе. Как сделать отказоустойчивый грид из тысячи браузеров

- КиТ :: Будь в СЕТИ!

Любой специалист, причастный к тестированию веб-приложений, знает, что большинство рутинных действий на сервисах умеет делать фреймворк

В Яндексе в день выполняются миллионы автотестов, использующих Selenium для работы с браузерами, поэтому нам нужны тысячи различных браузеров, доступных одновременно и 24/7. И вот тут начинается самое интересное. Selenium с большим количеством браузеров имеет много проблем с масштабированием и отказоустойчивостью. После нескольких попыток у нас получилось элегантное и простое в обслуживании решение, и мы хотим поделиться им с вами. Наш проект gridrouter позволяет организовать отказоустойчивый Selenium-грид из любого количества браузеров. Код выложен в open-source и доступен на . Под катом я расскажу, на какие недостатки Selenium мы обращали внимание, как пришли к нашему решению, и объясню, как его настроить.

Проблема

Selenium с момента своего создания не раз кардинально менялся, текущая архитектура, называющаяся Selenium Grid, работает так.

Кластер состоит из двух приложений: хаба (hub) и ноды (node). Хаб – это API, принимающее запросы пользователей и отправляющее их на ноды. Нода – исполнитель запросов, запускающий браузеры и выполняющий в них шаги теста. К одному хабу может быть теоретически подключено бесконечное число нод, каждая из которых умеет запускать любой из поддерживаемых браузеров. А что же на практике?

Есть уязвимое место. Хаб – это единственная точка доступа к браузерам. Если по каким-то причинам процесс хаба перестает отвечать, то все браузеры становятся недоступны. Ясно, что сервис также перестает работать, если у дата-центра, где стоит хаб, происходит отказ по сети или питанию. Selenium Grid плохо масштабируется. Наш многолетний опыт эксплуатации Selenium на разном оборудовании показывает, что под нагрузкой один хаб способен работать не более чем с несколькими десятками подключенных нод. Если продолжать добавлять ноды, то при пиковой нагрузке хаб может перестать отвечать по сети или обрабатывает запросы слишком медленно. Нет квотирования. Нельзя создать пользователей и указать, какие версии браузеров какой пользователь может использовать.

Решение

Чтобы не страдать при падении одного хаба, можно поднять несколько. Но обычные библиотеки для работы с Selenium рассчитаны на работу только с одним хабом, поэтому придется научить их работать с распределенной системой.

Балансировка на клиенте

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

Вот как это работает:

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

Реализация такого алгоритма несложная, но требует интеграции с каждой библиотекой для работы с Selenium. Допустим, в ваших тестах браузер получается таким кодом: Здесь – это стандартный класс для работы с Selenium на Java. Для работы в нашей инфраструктуре придется обернуть его в наш собственный код с выбором хаба: В коде тестов больше нет URL до Selenium, он содержится в конфигурации библиотеки. Также это значит, что код тестов теперь привязан к SeleniumHubFinder и без него не запустится. Кроме того, если у вас есть тесты не только на Java, но и на других языках, то придется писать клиентский балансировщик для них всех, а это может быть затратно. Гораздо проще вынести код балансировки на сервер и указать его адрес в коде тестов.

Балансировка на сервере

При проектировании сервера мы заложили следующие естественные требования:

Сервер должен реализовывать API Selenium (протокол ), чтобы тесты работали с ним, как с обычным Selenium-хабом. Можно расставить сколько угодно голов сервера в любых дата-центрах и забалансировать их железным или программным балансировщиком (SLB). Головы сервера совершенно независимы друг от друга и не хранят общее состояние (shared state). Сервер из коробки обеспечивает квотирование, то есть независимую работу нескольких пользователей. Архитектурно полученное решение выглядит так:

Балансировщик нагрузки (SLB) раскидывает запросы от пользователей на одну из N голов с сервером, слушающих на стандартном порту (4444). Каждая из голов хранит в виде конфигурации информацию обо всех имеющихся Selenium-хабах. При поступлении запроса на браузер сервер использует алгоритм балансировки, описанный в предыдущем разделе, и получает браузер. Каждый запущенный браузер в стандартном Selenium получает свой уникальный идентификатор, называемый ID сессии. Это значение передается клиентом хабу при любом запросе. При получении браузера сервер подменяет настоящий ID сессии на новый, дополнительно содержащий информацию о хабе, на котором была получена данная сессия. Полученная сессия с расширенным ID отдается клиенту. При следующих запросах сервер извлекает адрес хоста с хабом из ID сессии и проксирует запросы на этот хост. Поскольку вся нужная серверу информация есть в самом запросе, не надо синхронизировать состояние голов – каждая из них может работать независимо.

Gridrouter

Сервер мы назвали gridrouter и решили поделиться его кодом со всеми. Сервер написан на Java с использованием . Исходники проекта можно посмотреть по . Мы также подготовили , устанавливающие сервер.

В настоящий момент gridrouter установлен в качестве боевого сервера, используемого разными командами Яндекса. Общее количество доступных браузеров в этом гриде более трех тысяч. В пиках нагрузки мы обслуживаем примерно такое же количество пользовательских сессий.

Как настраивать gridrouter

Для того чтобы настроить gridouter, нужно задать список пользователей и квоты для каждого пользователя. Мы не ставили цель сделать супербезопасную аутентификацию с хэш-функциями и солью, поэтому используем обычную , а логины и пароли храним в открытом виде в текстовом файле /etc/grid-router/users.properties вида:user:password, user user2:password2, user

Каждая строчка содержит логин и пароль через двоеточие, а также роль, которая на данный момент одна и та же, – user. Что касается квот, то здесь все тоже очень просто. Каждая квота представляет собой отдельный XML-файл /etc/grid-router/quota/.xml, где – имя пользователя. Внутри файл выглядит так: Видно, что определяются имена и версии доступных браузеров, которые должны точно совпадать с теми, что указаны на хабах. Для каждой версии браузера определяется один или несколько регионов, то есть разных дата-центров, в каждый из которых записываются хост, порт и количество доступных браузеров (это и есть вес). Имя региона может быть произвольным. Информация о регионах нужна в тех случаях, когда один из дата-центров становится недоступен. В этом случае gridrouter после одной неудачной попытки получения браузера в некотором регионе пытается получить браузер из другого региона. Такой алгоритм значительно повышает вероятность быстрого получения браузера.

Как запустить тесты

Хотя в основном мы пишем на Java, мы проверяли наш сервер с Selenium тестами на других языках программирования. Обычно в тестах URL хаба указывается примерно так:http://example.com:4444/wd/hub

Поскольку мы используем basic HTTP-аутентификацию, при работе с gridrouter следует использовать такие ссылки:http://username:password@example.com:4444/wd/hub

Если у вас возникнут проблемы с настройкой, обращайтесь к нам, заводите issue на .

Рекомендации по настройке хабов и нод

Мы проводили эксперименты с разными конфигурациями хабов и нод и пришли к выводу, что с точки зрения простоты эксплуатации, надежности и легкости масштабирования наиболее практичным является следующий подход. Обычно устанавливают один хаб, к которому подключают много нод, потому что точка входа должна быть одна. При использовании gridrouter можно поставить сколько угодно хабов, поэтому проще всего настроить на одной машине один хаб и несколько нод, подключенных к localhost:4444. Особенно удобно так делать, если все разворачивается на виртуальных машинах. Например, мы выяснили, что для виртуальной машины с двумя VCPU и 4 Гб памяти оптимальным является сочетание хаба и пяти нод. На одну виртуальную машину мы устанавливаем только одну версию браузера, поскольку в этом случае легко измерять потребление памяти и переводить число виртуальных машин в число имеющихся браузеров.

ПодпискаБудь в СЕТИ! Новости социальных сетей - всегда актуальное
 
Группы: ВК | OK | Tg