unicorn/resqueのプロセス管理にeyeを使う
unicorn,resqueのプロセス管理に今回はeyeを使うことにします。 はじめBluepillで設定していたのですが、Ruby2.0環境だとBluepillのプロセスがゾンビで溜まりまくるという問題があり、代替をさがしていたところeyeというのがありました。God、Bluepillを参考に作られているということで設定も似た感じかな?と思い採用しています。今のところRuby2.x環境でも問題は起きていません。
EyeとMonitで監視対象のプロセスをわける
デプロイの際に再起動するプロセスをeyeで、それ以外をmonitで監視するようにします。
インストール
bundleでは無くgemで入れてます。
$gem install eye
eyeの起動方法はeye load 設定ファイルとなります。
設定ファイルはサーバの役割毎に用意しておきます。
各サーバに環境変数SERVER_ROLEというのを設定していて、この環境変数から読み込む設定ファイルが決まるようします。
これでappsサーバはunicornを起動する設定を読み込み、resqueサーバはresque関連を起動する設定を読み込むことになります。
eyeの設定ファイル
Railsプロジェクトのconfig/eye/以下に設定ファイルをそれぞれ配置しておきました。 以下はunicornとresque-workerプロセスを管理する設定例になります。 各プロセスの起動停止コマンドや、監視内容を設定しています(プロセスのメモリが300m超えたら再起動するとか)
- config/eye/staging-app.eye
# -*- coding: utf-8 -*- ENV['RAILS_ROOT'] ||= File.expand_path(File.join(File.dirname(__FILE__), "../..")) rails_root = ENV['RAILS_ROOT'] rails_env = ENV['RAILS_ENV'] stage = ENV['STAGE'] current_path = "/home/ec2-user/example/#{stage}/current" shared_path = "/home/ec2-user/example/#{stage}/shared/" # load submodules, here just for example Eye.load("./eye/*.rb") # Eye self-configuration section Eye.config do logger "/var/log/eye/eye.log" end Eye.application "#{stage}-app" do working_dir current_path process("unicorn") do pid_file "tmp/pids/unicorn.pid" start_command "bundle exec unicorn -Dc config/unicorn/#{stage}.rb -E #{rails_env}" stdall "log/unicorn.log" # stop signals: # http://unicorn.bogomips.org/SIGNALS.html stop_signals [:TERM, 10.seconds] # soft restart restart_command "kill -USR2 {PID}" check :cpu, :every => 30, :below => 99, :times => 3 check :memory, :every => 30, :below => 300.megabytes, :times => [3,5] start_timeout 120.seconds restart_grace 120.seconds monitor_children do stop_command "kill -QUIT {PID}" check :cpu, :every => 30, :below => 99, :times => 3 check :memory, :every => 30, :below => 300.megabytes, :times => [3,5] end end process ("resque_worker") do pid_file "tmp/pids/resque_worker.pid" start_command "bundle exec rake -t -f Rakefile environment resque:work" stop_signals [:QUIT, 5.seconds,:TERM, 5.seconds,:KILL] start_timeout 120.seconds restart_grace 120.seconds daemonize false env "RAILS_ENV" => rails_env, "QUEUE" => "*", "INTERVAL" => 1, "BACKGROUND" => "yes", "PIDFILE" => "tmp/pids/resque_worker.pid" stdall "log/resque_worker.log" monitor_children do stop_command "kill -QUIT {PID}" check :cpu, :every => 30, :below => 99, :times => 3 check :memory, :every => 30, :below => 300.megabytes, :times => [3,5] end end end
デプロイタスクも用意する
デプロイ時にunicorn,resqueの再起動が必要になるので、eyeを通して再起動を行えるようにタスクを書いておきます。 なお、unicornの再起動にはUSR2でのリスタートとプロセスを落として起動するの2つの方法で再起動できるようにそれぞれタスクを用意しました。 (ファイルアップロードのデプロイ時はUSR2リロードで良く、currentシンボリックリンクが切り替わる場合は停止、起動を行います)
- cap staging deploy:reload - eye restartが実行される(unicornのUSR2リロード)
- cap staging deploy:restart - eye stop,startが実行される)
lib/capistrano/tasks/eye.cap
namespace :eye do
desc "eye start"
task :start do
on roles(:app), in: :parallel do
execute "sudo /etc/init.d/eye start || true"
end
end
desc "eye stop"
task :stop do
on roles(:app), in: :parallel do
execute "sudo /etc/init.d/eye stop || true"
end
end
desc "eye restart"
task :restart do
on roles(:app), in: :sequence, wait: 10 do
execute "sudo /etc/init.d/eye restart || true"
end
end
desc "eye stop and start"
task :stop_start do
on roles(:app), in: :sequence, wait: 20 do |host|
puts host
if host.roles.include?(:web)
execute "ssh gateway elb_remove.rb #{fetch(:stage)} #{host} || true"
end
execute "sudo /etc/init.d/eye stop || true"
execute "sudo /etc/init.d/eye start || true"
puts "sleep 30"
sleep 30
if host.roles.include?(:web)
execute "ssh gateway elb_add.rb #{fetch(:stage)} #{host} || true"
end
end
end
desc "eye info"
task :info do
on roles(:app), in: :parallel do
count = 0
while capture("sudo /etc/init.d/eye info || true").match(/starting/) && count < 18
puts "loop"
count = count + 1
sleep 10
end
end
end
after :restart, "eye:info"
after :stop_start, "eye:info"
end