Непрерывная интеграция, Jenkins и Middleman

Illustration of a person sitting on the floor against a wall, engrossed in reading a book, with an orange scarf and sneakers, in a grayscale setting with a purple shadow. Illustration of a person sitting on the floor against a wall, engrossed in reading a book, with an orange scarf and sneakers, in a grayscale setting with a purple shadow.

Во второй статье серии я рассказал о том, чем хорош генератор статичных сайтов Middleman и почему я выбрал именно его для новой версии личного блога fodoj.com. В этой, заключительной статье я опишу процесс автоматизации деплоя сайтов, написанных на Middleman. Инструменты и процесс, который я буду использовать, применимы не только для генерации статичных сайтов (вы можете провернуть тоже самое с Jekyll или Octopress), но и для любого проекта, нуждающегося в автоматическом развёртывании.

Процесс автоматического деплоя приложений

Прежде чем перейти к описанию конкретных инструментов, рассмотрим основные этапы грядущей автоматизации. Как только новая статья (или любое другое изменение в проекте) слиты в ветку master git-репозитория с сайтом нам нужно автоматически выполнить следующие шаги:

  1. Вытянуть репозиторий с кодом
  2. Внутри репозитория собрать итоговую скомпилированную версию
  3. Залить эту версию на хостинг

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

Запускаем процесс при помощи хуков

И в BitBucket и в GitHub есть функция под названием webhooks. Её суть проста: вы можете указать сервису выполнить определённое действие при возникновении какого-то события. В большинстве случаев событие – это новый коммит в какую-то ветку. Действием может быть что угодно: помимо интеграции с множеством других сервисов типа Jenkins и Travis CI можно попросить BitBucket/GitHub/GitLab отправить POST-запрос по любому адресу.

Вот так, например, выглядит конфигурация хуков в GitHub:

В самом git так же есть встроенная поддержка "хуков":

Hooks are little scripts you can place in $GIT_DIR/hooks directory to trigger action at certain points.

Это очень удобно если вы хотите прогнать какие-нибудь тесты перед слиянием веток или прогнать lint-инструмент по всему коду.

Сервисы непрерывной интеграции

Как я уже сказал, получить хук может что угодно. Например, сервис для непрерывной интеграции (Continious Intregration). Что за непрерывная интеграция? Это процесс, в котором ваше приложение постоянно тестируется и, при успешном прогоне тестов, компилируется и, при успешной компиляции, выкладывается на сервер. При неуспешной – сразу же оповещает разработчиков об этом.

В простейшем случае (возьмём мир Rails) это означает "прогони тесты при помощи rspec spec и, если тесты успешно прогнаны, выполни cap deploy и отправь оповещение в Slack". В более сложных случаях и больших приложениях процесс непрерывной интеграции состоит из множества различных задач.

Одним из самых популярных сервисов для CI небольших и open source проектов является Travis CI. Он бесплатен для проектов с открытым исходным кодом, работает только с GitHub и, вообщем-то, справляется со своей задачей "прогнать тесты и задеплоить приложуху". В обмен на значительную потерю в гибкости, разнообразии фич и масштабируемости пользователи Travis CI получают красивый интерфейс, снятие боли от поддержки своих серверов для CI и заточенное под решение нескольких задач решение.

Если же вашей компании нужно по-настоящему сверх-мощное, расширяемое, масштабируемое и способное вообще на что угодно решение, то вам придётся выбрать Jenkins. Jenkins – open source решение для непрерывной интеграции, написанное на Java и использующееся в любой уважающей себя крупной компании.

В Jenkins есть система плагинов, которых на данный момент накопилось уже больше тысячи. Например, есть отдельный плагин для BitBucket, GitHub, для RVM, для LDAP, для Chef, для Puppet... Вообщем, сложно придумать что-то, для чего ещё нет плагина для Jenkins.

Помимо этого, в Jenkins есть разбиение на master и slaves машины, что позволяет строить очень интересные топологии для автоматизации и бесконечно ускорять билды просто запуская побольше slave'ов. Дополнительно стоит обратить внимание на плагин build pipeline, с помощью которого можно рисовать нетривиальные схемы действий для разных задач.

Быстрый способ начать с Jenkins

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

Как настроил Jenkisn я: запустил EC2-сервер с AMI образом от bitnami. Если вам это ни о чём не говорит, то не страшно.

Jenkins + S3 + CloudFront

В статье про основные преимущества Middleman я упомянул плагин middleman-aws, который одной консольной командой компилирует итоговый билд сайта, заливает его на S3 и говорит CloudFront обновить дистрибуцию. В нашем случае, чтобы автоматизировать обновление сайта, достаточно создать job внутри Jenkins, который выполнит эту самую команду. Впоследствии можно запускать билды вручную, если автоматического оповещения из BitBucket недостаточно. Вот как выглядит создание job в схеме ниже:



Так как Jenkins устанавливается на сервер, то вполне возможно что на этом сервере нет ruby и нужных гемов. Решить первую проблему можно при помощи установки ruby вручную или при помощи Chef. Вторая проблема решается непосредственно во время конфигурации задачи (job) внутри Jenkins. Выглядит итоговая команда вот так:

# Я установил ruby при помощи rvm чтобы иметь возможность гонять билды на разных версиях ruby
source /etc/profile.d/rvm.sh

# Кто не мучился с ошибками компиляции при установке nokogiri, тот не жил
bundle config build.nokogiri --use-system-libraries

# Ставим нужные гемы внутрь workspace (папки, куда Jenkins сделает git pull репозитория)
bundle install --path vendor/bundle

# Собираем и публикуем сайт
bundle exec rake mm:publish

Ну и напоследок, как выглядит панель управления Jenkins:

Automate all the things!

Мы рассмотрели процесс автоматизации развёртывания приложений на высоком уровне, не углубляясь в детали настройки конкретных инструментов. Этот процесс применим, вообщем-то, для чего угодно. Возможно, у вас не Middleman, а одностраничное приложение на Angular.js, разработанное с использованием Gulp или Grunt. В таком случае весь описанный выше процесс не меняется, за исключением конкретных команд по прогону тестов и сборке приложения.

О том, как разрабатывать Angular.js приложения на mkdev есть целый мини-курс, который проведёт вас от настройки рабочего окружения для фронтенд разработки до работающего приложения.

Если у вас небольшой или open-source проект, то берите Travis CI и используйте его. Если что-то серьёзное и нуждающееся в тонкой или нетривиальной настройке, то смотрите в сторону Jenkins. И в том и в любом случае, уменьшайте количество ручной работы и автоматизируйте всё что можно.

А какой, кстати, у вас опыт успешной (или не успешной) автоматизации развёртывания приложений?