Yak shaving logs


My life is just yak shaving.

godを使ってunicornのプロセスが死んでしまったら自動でunicornのプロセスを立ち上げる

2017/11/10 #rails #unicorn #god

By Yusuke Takita


こんにちは。Takitaです。

なぜかunicornのプロセスが死んでしまいサービスがエラーになるというケースがありました。
原因調査と並行してunicornのプロセスが死ぬと自動で立ち上げられるようにしました。

環境

  • Rails 4.2.0
  • Capistrano 3.4.1
  • God 0.13.7
  • AWS EC2

コード

  • Gemfile
+gem 'god'
  • unicorn.god
+RAILS_ENV  = ENV['RAILS_ENV']  || 'production'
 +RAILS_ROOT = ENV['RAILS_ROOT'] || '/var/www/app_name/current'
 +
 +God.watch do |w|
 +  w.name     = 'unicorn'
 +  w.dir      = RAILS_ROOT
 +  w.interval = 30.seconds
 +
 +  w.start   = "bundle exec unicorn -c #{RAILS_ROOT}/config/unicorn.rb -E #{RAILS_ENV} -D"
 +  w.stop    = "kill -s -QUIT `cat #{RAILS_ROOT}/tmp/pids/unicorn.pid`"
 +  w.restart = "kill -s -USR2 `cat #{RAILS_ROOT}/tmp/pids/unicorn.pid`"
 +
 +  w.start_grace   = 10.seconds
 +  w.restart_grace = 10.seconds
 +  w.pid_file      = "#{RAILS_ROOT}/tmp/pids/unicorn.pid"
 +  w.log           = "#{RAILS_ROOT}/log/unicorn.god.log"
 +
 +  w.behavior(:clean_pid_file)
 +
 +  w.start_if do |start|
 +    start.condition(:process_running) do |c|
 +      c.interval = 5.seconds
 +      c.running  = false
 +    end
 +  end
 +
 +  # TODO: ひとまずprocessが死んだ時に再起動するだけにとどめたいのでコメントアウト
 +  # w.restart_if do |restart|
 +  #   restart.condition(:cpu_usage) do |c|
 +  #     c.above = 80.percent
 +  #     c.times = 5
 +  #   end
 +  # end
 +
 +  w.lifecycle do |on|
 +    on.condition(:flapping) do |c|
 +      c.to_state     = %i[start restart]
 +      c.times        = 5
 +      c.within       = 5.minutes
 +      c.transition   = :unmonitored
 +      c.retry_in     = 10.minutes
 +      c.retry_times  = 5
 +      c.retry_within = 2.hours
 +    end
 +  end
 +end
  • config/deploy.rb
after 'deploy:publishing', 'deploy:restart'

namespace :deploy do
-  desc 'Restart application'
-  task :restart do
-    on roles(:app), in: :sequence, wait: 5 do
-      invoke 'unicorn:legacy_restart'
-    end
+  # ress: https://gist.github.com/jtomaszewski/8091945
+  def god_is_running
+    capture(:bundle, "exec god status > /dev/null 2>&1 || echo 'god not running'") != 'god not running'
   end

-  desc 'Create database'
-  task :db_create do
-    on roles(:app) do
-      with rails_env: fetch(:rails_env) do
-        within release_path do
-          execute :bundle, :exec, :rake, 'db:create'
-        end
-      end
-    end
+  def config_file
+    "#{current_path}/unicorn.god"
   end

-  after :publishing, :restart
+  def start_god
+    execute :bundle, "exec god -c #{config_file}"
+  end

-  after :restart, :clear_cache do
-    on roles(:web), in: :groups, limit: 3, wait: 10 do
+  desc "Restart god's child processes"
+  task :restart do
+    on roles(:app) do
+      within current_path do
+        with RAILS_ENV: fetch(:rails_env) do
+          if god_is_running
+            execute :bundle, "exec god load #{config_file}"
+            execute :bundle, 'exec god restart'
+          else
+            start_god
+          end
+        end
+      end
    end
  end
end  

差分が見にくいので素のコードも貼っておきます。

after 'deploy:publishing', 'deploy:restart'

namespace :deploy do
  # ress: https://gist.github.com/jtomaszewski/8091945
  def god_is_running
    capture(:bundle, "exec god status > /dev/null 2>&1 || echo 'god not running'") != 'god not running'
  end

  def config_file
    "#{current_path}/unicorn.god"
  end

  def start_god
    execute :bundle, "exec god -c #{config_file}"
  end

  desc "Restart god's child processes"
  task :restart do
    on roles(:app) do
      within current_path do
        with RAILS_ENV: fetch(:rails_env) do
          if god_is_running
            execute :bundle, "exec god load #{config_file}"
            execute :bundle, 'exec god restart'
          else
            start_god
          end
        end
      end
    end
  end
end

動作確認

staging, productionともに以下の点を確認しました。

unicornのプロセスが死んだら再起動してくれるかを確認

まず現在のunicornプロセスを見つけます。

$ ps aux | grep unicorn
ubuntu    5522  2.9  0.3 398832 31520 ?        Sl   16:03   0:00 /var/www/app_name/shared/bundle/ruby/2.3.0/bin/god -c /var/www/app_name/current/unicorn.god
ubuntu    5551 80.6  2.6 1052516 218112 ?      Sl   16:03   0:08 unicorn master -c /var/www/app_name/current/config/unicorn.rb -E production -D
ubuntu    5559  0.0  2.5 1052380 206624 ?      Sl   16:03   0:00 unicorn worker[0] -c /var/www/app_name/current/config/unicorn.rb -E production -D
(省略)

$ kill -9 5522 をしてunicorn masterとworkderのプロセスをkillします。
すると以下のようにgodのプロセスだけ残り、unicornのプロセスはすべて落ちたことがわかります。
(topコマンドとかでもいいですが)

$ ps aux | grep unico
ubuntu    5522  1.4  0.3 398832 31520 ?        Sl   16:03   0:00 /var/www/app_name/shared/bundle/ruby/2.3.0/bin/god -c /var/www/app_name/current/unicorn.god
ubuntu    5610  0.0  0.0  10460   932 pts/0    S+   16:04   0:00 grep --color=auto unico

数十秒経つとunicornのプロセスが立ち上がります。

連続デプロイしても大丈夫か確認

godのプロセスが立ち上がっている状態でデプロイするとgodプロセスが複数立ち上がってしまったような気がしたので(少し前に対応したのであまり覚えてない)、
連続したデプロイでもプロセスが期待通りかどうかを念のため確認しておきました。

さいごに

今回は以前少し触ったことがあるGodを採用しましたが、Monitも機会があれば触ってみたいなと思います。

参考

投稿が古かったりしますが、以下が非常に参考になりました。

https://gist.github.com/jtomaszewski/8091945
http://d.hatena.ne.jp/nekonokataomoi/20140214

このエントリーをはてなブックマークに追加

categories


最新記事


tags