rbenvとunicornでRedmineのRuby環境を分けて動かす

超絶シンプルなブログを作ろうとして逆算していくとRedmineが障壁になったのでRedmineを単独で動かすことにしました。

今までRedmineはPassengerを使ってApacheやNginxで動かして来ましたが、今回はunicornで動かすことにしました。unicornで動かすと言ってもポート80はウェブサーバで使うのでリバースプロキシで接続することになります。多くの場合Redmineはチーム内であったりして使う人が限られていると思うのでそれで多少パフォーマンスが落ちても問題ないと思っています。それよりも環境を独立して動かすことを優先するという形です。

  • rbenvでrubyは入れる
  • nginxからproxyでunicornに接続する
  • Redmineはgitでいれる
  • Redmineに必要なgemはvendor/bundleに入れる
  • Redmineのデータベースはmysqlを使う
  • RedmineはSubURI/redmineで公開する

これによりRedmine(Railsアプリ)の実行環境を完全に独立できます。OSに依存する部分は少ないですがDebian/Ubuntu向けの内容になります。基本的に最後の仕上げらへんのsudoコマンドを使っている部分を除いて一般ユーザでセットアップします。

rbenvをセットアップ

OS XならHomebrewでbrew install rbenvで入れると楽です。aptはパッケージが古かったので直接入れることにします。

git clone git://github.com/sstephenson/rbenv.git ~/.rbenv
git clone git://github.com/sstephenson/ruby-build.git ~/.rbenv/plugins/ruby-build
echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> ~/.profile
echo 'eval "$(rbenv init -)"' >> ~/.profile

Debian/Ubuntuなので.profileに書いています。rbenvのセットアップ詳細はrbenvを参照。

Redmineのruby環境をセットアップ

Redmineで使うrubyをインストールします。とりあえず1.9.xのこの時点の最新を使いました。

rbenv install 1.9.3-p392
...

Bundlerのインストール

rbenv shell 1.9.3-p392
gem install bundler
rbenv rehash

Redmineのインストール

どこにインストールしても問題ないです。~/redmineでも可。この時点の最新2.3を使いました。

git clone https://github.com/redmine/redmine redmine
cd redmine
git checkout -b release/2.3.0 2.3.0

Redmineの依存解決

データベースはmysqlを使うのでsqlite, postgresqlを除外します。--path vendoer/bundleを設定しているのでRedmineで使うgemはそこにインストールされ他のRubyアプリケーションに影響させないようにします。

rbenv local 1.9.3-p392
bundle install --without development test rmagick sqlite postgresql --path vendor/bundle

データベースのセットアップ

例です。データベースユーザやパスワードはちゃんとしたものを使ってください。

mysql -u root -p
create database redmine character set utf8;
create user 'redmine'@'localhost' identified by 'redmine';
grant all privileges on redmine.* to 'redmine'@'localhost';
exit;
cp -p config/database.yml.example config/database.yml

config/database.ymlを編集します。

# mysql
production:
  adapter: mysql2
  database: redmine
  host: localhost
  username: redmine
  password: redmine
  encoding: utf8

Redmineのセットアップ

cd /path/to/redmine
bundle exec rake generate_secret_token
RAILS_ENV=production bundle exec rake db:migrate
RAILS_ENV=production bundle exec rake redmine:load_default_data
mkdir tmp tmp/pdf public/plugin_assets

Sub URIで動作させるためにconfig.ruを修正します。Root(/)で動かすならこの作業は不要です。

# This file is used by Rack-based servers to start the application.

require ::File.expand_path('../config/environment',  __FILE__)
#run RedmineApp::Application
if ENV['RAILS_RELATIVE_URL_ROOT']
  map ENV['RAILS_RELATIVE_URL_ROOT'] do
    run RedmineApp::Application
  end
else
  run RedmineApp::Application
end

unicorn追加でインストール

cd /path/to/redmine
echo 'gem "unicorn"' >> Gemfile.local
bundle

unicorn設定ファイル

cd /path/to/redmine
curl -o config/unicorn.rb https://raw.github.com/defunkt/unicorn/master/examples/unicorn.conf.rb

パスのみ変更してあとはデフォルトのままです。必要に応じて調整してください。

# See http://unicorn.bogomips.org/Unicorn/Configurator.html for complete
# documentation.

APP_PATH = File.expand_path('../../', __FILE__)

worker_processes 4

working_directory APP_PATH # available in 0.94.0+

listen "#{APP_PATH}/tmp/unicorn.sock", :backlog => 64

timeout 30

pid "#{APP_PATH}/tmp/unicorn.pid"

stderr_path "#{APP_PATH}/log/unicorn.stderr.log"
stdout_path "#{APP_PATH}/log/unicorn.stderr.log"

preload_app true
GC.respond_to?(:copy_on_write_friendly=) and
  GC.copy_on_write_friendly = true

check_client_connection false

before_fork do |server, worker|
  defined?(ActiveRecord::Base) and
    ActiveRecord::Base.connection.disconnect!
end

after_fork do |server, worker|
  defined?(ActiveRecord::Base) and
    ActiveRecord::Base.establish_connection
end

unicorn実行

テスト実行。Root(/)で動かすなら--path /redmineは不要です。

cd /path/to/redmine
bundle exec unicorn_rails -c config/unicorn.rb -E production --path /redmine

nginx設定

config/unicorn.rbでリスニングしているunix socketに接続させます。

upstream redmine {
    server unix:/path/to/redmine/tmp/unicorn.sock;
}
server {
...
    location /redmine {
        root /path/to/redmine/public;
        if (-f $request_filename) { break; }
        proxy_set_header    X-Real-IP           $remote_addr;
        proxy_set_header    X-Forwarded-Host    $host;
        proxy_set_header    X-Forwarded-For     $proxy_add_x_forwarded_for;
        proxy_pass http://redmine;
    }
...
}

仕上げ

問題なければ起動スクリプトを/etc/init.d/redmineに置いて実行。USER, APPPATH, URIPATHあたりを修正してください。

#! /bin/sh

### BEGIN INIT INFO
# Provides:          redmine
# Required-Start:    $all
# Required-Stop:     $network $local_fs $syslog
# Default-Start:     2 3 4 5
# Default-Stop:      0 1 6
# Short-Description: starts the redmine web server
# Description:       starts redmine
### END INIT INFO

USER=username
APP_PATH=/path/to/redmine
RAILS_ENV=production
URI_PATH=/redmine

SET_PATH="cd $APP_PATH"
DAEMON="bundle exec unicorn_rails"
DAEMON_OPTS="-c $APP_PATH/config/unicorn.rb -E $RAILS_ENV -D --path $URI_PATH"
CMD="$SET_PATH; $DAEMON $DAEMON_OPTS"
NAME=redmine
DESC="Unicorn app for redmine"
PID="$APP_PATH/tmp/unicorn.pid"
OLD_PID="$PID.oldbin"

cd $APP_PATH || exit 1

sig () {
        test -s "$PID" && kill -$1 `cat $PID`
}

oldsig () {
        test -s $OLD_PID && kill -$1 `cat $OLD_PID`
}

case ${1-help} in
start)
        sig 0 && echo >&2 "Already running" && exit 0
        su - $USER -c "$CMD"
        ;;
stop)
        sig QUIT && exit 0
        echo >&2 "Not running"
        ;;
force-stop)
        sig TERM && exit 0
        echo >&2 "Not running"
        ;;
restart|reload)
        sig HUP && echo reloaded OK && exit 0
        echo >&2 "Couldn't reload, starting '$CMD' instead"
        su - $USER -c "$CMD"
        ;;
upgrade)
        sig USR2 && exit 0
        echo >&2 "Couldn't upgrade, starting '$CMD' instead"
        su - $USER -c "$CMD"
        ;;
rotate)
        sig USR1 && echo rotated logs OK && exit 0
        echo >&2 "Couldn't rotate logs" && exit 1
        ;;
*)
        echo >&2 "Usage: $0 <start|stop|restart|upgrade|rotate|force-stop>"
        exit 1
        ;;
esac

exit 0
sudo service redmine start
sudo update-rc.d redmine defaults

起動時に動かすようにもしておきます。

その他

Redmineを初期セットアップ風に書いているけど実際は2.2->2.3のアップグレード。

Ruby 1.9にあげたらgem install activerecord-mysql-adapterというエラーになった。bundlerで依存関係解決しているのに・・・調べたらconfig/database.ymlのadapter: mysqlをadapter: mysql2にすると直った。

実は最初はpassenger-standaloneで進めていたがSubURIで動かせないという問題に遭遇した。proxy redirectで無理やり変換できそうだが面倒なのとunicornつかってみたかったので諦めた。ルートディレクトリで公開できるなら行けた。

参考


This markdown is rendered by wp-gfm