可能性の獣

mongrelで動かしていたRailsアプリをUnicornに切り替えた。 nginxの方が情報は多いんだけど、今回は他のWebアプリも動いてるので敢えてApacheにする。 若干ハマったのでメモ。

まず、Unicornのインストール。 ついでにRailsもアップデートしておく。 予めenvironment.rbのRAILS_GEM_VERSIONを変更しておき、

gem install unicorn
gem install -v 2.3.10 rails
cd /path/to/rails_app
rake rails:update

で完了。

次にunicornの設定。 設定ファイル名は任意だが、RAILS_ROOT/config/unicorn.conf.rbとする。 例ではポート5001、ユーザ名unicornで子プロセスは5つ立ち上げている。

user 'unicorn'
group 'unicorn'
shared_path = "/path/to/rails_app/shared"
pid "#{shared_path}/pids/unicorn.pid"
stderr_path "#{shared_path}/log/unicorn.stderr.log"
stdout_path "#{shared_path}/log/unicorn.stdout.log"
processes = 5
timeout = 75
listen = 5001

rails_env = ENV['RAILS_ENV'] || 'production'
worker_processes (rails_env == 'production' ? processes : 1)
preload_app true
timeout timeout
listen listen, :backlog => 2048

after_fork do |server, worker|
  ActiveRecord::Base.establish_connection
  begin
    uid, gid = Process.euid, Process.egid
    target_uid = Etc.getpwnam(user).uid
    target_gid = Etc.getgrnam(group).gid
    worker.tmp.chown(target_uid, target_gid)
    if uid != target_uid || gid != target_gid
      Process.initgroups(user, target_gid)
      Process::GID.change_privilege(target_gid)
      Process::UID.change_privilege(target_uid)
    end
  rescue => e
    if RAILS_ENV == 'development'
      STDERR.puts "couldn't change user"
    else
      raise e
    end
  end
end

ここでいったんテストしようとして、unicorn -C /path/to/rails_app/config/unicorn.conf.rb -E productionとかやると、config.ruが無いよとか言われる。無理矢理gemに含まれてるやつを持ってきて起動すると、今度は-E付けてるのになぜかdevelopment環境になったりしてわけわかめ。 正解は、unicornではなくunicorn_railsを使う。 これを踏まえて、deploy.rbはこんな感じに。

set :application, "app_name"
set :scm,         :git
set :repository,  "~/src/app_name"
set :branch,      'master'
set :deploy_via,  :copy
set :deploy_to,   "/path/to/#{application}"
set :user,        "unicorn"
set :runner,      "unicorn"
set :use_sudo,    false
role :app, "app.example.com"
role :web, "www.example.com"
role :db,  "db.example.com", :primary => true
set :rails_env, :production
set :unicorn_binary, "/usr/local/bin/unicorn_rails"
set :unicorn_config, "#{current_path}/config/unicorn.conf.rb"
set :unicorn_pid, "#{current_path}/tmp/pids/unicorn.pid"

namespace :deploy do
  task :start, :roles => :app, :except => { :no_release => true } do 
    run "cd #{current_path} && #{unicorn_binary} -c #{unicorn_config} -E #{rails_env} -D"
  end
  task :stop, :roles => :app, :except => { :no_release => true } do 
    run "kill `cat #{unicorn_pid}`"
  end
  task :graceful_stop, :roles => :app, :except => { :no_release => true } do
    run "kill -s QUIT `cat #{unicorn_pid}`"
  end
  task :reload, :roles => :app, :except => { :no_release => true } do
    run "kill -s USR2 `cat #{unicorn_pid}`"
  end
  task :restart, :roles => :app, :except => { :no_release => true } do
    stop
    start
  end
end

sudo使う場合はtaskに#{try_sudo}とか入れる。 ちなみに一度目はエラーになっちゃうのでdeploy:coldを使うこと。 設定変えた時とかはdeploy:reloadを使えば無停止で切り替えられる。 ついでにapacheの設定。 mongrel_clusterだとapacheのロードバランサを使ったけど、unicornはそれ自体がロードバランサになるので全部masterに振り向ければいい。

 ProxyRequests Off
<Proxy *>
  Order deny,allow     Allow from all
</Proxy>
ProxyPass / http://localhost:5001/
ProxyPassReverse / http://localhost:5001/

こんだけ。

色々分かってなくて手探りだったので、もうちょっと最適化できると思う。 特にunicorn.conf.rbは無駄が多い気がする…