こんにちは、虎の穴開発室です。
Railsでバックグラウンドで非同期処理を行う一般的な方法として、delayed_jobがよく使われます。このdelayed_jobにちょっとした工夫を加えることで、実運用を想定した以下要件を満たすことができます。
要件
- メール送信とバッチ処理などdelayed_jobプロセスを分割する
- delayed_jobプロセスの死活を監視し、死んでいたら再起動する
- 再起動の結果をslackに通知する
メール送信とバッチ処理などdelayed_jobプロセスを分割
メール送信とバッチ処理など複数の性質の異なるジョブを1つのdelayed_jobで実行すると、バッチ処理が終わるまでメール送信ができない状態になってしまいます。そんな時は名前付きキューを利用してジョブの種類ごとに名前を付け、さらにその名前ごとにdelayed_jobのプロセスを割り当てると便利です。
名前付きキューを利用してジョブを登録:
object.delay(:queue => 'mail').my_method
delayed_jobの起動:
cd /home/my_user/rails_app
bin/delayed_job -i 1 --queue=mail start
iオプションででこのdelayed_jobを一意に示す数値、queueオプションでこのdelayed_jobが引き受けるジョブを指定しています。起動すると以下のようになります。
ps aux|grep delayed
(略) ... 6:11PM 0:00.36 delayed_job.1
注意として、statusを見る際にはidentifierの指定が必要です。
cd /home/my_user/rails_app
bin/delayed_job -i 1 status
delayed_job.1: running [pid 30499]
bin/delayed_job status
delayed_job: no instances running
delayed_jobプロセスの死活を監視し、死んでいたら再起動する
delayed_jobが予期せぬ理由で死亡した場合に備え、プロセスの死活を監視して、死亡時には再起動をかけられるようにします。
方法としては対象のdelayed_jobのプロセス数を定期的に取得し、0だったら再起動をかけます。プロセス監視ツールなどを使う方法もありますが、今回はシンプルにシェルスクリプトをcronに登録する実装としました。※以下の例はidentifierが1のdelayed_jobの場合です
count=`ps -ef | grep "delayed_job.1" | grep -v grep | wc -l`
if [ $count = 0 ]; then
cd /home/my_user/rails_app
RAILS_ENV=production bin/delayed_job -i 1 --queue=mail restart
after_count=`ps -ef | grep "delayed_job.1" | grep -v grep | wc -l`
if [ $after_count = 0 ]; then
else
fi
else
echo "OK. mail delayed_job is running"
fi
上記をcronに追記します。
*/5 * * * * /bin/bash -l -c 'RAILS_ENV=production /home/my_user/rails_app/scripts/monitor_delayed_job.sh >> /home/my_user/rails_app/log/monitor_delayed_job.log 2>&1'
再起動の結果をslackに通知する
再起動が発生するということは何らかの原因でプロセスが死んだということですので、その時はすぐわかるようslackに通知をしておきます。
slackへの通知は事前にincoming webhookの設定が必要ですが、以下記事を参考に下さい。
toranoana-lab.hatenablog.com
Webhook URLを発行したらそのURLへPOSTでリクエストを行います。まとめると、スクリプトは以下のようになります。
count=`ps -ef | grep "delayed_job.1" | grep -v grep | wc -l`
if [ $count = 0 ]; then
cd /home/my_user/rails_app
RAILS_ENV=production bin/delayed_job -i 1 --queue=mail restart
after_count=`ps -ef | grep "delayed_job.1" | grep -v grep | wc -l`
if [ $after_count = 0 ]; then
msg="本番のxxxサーバでdelayed_jobが起動していません。再起動も失敗しました。"
else
msg="本番のxxxサーバでdelayed_jobが起動していません。再起動が完了しました。"
fi
echo $msg
curl -XPOST \
--data-urlencode "payload={'channel': '#test',
'username': 'xxxサイト監視bot',
'link_names' : 1 ,
'text': '@channel ${msg}' }" \
https://hooks.slack.com/services/xxxxxxxx
else
echo "OK. mail delayed_job is running"
fi
以上で後はプロセス死亡時に以下のような通知が来ます。
通知自体はインシデントなので真面目ですが、せめてアイコンだけでも可愛くしています。
最後に
今回紹介したスクリプトは実サービスでも運用しています。今のところは日陰者ですが、Webサービスとしてはそのほうがありがたいですね。
虎の穴では一緒に働く仲間を絶賛募集中です。興味を持っていただけた方はぜひ弊社の採用情報をご覧下さい。
www.toranoana.jp