Немного об инструментах
Каждый новый тикет мы делаем в отдельной ветке, как советует это git-flow. В качестве таск-менеджера/баг-трекера мы используем JIRA, поэтому номер тикета в JIRA = названию ветки. Jenkins CI мы используем не на полную мощь, пока только для деплоя кода на staging разработческой версии после мержа в нее для интеграционного тестирования.
Суть проблемы
Хотелось иметь возможность проверять каждый тикет изолированно, и в релиз брать только проверенные тикеты, а те что содержат баги — оставлять на следующий.
Что кроме Capistrano?
Рассматривались различные варианты от шаринга рабочей машины с помощью ngrok до SaaS наподобие teatro. Первый вариант оказался неудобен, а второй отпал потому что не все проекты есть возможность отправить третьей стороне. Поэтому ввиду того, что все наши RoR-проекты деплоятся с помощью capistrano, было принято решение написать небольшое расширение, которое будет разворачивать проект из определенной ветки на свой хост (например, jira-123.example.com).
Процесс
Если коротко, то процесс разработки выглядит так: после выполнения тикета разработчик выливает его на демо-хост, после проверки тестировщиком создается мерж реквест, после закрытия которого Jenkins выливает будущий релиз на staging.
Что делает capistrano-demo
Все то, что делает разработчик при разработке – выливает код, накатывает миграции и запускает сторонние сервисы (sidekiq, resque и т.д.).
Данный плагин имеет ряд ограничений, самое больше это то, что он работает только для RoR-проектов, и только с git.
Конфигурация
# По умолчанию используется одна БД для всех хостов, если есть деструктивные миграции,
# то гем дает возможность вручную вписать имя БД
set :demo_db, -> { demo_default_db }
# Хост, на поддомене которого будет создавать демо-хост
set :demo_host, -> { fetch(:application) }
# Команда которую нужно выполнить при рестарте хоста.
# Пример с одно из наших проектов, где требовалось перезагрузить unicorn, nginx и sidekiq:
# invoke 'unicorn:restart'
# invoke 'sidekiq:restart'
# execute :sudo, :service, 'nginx restart'
# execute :rake, 'cache:clear'
set :demo_restart_cmd, -> { raise 'You must specify "demo_restart_cmd" proc' }
# Папка, в которой лежат шаблоны конфигов, для конкретного окружения
# Пример:
# File.expand_path("../../../../config/stages/#{fetch(:stage)}/templates", __FILE__)
set :demo_templates_dir, nil
# Хеш, для настройки какой шаблон куда положить после компиляции, шаблоны должны быть .erb
# Пример:
# set :demo_templates_entries, [
# {template: '/nginx.conf.erb', file: demo_path.join('config', 'nginx.conf')},
# {template: '/database.yml.erb', file: demo_path.join('config', 'database.yml')},
# {template: '/unicorn.rb.erb', file: demo_path.join('config', 'unicorn.rb')},
# {template: '/settings.local.yml.erb', file: demo_path.join('config', 'settings.local.yml')}]
set :demo_templates_entries, []
Как пользоваться
Данный плагин имеет всего три команды:
- demo:create — создание/обновление демо-хоста
- demo:restart — перезагрузка
- demo:destroy — Остановка процессов(настраивается с помощью before/after) и удаление директории.
Чтобы создать демо-хост нужно просто из рабочей директории набрать команду cap staging demo:create и все. По умолчанию будет предложено вылить текущую ветку.
Заключение
На данный момент самая большая проблема это долгая сборка ассетов, нужно заставить его не пересобирать всё, а только диф. А также, чья-то миграция может сломать чужие хосты, поэтому на staging мы держим чистую базу, на такой случай. Были попытки делать отдельную БД для каждой ветки, но тогда приходилось создавать либо пустую БД, либо копировать. Первый вариант плох тем, что приходилось бы забивать контент для тестирования, а второй — нет универсального средства для копирования данных для нескольких СУБД, но в будущем планируем сделать адаптеры для SqlLite, MySql и Postgres.
Наши наработки мы выложили в открытый доступ, поэтому каждый может ознакомиться и воспользоваться, pull request'ы приветствуются.
PS: В комментариях готов ответить на ваши вопросы, выслушать альтернативные варианты решения и конструктивную критику.
Только зарегистрированные пользователи могут участвовать в опросе. Войдите, пожалуйста.
Комментарии (8)
AlexLeonov
31.08.2015 13:22+1Вам осталось только использовать что-то вроде TeamCity, чтобы отслеживать изменения в ветках и инициировать сборку автоматически и вы откроете для себя волшебный мир Continious Integration )))
fc_arny
31.08.2015 13:43Уже открыли Jenkins, но пока не на полную мощь -) Скорее всего следующим шагом автоматизации будет создание демо-хоста именно на пуш + обновление тикета в JIRA, с плагином это делать удобнее, т.к. он позволяет — sidekiq запустить, переиндексацию или еще что-то, что в коде уже есть в виде rake-тасков или просто модулей.
AlexLeonov
31.08.2015 14:27Давно хочу описать такую связку. В двух местах уже удачно реализовал. Правда, на PHP, но какая разница?
И да, «демо-хосты» называются по уму «шоты». Терминология предложена разработчиками Badoo, которые были первопроходцами в этом деле.fc_arny
31.08.2015 14:36Да, про shot в терминологии Badoo знаком, но еще до той статьи мы делали для PHP аналогичный механизм, но только на fabric, и оперировали понятием демо-хост, но наверное shot — лаконичнее.
AlexLeonov
31.08.2015 17:24+1Шот правильнее, потому что есть следующий уровень — билды. Имхо проверка интеграции нескольких задач еще важнее, чем изолированный стенд для каждой задачи.
dannote
Данные в БД можно каждый раз забивать при помощи
db/seeds.rb
, добавив вdeploy.rb
для роли demo строчкуexecute :rake, 'db:seed'
. Asset-ы проще собирать локально один раз и синхронизировать rsync-ом. Для этого есть готовый gem.А Sidekiq и Resque для чего-то кроме отправки электронной почты используете?
fc_arny
Да, рассматривался вариант с seed (в том числе и с seedbank'ом), но не устроил нас по одной причине: на проекте сильно накрученный elasticsearch, и для проверки алгоритма(который видоизменялся по требованию заказчика со скоростью света) часто требовался большой объем данных, который хранить в репозитории не хотелось. Также использование одной БД позволило использовать одни и те же uploads-файлы, и как следствие на всех демо-хостах у нас полностью заполнен контент.
capistrano-local-precompile пытался использовать, но тут тоже не все так просто получалось. Если приходиться деплоить на несколько окружений(демо-хосты + staging + production и т.д.), то для каждого окружения создается своя копия ассетов + магифест (это очень заметно, когда статика лежит на поддоменах) и получается, что за спринт накапливается огромное количество ассетов, хотя возможно я что-то делал не так и наверное надо сделать еще один подход и разобраться лучше.
Sidekiq/Resque мы используем в основном для синхронизации данных со сторонними сервисами, которые часто могут отвечать очень долго или быть недоступны.