CI: integrity with resque
Я думаю, что в каждой команде в процессе разработки рано или поздно встаёт вопрос о настройке continuous integration. Для руби-проектов существует много инструментов и по статистике ruby toolbox наибольшей популярностью пользуются cjoe, integrity и cruisecontrol.rb. Раньше мы использовали cruisecontrol.rb, но на текущем проекте решили попробовать что-нибуть альтернативное, отчасти потому, что cruisecontrol.rb достаточно достаточно сложно настраивать, там отсутствуют web-hooks для github, а так же он периодически у нас зависал. Сначала я решил попробовать cijoe, который достаточно прост, но не подошел тем, что для каждого бранча нужно было поднимать отдельный экземпляр приложения, а CI у нас планировалось запускать на одном из development серверов.
В итоге выбор пал на integrity. Посмотреть его в действии можно на http://builder.integrityapp.com. Это простой в установке и использовании сервис. Написан на sinatra и легко устанавливается в окружении, позволяющем запуск rack приложений. Внутри integrity очень простой, но в тоже время функциональный. Он предоставляет несколько механизмов уведомления: email, IRC, campfire, TCP, HTTP, notifo and AMQP.
Build можно запустить несколькими способами:
- сделать HTTP POST request по адресу
/:project/builds; - настроить github post receive hook на адрес
/github/:token; - нажать кнопку “Fetch and build” на web-интерфейсе integrity;
- создать собственный скрипт и запускать его через cron. Этот способ зависит от того, какой builder вы используете.
И, наконец, integrity позволяет механизм постановки в очередь и запуска заданий:
- threaded — integrity создаёт пул потоков и запускает тесты параллельно. Я думаю тут нужно учитывать, что это зелёные потоки ruby, которые на самом деле запускаются в одном системном.
- delayed — в этом случае, integrity использует delayed_job в качестве back-end. Для работы этого метода нужно включить дополнительные зависимости в Gemfile.
- resque — этот метод я выбрал как наиболее безглючный и простой в настройке. Resque — это библиотека, написанная на ruby, для работы с фоновыми задачами. Resque использует redis в качестве хранилища, а так же предоставляет front-end на sinatra для мониторинга очередей. Очереди в resque могут быть распределены между несколькими хостами. Подробнее можно прочитать в файле README из репозитория.
Настройка
Так как мы используем ubuntu на сервере, некоторые команды ниже могут быть специфичны для этой системы, но я думаю будет не сложно найти аналогичные для вашей. Для начала нужно установить и настроить resque.
Устанавливаем redis. При этом redis должен создать стартовый скрипт в /etc/rc.*.
# apt-get install redis-server
Далее устанавливаем resque:
# gem install resque
Теперь настроим front-end для него. Мы используем nginx и passenger для запуска rack/rails приложений, так что дальше следует типичная настройка rack приложения для passenger (подробнее можно прочитать в официальной документации).
# mkdir -p /home/deploy/resque/public
# mkdir -p /home/deploy/resque/tmp
Пример файла /home/deploy/resque/config.ru можно взять в репозитории resque. Мы используем такой:
require 'logger'
$LOAD_PATH.unshift File.expand_path(File.dirname(__FILE__) + '/lib')
require 'resque/server'
use Rack::ShowExceptions
Resque::Server.use Rack::Auth::Basic do |username, password|
password == 'secret' && username = 'admin'
end
run Resque::Server.new
Тут вы можете защитить паролем ваш сервер. В nginx нужно добавить еще одну секцию server:
server {
listen 80;
server_name resque.example.com;
root /home/deploy/resque/public;
passenger_enabled on;
}
И после этого можно зайти на front-end в браузере и проверить, что всё ок.

Дальше установим и настроим integrity:
# cd /home/deploy
# git clone git://github.com/integrity/integrity.git
# cd integrity
# mkdir -p tmp/pids
Чтобы включить resque backend для integrity нужно раскоментировать соответствующую строку в Gemfile:
gem "resque"
Для nginx нужно добавить новую секцию server:
server {
listen 80;
error_log logs/integrity.error.log info;
server_name integrity.example.com;
passenger_enabled on;
root /home/deploy/integrity/public;
}
Мы используем следующий конфиг для integrity:
LOAD_PATH.unshift(File.join(File.dirname(__FILE__), "lib"))
require ".bundle/environment"
require "integrity"
require "integrity/notifier/email"
Integrity.configure do |c|
c.database = "sqlite3:integrity.db"
c.directory = "builds"
c.base_url = "http://integrity.example.com"
c.log = "integrity.log"
c.github_token = "YOUR_SECRET_GITHUB_TOKEN"
c.build_all = true
c.builder = :resque
c.auto_branch = false
c.username = "admin"
c.password = "secret"
end
Теперь осталось только запустить resque worker:
# rake resque:work
Только скорее всего эта команда у вас не запустится, потому что в integrity есть небольшой баг в Rakefile и, я надеюсь, что его скоро исправят. Если нет, то попробуйте вот этот патч:
diff --git a/Rakefile b/Rakefile
index e384f82..4af7479 100644
--- a/Rakefile
+++ b/Rakefile
@@ -53,11 +53,11 @@ end
begin
namespace :resque do
+ require "init"
require "resque/tasks"
desc "Start a Resque worker for Integrity"
task :work do
- require "init"
ENV["QUEUE"] = "integrity"
Rake::Task["resque:resque:work"].invoke
end
В github нужно добавить post-receive hook, чтобы тесты запускались каждый раз, когда кто-нибудь заливает новый код. Адрес должен быть в формате http://username:password@integrity.example.com/github/:github_token
Ещё можно настроить monit для мониторинга integrity workers. Пример конфигурации
которую мы используем /etc/monit/conf.f/resque:
check process resque_worker_integrity
with pidfile /home/deploy/integrity/tmp/pids/resque_worker_integrity.pid
start program = "/bin/sh -c 'cd /home/deploy/integrity; COUNT=1 QUEUE=integrity VERBOSE=1 nohup /opt/ruby-enterprise/bin/rake resque:work& > log/resque_worker_integrity.log && echo $! > tmp/pids/resque_worker_integrity.pid'" as uid deploy and gid deploy
stop program = "/bin/sh -c 'cd /home/deploy/integrity && kill -s QUIT `cat tmp/pids/resque_worker_integrity.pid` && rm -f tmp/pids/resque_worker_integrity.pid; exit 0;'"
if totalmem is greater than 200 MB for 10 cycles then restart
group resque_workers
Теперь monit будет следить чтобы было запущено не более одного workers и будет перезапускать их, если количество используемой памяти превышает 200 мегабайт. Если вы хотите использовать больше workers, то вам нужно позаботиться чтобы ваш тестовый rake task учитывал одновременный запуск нескольких копий проекта (использовал разные базы).
Вот и всё, теперь можно зайти на http://integrity.example.com и добавить ваш проект. Надеюсь эта статья
поможет вам. Спасибо.