Railsアプリの本番環境での運用 -godとか-
perlでdaemontools + Server::Starterで面倒見てたあたりをRailsでもしっかりやりたいなぁと。
以外としっかりとやってるサンプルがウェブに落ちてなかったので、いろいろ調べました。
いろいろ検証してみたけど、最終的に以下のように落ち着きそうです。試行錯誤した中身もまとめたい気もする。
やること
- graceful restart
- Railsアプリの永続化
- 永続化してるやつの自動起動とか永続化とか
- capistranoからアプリの再起動などできるように
終着点
Railsアプリをgod + unicornで上げて、godをinit script置いてchkconfigで自動起動とかの設定。
あと、capistranoからgod監視下のアプリを再起動できるように
unicornをgodで動かす
よくあるやつ。
rails_env = ENV['RAILS_ENV'] || 'production' rails_root = ENV['RAILS_ROOT'] || '/path/to/application' God.watch do |w| w.name = 'application_name' w.interval = 30.seconds w.uid = 'app' w.gid = 'app' w.start = "bundle exec -- unicorn_rails -c #{rails_root}/config/unicorn.rb -E #{rails_env} -D" w.stop = "kill -QUIT `cat #{rails_root}/tmp/pids/unicorn.pid`" w.restart = "kill -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 = File.expand_path(File.join(File.dirname(__FILE__), '..', 'log', '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 w.restart_if do |restart| restart.condition(:memory_usage) do |c| c.above = 300.megabytes c.times = [3, 5] end restart.condition(:cpu_usage) do |c| c.above = 50.percent c.times = 5 end end w.lifecycle do |on| on.condition(:flapping) do |c| c.to_state = [: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
init script
はこんな感じ。
/etc/init.d/godに置く。
#!/bin/bash . /etc/rc.d/init.d/functions RBENV_ROOT=/opt/rbenv RBENV_VERSION="" export RBENV_VERSION PATH=$RBENV_ROOT/shims:$RBENV_ROOT/bin:/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin RUBY_PATH=$RBENV_ROOT/shims/ruby DAEMON=$RBENV_ROOT/shims/god PID_FILE=/var/run/god.pid LOG_FILE=/var/log/god.log SCRIPT_NAME=/etc/init.d/god CONFIG_FILE_DIR=/etc/god #DEBUG_OPTIONS="--log-level debug" DEBUG_OPTIONS="" test -x $DAEMON || exit 0 RETVAL=0 god_start() { start_cmd="$DAEMON -l $LOG_FILE -P $PID_FILE $DEBUG_OPTIONS" echo $start_cmd $start_cmd || echo -en "god already running" RETVAL=$? if [ "$RETVAL" == '0' ]; then sleep 2 if [ -d $CONFIG_FILE_DIR ]; then echo "god: loading master config" $DAEMON load $CONFIG_FILE_DIR/master.conf fi fi return $RETVAL } god_stop() { stop_cmd="god terminate" echo $stop_cmd $stop_cmd || echo -en "god not running" } case "$1" in start) god_start RETVAL=$? ;; stop) god_stop RETVAL=$? ;; restart) god_stop god_start RETVAL=$? ;; status) $DAEMON status RETVAL=$? ;; *) echo "Usage: god {start|stop|restart|status}" exit 1 ;; esac exit $RETVAL
で、/etc/god/master.confに各設定のloadを書く。
ここは、/etc/god以下にconfig fileを複数用意するのもありだけど、
master.confだけ置いて中でloadする形にした。
capistranoのtask.
capistrano 3なので、lib/capistrano/tasks/god.capとかに置く感じで。
namespace :god do task :environment do set :god_config, "#{current_path}/config/god/#{fetch(:rails_env)}.rb" end task :start do on roles(:web) do execute :sudo, "/opt/rbenv/shims/god start #{fetch(:application)}" end end task :restart do on roles(:web) do execute :sudo, "/opt/rbenv/shims/god restart #{fetch(:application)}" end end task :stop do on roles(:web) do execute :sudo, "/opt/rbenv/shims/god stop #{fetch(:application)}" end end end
これでcapistrano経由でrestartとかできる。
god(というかrbenv)のpathは分離したい感じだ。
capistrano-unicornとか使うとcapistrano経由でunicornでアプリ上げれるけど、永続化とかサーバ再起動時にアプリがあがらないのとか気になってたのでちゃんと調べた。冗長なサーバ構成である程度アプリ落ちても動作する+監視で落ちたのに気づけるというところで少しは大丈夫なところもあるのかもと思いもしたけど、やっぱり気になったので。