【Ruby on Rails】whenever を使って定期的にバッチ処理を行う

概要

Ruby on Rails で定期的にバッチ処理を実行する方法をまとめます。

定期的に処理を実行したい場合、クーロンを利用するのが定石かと思います。

クーロンを実行するには、crontab に設定を記載する必要があります。

Ruby on Rails の場合、lib ディレクトリ内に置かれたモジュールや Rakefile に書かれたタスクを定期的に実行したいと思う事が多いと思います。

しかし、これらを処理を crontab に記載するのは慣れてないと少々難しいです。

何故なら、Rakefile のタスクだったり、モジュールの実行は、Rails アプリケーションの ROOT ディレクトリで実行する必要があるからです。

更に Rails の環境ごとに実行だったり、ログ出力だったりを考えると、どんどん複雑になってきます。

そこで、これらの設定を簡単に crontab に自動生成してくれる whenever と言う便利な gem があるので、それの使い方をまとめました。

 

導入方法

gem インストール

github.com

Gemfile に whenever を追記してインストールしてください。

$ vim Gemfile
gem 'whenever', require: false

# インストール
$ bundle install --path vendor/bundle

設定ファイル生成

Rails アプリケーションの ROOT ディレクトリで以下のコマンドを実行して下さい。

そうすると config ディレクトリ内に schedule.rb と言うファイルが生成されます。

この schedule.rb がクーロンの設定を記載するファイルです。

$ bundle exec wheneverize .
[add] writing './config/schedule.rb' # schedule.rb ファイルが生成
[done] wheneverized!

 

定期的に実行したい処理を記載

例として 2 つのバッチ処理を用意しました。

  • 毎週月曜日 AM 4:30 と PM 6:00 に Rakefile に記載された hoge タスクを実行する
  • 4 日おきに Hoge モジュールの hoge メソッドを実行する

crontab の書式と違って、schedule.rb の書式の方が書きやすいし、読みやすいと思います。

また、ruby ファイルなのでメソッドを使えたり、変数を使えたりするのが便利です。

$ vim config/schedule.rb
# Use this file to easily define all of your cron jobs.
#
# It's helpful, but not entirely necessary to understand cron before proceeding.
# http://en.wikipedia.org/wiki/Cron

require File.expand_path(File.dirname(__FILE__) + "/environment")

rails_env = Rails.env.to_sym
rails_root = Rails.root.to_s

# environment は設定しないと production になってしまう set :environment, rails_env set :output, "#{rails_root}/log/cron.log"
# 毎週月曜日 AM 4:30 と PM 6:00 に rake タスクを実行する every :monday, at: ['4:30 am', '6:00 pm'] do rake 'hoge' end # 4 日おきに Hoge モジュールの hoge メソッドを実行する every 4.days do runner 'Hoge.hoge' end

詳しい書き方は README.md を参考にして下さい。

whenever/README.md at master · javan/whenever · GitHub

 

設定の反映・削除など

設定ファイルが出来上がったら whenever のコマンドを実行して、crontab を生成します。

設定の確認
# 生成される crontab が問題ないか確認
$ bundle exec whenever
30 4 * * 1 /bin/bash -l -c 'cd /var/workspace/rails_app && RAILS_ENV=development bundle exec rake hoge --silent >> /var/workspace/rails_app/log/cron.log 2>&1'

0 18 * * 1 /bin/bash -l -c 'cd /var/workspace/rails_app && RAILS_ENV=development bundle exec rake hoge --silent >> /var/workspace/rails_app/log/cron.log 2>&1'

0 0 1,5,9,13,17,21,25,29 * * /bin/bash -l -c 'cd /var/workspace/rails_app && bundle exec bin/rails runner -e development '\''Hoge.hoge'\'' >> /var/workspace/rails_app/log/cron.log 2>&1'
設定の反映
$ bundle exec whenever --update-crontab
[write] crontab file updated

# crontab に設定が反映されたことを確認
$ crontab -l
# Begin Whenever generated tasks for: /var/workspace/rails_app/config/schedule.rb at: 2018-02-18 16:52:03 +0900
30 4 * * 1 /bin/bash -l -c 'cd /var/workspace/rails_app && RAILS_ENV=development bundle exec rake hoge --silent >> /var/workspace/rails_app/log/cron.log 2>&1'

0 18 * * 1 /bin/bash -l -c 'cd /var/workspace/rails_app && RAILS_ENV=development bundle exec rake hoge --silent >> /var/workspace/rails_app/log/cron.log 2>&1'

0 0 1,5,9,13,17,21,25,29 * * /bin/bash -l -c 'cd /var/workspace/rails_app && bundle exec bin/rails runner -e development '\''Hoge.hoge'\'' >> /var/workspace/rails_app/log/cron.log 2>&1'

# End Whenever generated tasks for: /var/workspace/rails_app/config/schedule.rb at: 2018-02-18 16:52:03 +0900
設定の削除
$ bundle exec whenever --clear-crontab
[write] crontab file

# crontab から設定が削除されたことを確認
$ crontab -l
# 何も出てこなければOK

 

生成された crontab を見ると以下の処理をワンライナーで実行してくれているのが分かります。

  1. Rails アプリケーションの ROOT ディレクトリに移動
  2. 環境変数をセット (rake の場合) or 環境を指定 (rails runner の場合)
  3. コマンドを実行
  4. 標準エラーを標準出力にマージしてログ出力

 

まとめ

whenever を利用する事で Rakefile に記載されたタスクや、lib ディレクトリ内で管理されたモジュールを簡単に呼び出して crontab に記載する事ができました。

複雑なワンライナーを crontab に自動生成してくれる点以外にも、以下のメリットがあると思います。

  • schedule.rb の方が書式が見やすい・書きやすい
  • ruby ファイルなので凝った事ができる
  • 設定ファイルを config ディレクトリ内で一元管理できる

「設定ファイルを config ディレクトリ内で一元管理できる」メリットとは、crontab.conf などを別途作って GitHub で管理する場合、何処のディレクトリに配置すればいいのか悩みます。

schedule.rb ならば Rails アプリケーションの config ディレクトリ内に配置できるので、設定ファイルを一元管理する事もできて分かりやすいと思いました。

whenever では他にもメールを飛ばしたりなど、色んな機能があるので、是非 README.md を読んでみてください。