Библиотека сайта rus-linux.net
Система непрерывной интеграции
Оригинал: A Continuous Integration System
Автор: Malini Das
Дата публикации: July 12, 2016
Перевод: Н.Ромоданов
Дата перевода: октябрь 2016 г.
Creative Commons
Перевод сделан в соответствие с лицензией
Малини Дас (Malini Das) является инженером-программистом, которая увлечена технологией быстрой (но безопасной) разработки, а также решением кросс-функциональных проблем. Она работала в компании Mozilla в качестве инженера-инструментальщика, а в настоящее время оттачивает свои навыки в компании Twitch. Вы можете найти Малини в
Что такое система непрерывной интеграции?
При разработке программного обеспечения мы хотим иметь возможность проверять, что наши новые функции или исправления ошибок являются безопасными и работают так, как ожидалось. Мы делаем это путем запуска тестов на нашем коде. Иногда разработчики для того, чтобы убедиться, что их изменения безопасны, запускают тесты локально, но у них может не быть времени для проверки того, что их код будет работать в любой системе, для которой предназначена их программа. Далее, по мере добавления все большего и большего числа тестов, количество времени, необходимое для запуска тестов даже только локально, становится все более нереальным. Из-за этого и были созданы системы непрерывной интеграции.
Системы непрерывной интеграция (Continuous Integration - CI) являются специализированными системами, используемыми для тестирования нового кода. После того, как код добавляется в репозиторий, на систему непрерывной интеграции возлагается ответственность убедиться в том, что этот код не нарушит выполнение тестов. Для этого в системе должна быть возможность добавлять новые изменения, запускать тесты и сообщать о результатах их выполнения. Как и любая другая система, эта система также должна быть отказоустойчивой. Это означает, что если в какой-либо части системы произойдет сбой, у ней должна быть возможность восстановиться и продолжить работу с этой точки.
Такая тестовая система должна также быстро работать под нагрузкой с тем, чтобы мы могли в случае, когда коммиты поступают быстрее, чем могут быть выполнены тесты, получать результаты работы за приемлемое время . Мы можем добиться этого путем распределения и распараллеливания работы по тестированию. В данном проекте будет рассказано о небольшой распределенной системе непрерывной интеграции, которая обладает свойством расширяемости.
Проектные ограничения и замечания
В этом проект в качестве репозитория для кода, который должен тестироваться, используется Git. Будут использоваться только стандартные вызовы управления исходным кодом, поэтому если вы не знакомы с Git, но знакомы с другими системами управления версий (version control systems - VCS), например, с SVN или Mercurial, вы все еще сможете следить за нашими рассуждениями.
Функционал, касающийся тестирования, упрощен из-за ограничений, заданных длиной кода и методикой модульным тестирования. Мы будем запускать тесты, которые находятся в репозитории в каталоге с именем tests.
Системы непрерывной интеграции следят за состоянием мастер-репозитория, который обычно размещен на веб-сервере, а не локально в файловой системе системы непрерывной интеграции. В случае нашего примера мы будем использовать локальный репозиторий вместо удаленного.
Системы непрерывной интеграции не должны работать по фиксированному регулярному расписанию. Также вы можете запускать тесты для нескольких коммитов или для каждого коммита. В нашем примере система непрерывной интеграции будет запускаться с заранее заданным периодом. Это означает, что, если она настроена для проверки изменений с пятисекундным периодом, то она будет снова запускать тесты после самого последнего коммита, сделанного в течение пятисекундного интервала. Она не будет проверять каждый коммит, сделанный в течение этого периода времени, а только самый последний.
Данная система непрерывной интеграции предназначена для периодической проверки изменений в репозитории. В реальных системах непрерывной интеграции у вас также может быть наблюдатель (observer) за репозиторием, посылающий уведомления из репозитория. В github, например, есть специальные пускатели-хуки, срабатывающие после выполнения коммита (post-commit hooks), которые посылают уведомления по некоторому URL. В соответствие с этой моделью, наблюдатель за репозиторием должен вызываться веб-сервером, размещенном на этом URL с тем, чтобы отреагировать на это уведомление. Поскольку для локальной модели это сложно сделать, мы используем модель наблюдателя, когда наблюдатель за репозиторием будет проверять наличие изменений, а не получать уведомления.
В системах непрерывной интеграции также есть средства создания отчетов, с помощью которых для некоторого компонента создается отчет по результатам запуска тестов и это отчет предоставляется разработчику, возможно, в виде веб-страницы. В настоящем проекте для упрощения результаты запуска тестов собираются и сохраняются виде файлов в локальной файловой системе с тем, чтобы можно было использовать специальный диспетчер.
Следует отметить, что архитектура данной системы непрерывной интеграции является лишь одним из многих возможных вариантов. Этот вариант был выбран с тем, чтобы ограничить нашу сферу изучения лишь тремя основными компонентами.
Введение
Базовая структура системы непрерывной интеграции состоит из трех компонентов: наблюдателя (observer), диспетчера тестовых заданий (test job dispatcher) и средства запуска тестов (test runner). Наблюдатель следит за репозиторием. Когда он обнаруживает, что был сделан коммит, он уведомляет об этом диспетчер заданий. Затем диспетчер заданий находит средство запуска тестов и передает ему номер коммита для записка тестов.
Есть множество вариантов архитектуры системы непрерывной интеграции. Мы бы могли поместить наблюдатель, диспетчер и средство запуска тестов в один и тот же процесс, выполняемый на одной машине. Такой подход весьма ограничен, поскольку система не выдержит нагрузки в случае, если в репозиторий будет добавлено больше изменений, чем может обработать система непрерывной интеграции, и отставание будет нарастать. Этот подход также абсолютно неотказоустойчивый; если в работе компьютера произойдет сбой или произойдет отключение электропитания, систем, которые бы нейтрализовали отказ, нет и, поэтому, тесты выполняться не будут. Идеальная система должна быть такой, чтобы она могла обрабатывать столько тестовых заданий, сколько потребуется, и делает все возможное, чтобы компенсировать ситуации, когда некоторые из компьютеров прекращают работу.
Чтобы создать систему непрерывной интеграции, которая будет отказоустойчивой и будет справляться с большой нагрузкой, в данном проекте каждый из компонентов системы запускается в своем собственном процессе. В результате каждый процесс независим от других процессов, причем также можно будет запускать несколько экземпляров каждого процесса. Это полезно в случаях, когда у вас более одного тестового задания, которые должны выполняться одновременно. Тогда вы сможете, по мере необходимости, параллельно запустить сразу несколько процессов с помощью средств запуска тестов, и это позволит вам в очереди на выполнения тестов избежать накопления отставания.
В данном проекте эти компоненты не только выполняются в виде отдельных процессов, но они также взаимодействую друг с другом через сокеты, и это позволяет нам запускать каждый процесс на отдельном сетевом компьютере. Каждому компоненту назначается уникальный адрес хоста/порт, и каждый процесс может обмениваться данными с другими процессами, отправляя сообщения на назначенные адреса.
Такая конструкция за счет распределенной архитектуры позволит нам на лету обрабатывать аппаратные сбои. У нас наблюдатель может работать на одной машине, диспетчер тестовых заданий - на другой, а средства запуска тестов — еще на одной, и все они могут общаться друг с другом по сети. Если какая-нибудь из этих машин остановится, мы можем запланировать запуск в сети новой машины и таким образом система становится отказоустойчивой.
В данном проекте не приводится код, обеспечивающий автоматическое восстановление, поскольку он зависит от архитектуры нашей распределенной системы, но в реальных условиях для обеспечения избыточной отказоустойчивости системы непрерывной интеграции работают в распределенных средах, похожих на описанную выше (то есть, мы можем запустить резервное устройство в случае, если одна из машин, на которой был запущен процесс, остановится).
В рамках данного проекта каждый из этих процессов будет работать в одной и той же файловой системе и будет запускаться вручную на различных портах.
Files in this Project
В данном проекте для каждого из описанных выше компонентов есть файлы на языке Python: наблюдатель за репозиторием (repo_observer.py), диспетчер тестовых заданий (dispatcher.py) и средство для запуска тестов (test_runner.py). Каждый из этих трех процессов взаимодействует с другими с помощью сокетов, и поскольку во всех этих файлах используется один и тот же код, с помощью которого реализуется обмен информацией, он с целью избежать дублирования функций обмена данными выделен отдельно в виде файла helpers.py, импортируемого в каждый из компонентов.
Имеются также файлы скриптов на языке bash, которые используются в этих процессах. Они применяются для выполнения команд bash и git более простым способом, нежели постоянные обращения к модулям системного уровня языка Python, таким как os или subprocess.
И, наконец, есть каталог тестов, в котором находятся два примера тестов, запускаемые системой непрерывной интеграции. Один тест должен проходить, а другой заканчиваться неудачным исполнением.
Первоначальная настройка
Хотя наша система непрерывной интеграции готова к работе в распределенной системе, давайте для того, чтобы мы могли получить представление о том, как система непрерывной интеграции работает без проявления проблем, касающихся работы в сети, начнем с того, что запустим все локально на одном компьютере. Если вы хотите запустить систему в распределенной среде, вы можете запустить каждый компонент на отдельно взятой машине.
Системы непрерывной интеграции запускают тесты после обнаружения изменении в репозитории, в котором хранится код, поэтому для начала нам нужно будет указать репозиторий, за которым будет наблюдать наша система непрерывной интеграции.
Давайте создадим репозиторий test_repo
:
$ mkdir test_repo $ cd test_repo $ git init
Это будет наш главный репозиторий (master repository). Это место, куда разработчики выкладывают свой код, поэтому наша система непрерывной интеграции должна получать доступ к содержимому этого репозитория, проверять наличие коммитов, а затем запускать тесты. Тот компонент, который проверяет наличие новых коммитов, является наблюдателем за репозиторием.
Наблюдатель за репозиторием выполняет работу с помощью проверки коммитов, поэтому нам нужен по крайней мере один коммит в главном репозитории. Давайте закоммитим наши тестовые пример с тем, чтобы можно запустить некоторые тесты.
Скопируйте тестовый каталог из этого базового кода в test_repo
и закоммитьте его:
$ cp -r /this/directory/tests /path/to/test_repo/ $ cd /path/to/test\_repo $ git add tests/ $ git commit -m ”add tests”
Компоненту-наблюдателю за репозиторием нужен будет свой собственный клон кода, с тем чтобы он мог обнаружить, что был сделан новый коммит. Давайте создадим клон нашего главного репозитория и назовем его test_repo_clone_obs
:
$ git clone /path/to/test_repo test_repo_clone_obs
Средству запуска тестов также понадобится свой собственный клон кода с тем, чтобы он мог брать из репозитория указанный коммит и запускать тесты. Давайте создадим еще один клон нашего главного репозитория и назовем его ите его test_repo_clone_runner
:
$ git clone /path/to/test_repo test_repo_clone_runner
Перейти к следующей части статьи.