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