IT・技術研修ならCTC教育サービス

サイト内検索 企業情報 サイトマップ

研修コース検索

コラム

Ruby & Rails

CTC 教育サービス

 [IT研修]注目キーワード   Python  UiPath(RPA)  最新技術動向  OpenStack  システムトラブルシュート 

第22回 『Whenever』を使ってタスクを自動化! (藺藤卓実) 2016年2月

 こんにちは。 ゼネットの藺藤です。
 さて、みなさんは"自動化"はお好きですか? 私は好きです。(*1) 今回のコラムでは、Rails上で定期実行するジョブの管理に便利なGem、「Whenever」を紹介します。(*2,*3,*4) 
 Linux系OSでは、定期的にジョブを実行するための仕組みとしてcron (crontab)が用意されていますが、「Whenever」はcronの仕組みをより扱いやすいようにRubyを使って記述・拡張したツールであると言えます。
 Railsアプリケーションにおいて自動化したいケースとしては、「大量データに対する集計・統計処理」、「logファイルの定期的な削除」、「非同期で処理すべきタスクの実行(例えば、指定時刻にメール送信)」等が挙げられるでしょうか。 ここで述べたような要件がある場合には、「Whenever」の導入を検討するのもよいでしょう。
 それでは早速「Whenever」の使い方を見てゆきましょう。 なお、今回の動作確認環境は以下の通りです。

  • CentOS    6.7
  • Ruby      2.1.5p273
  • Rails     4.2.4
準備

まずは新規のRailsアプリケーションを作成します。 私は「whenever_sample」という名称にしました。

$ cd ~/work
$ rails new whenever_sample

 次に、「Whenever」を使えるようにするため、Gemファイルに以下を追記します。

[Gemfile]

 (初期設定されているgemの後ろに次を追加)
gem 'whenever', require: false

bundle install を実行します。

 $ bundle install
 ...
 Installing whenever 0.9.4 
 ...

「Whenever」のインストールに成功しました。 現在のバージョンは 0.9.4 のようです。

 ジョブの登録は config/schedule.rb に対して行います。 ファイルの生成には次のコマンドを利用します。

$ wheneverize .

 [add]  writing './config/schedule.rb'
 [done] wheneverized!

schedule.rb ファイルが生成されました。

ジョブの作成

生成されたconfig/schedule.rb にはいくつかの例が挙げられています。 初めにこれを読み解きながら、使い方を見てみます。

[config/schedule.rb]

# Example:
  #
  # set :output,  "/path/to/my/cron_log.log"    ...(1)
  #
  # every 2.hours do                            ...(2)
  #   command  "/usr/bin/some_great_command"
  #   runner "MyModel.some_method"
  #   rake "some:great:rake:task"
  # end
  #
  # every 4.days do                             ...(3)
  #   runner  "AnotherModel.prune_old_records"
  # end

(1) の「set :output」ではログファイルの出力先を指定します。 例えば、次のように指定することでRailsの標準的なログファイルの位置に出力されます。 今回は筆者の作成したプロジェクトの位置を含むパスを指定します。

set :output, '/home/ti/work/whenever_sample/log/cron.log' 

これで 'log' ディレクトリ以下にwheneverのログが書きだされるようになります。

 

(2) では「every 2.hours do ... end」の記述により2時間毎にジョブを実行するように指定しています。 実際のジョブの内容はメソッド呼出しのブロックに与えます。 ブロック内では利用したいものに応じて以下のような記述を加えます。

『command』
  sh 等のOS上のコマンドを実行したい場合に利用します。 
  引数にはコマンドを渡します。 フルパスで指定する方が後々安心でしょう。
『runner』
  モデルに定義されたメソッドを呼び出したい場合に使用します。 
  引数には「モデル名」と「メソッド名」をドットで連結した文字列を渡します。
『rake』
  その名の通り、実行させたい rake コマンドを指定します。

複数のコマンドがありますが、使い分けの判断に迷うようでしたらとりあえず以下のような基準で考えてみてはいかがでしょうか。

  • ユーザに直接的な便益をもたらす機能はRails上に実装されるべきでしょう。 このため、『runner』によって呼出すことが自然です。 例えば、ユーザの指定時刻にメールを送信する機能を実装するのであれば、「送信待ち(未送信)」のものが無いか定期的にチェックし、送信条件を満たすものを実際に送信することになります。 Webメール機能を持つのであれば、即座に送信する場合もあると思われます。 このような場合、送信に必要なものは例えばActionMailer を用いてRails上に実装されると期待されます。 他にも「人気記事のランキング集計機能」といったものもこちらに属するでしょう。
  • 一方、ユーザに間接的な便益のみもたらす機能(システムの管理上必要なもの)に関しては、『command』や『rake』を利用することになります。 こちらの例としては、「定期的に不要となったログファイルを削除」するといった用途が考えられます。

 

(3) では「every 4.days do ... end」の記述により4日毎にジョブを実行するように指定しています。 この例のように、プログラムの意味するところがプログラマにとって非常に解りやすい(読みやすい)のが Rubyとその文化の良いところですよね。

ジョブの実行時間指定あれこれ

 ここでジョブの実行時間の指定方法についてまとめておきます。

(1)実行間隔の指定

 先ほどまでの例で見てきた 「2.hours」, 「4.days」 等の指定に加え、以下のような指定も利用できます。

  [config/schedule.rb]

# (1) 実行間隔の指定 
#
# 一時間毎に実行する
every :hour do  #他にも :day, :month,  :year, :reboot 等が利用可
  runner "ViewRanking.sum_up"
end
(2) 実行日の指定

 「特定の曜日に実行したい」、あるいは「週末にのみ実行したい」といった場合には次のように指定できます。

[config/schedule.rb]

# (2) 実行日の指定
#
# 金曜日に実行する
every :friday do  #他にも :sunday や  :weekday, :weekend 等
  runner "Report.send_summary_mail"
end
(3) 実行時刻の指定

 実行される時刻まで指定したい場合には次のように記述します。

[config/schedule.rb]

# (3) 実行時刻の指定
#
# 毎日"3:00"に実行する
  every :day, at: "3:00 am" do"
runner "Task.count"
end
(4) 生の'crontab'形式での指定

 crontabの形式で直接指定することも可能です。

[config/schedule.rb]

# (4) crontab形式での指定
#
# 左から「分」「時」「日」「月」「曜日」
every '0 0 10-31 *  *' do 
  runner "ComplecatedTask.count"
end

もしかすると、「生のcrontab形式で指定するのなら、crontabコマンドを使って直接編集しても構わないのでは?」と思われるかもしれません。 しかし、config/schedule.rb にまとめて宣言しておくことで「このアプリケーションが必要としているジョブはどれなのか」を明示できます。 また、別のサーバへの適用も楽になります。
 それほど複雑なケースでなければ crontab形式以外の記述を採用すべきでしょう。 圧倒的に可読性が向上します。

作成したジョブの確認と登録

さて、ここまでの記述が正しいものかチェックしましょう。 「whenever」 コマンドを実行します。 コマンドの実行はアプリケーション・ディレクトリの直下で行ってください。

$ whenever 

0 * * * * /bin/bash -l -c "cd /home/ti/work/whenever_sample &&
  bin/rails runner -e production "\""ViewRanking.sum_up"\"""
0 0 * * 5 /bin/bash -l -c "cd /home/ti/work/whenever_sample && 
  bin/rails runner -e production "\""Report.send_summary_report"\"""
0 3 * * * /bin/bash -l -c "cd /home/ti/work/whenever_sample && 
  bin/rails runner -e production "\""Task.count"\"""
0 0 10-31 * * /bin/bash -l -c "cd /home/ti/work/whenever_sample &&
  bin/rails runner -e production "\""ComplecatedTask.count"\"""
# [message] Above is your schedule file converted to cron syntax; 
  your crontab file was not updated.
# [message] Run `whenever --help" for more options.

先ほどまでに作成した4つのジョブがcrontab形式でどのようになるのか表示されます。 出力の最後の方(オレンジの部分)でも述べられていますが、この段階ではまだ crontabファイルは更新されていません。 (従って、ジョブは実行されません)

ジョブの登録には以下のコマンドを実行してください。

$ whenever  --update-crontab
[write]  crontab file updated

ここまでで作成してきたジョブがcrontabに反映されたようです。 実際のcrontabファイルを確認しましょう。 次のコマンドを実行します。

$ crontab  -l

実行結果は省略しますが、「whenever」コマンドで表示した内容と同じジョブが登録されていることを確認できると思います。

なお、ジョブを取り消したい場合には「whenever -c 」コマンドが利用できます。

まとめ

今回は「Whenever」を紹介しました。 
「Whenever」の本家サイト(GitHubページ)には「Cron jobs in Ruby」とあります。 cronの利用が有効と思われるシチュエーションでは是非「Whenever」の活用をご検討下さい。
 今回触れなかった点に「CapistranoとWheneverの連携」があります。 こちらについては (*1) や (*3) が参考になります。

それでは、Enjoy Ruby!

注釈

*1 :筆者がコンピュータ(プログラム)に対して最も"すごい!"と思っている点はヒトの代わりに作業をしてくれる点、すなわち「自動化」にあります。 皆さんの周りには、コンピュータに任せられるムダな手作業がまだ残ってはいませんか?
*2 : 「Whenever」https://github.com/javan/whenever
*3 : 参考にさせていただきした。
  http://railscasts.com/episodes/164-cron-in-ruby?view=asciicast
*4 : 参考にさせていただきました。
  http://morizyun.github.io/blog/whenever-gem-rails-ruby-capistrano/

 


 

 [IT研修]注目キーワード   Python  UiPath(RPA)  最新技術動向  OpenStack  システムトラブルシュート