🐟

[Rails]Rails5 wheneverでRakeタスクを定期的に実行

2021/11/04に公開

Rakeとは

Rubyをインストールすると一緒にインストールされるrakeコマンドです。rubyで処理内容を定義するビルドツールです。

RakeコマンドとRakeタスクとは

rakeコマンドはmakeコマンド風にタスクを実行するためのタスクランナーで、実行する処理をRakeタスクと言います。また、定義する場所を「Rakefile」と呼びます。

簡単に言うと、Railsで定期的に実行したい処理をRubyコードでタスクとして定義しておき、必要なときに呼び出して実行することができる機能です。

特徴としてアプリケーションを起動せずにターミナル上で実行でき、サーバを起動せずに定義した処理を実行できます。

下記のようなときに使われます。

  • データの連携
  • データベースのバックアップ
  • 定期的なデータの更新や削除など

Rakeタスクの使い方

Rakeタスクファイルの作成

railsにおいてrakeタスクファイルを作成するには下記のコマンドを実行します。

$ rails g task task_sample

実行すると、lib/tasks以下のディレクトリにtask_sample.rakeというタスクファイルが生成されるので、このファイルに定義していきます。

※ rakeタスクを不適切なnamespaceへ追加しないようにします。

今回はメールに関するタスクなので、新たにディレクトリ分けします。

既存のタスクで関連するタスクがある場合は、同じnamespaceへ作成します。

rails g taske namespace(ディレクトリ名):新たに作成するRakeタスクファイル名

タスクファイルの記述

先程のコマンドによって作成されるタスクファイルは下記のようになっています。

lib/tasks/task_sample.rake

namespece :task_sample do
end

このブロック内に下記のように処理を記述していきます。

namespace :タスクのファイル名 do
	desc '実行する処理の説明'
	task :タスクの名前 do
		実行する内容
	end
end

具体例

lib/tasks/task_sample.rake

namespace :task_sample do
	desc 'Hello, Rake Task'
	task :hello do
		puts 'Hello, Rake!'
	end
end

DBに接続する場合

モデルにアクセスする場合は :environmentを付けます。

#モデルにアクセスする場合は :environment を指定します
namespace :task_sample do
	desc 'Hello, Rake Task'
	task hello: :environment do
		puts 'Hello, Rake!'
	end
end

作成したタスクの一覧

下記コマンドで作成したRakeファイルのタスクの名前と説明の一覧を取得できます。

$ bundle exec rake -T

rake hello  # Hello, Rake Task

タスクの実行

タスクの実行は下記コマンドです。

$ bundle exec rake タスクのファイル名
$ rake hello
Hello, Rake!

wheneverで定期的な処理を行う

先程のような処理を定期的に実行するにはwheneverというgemを使います。

wheneverはcrontab管理ライブラリです。wheneverを使ってcronを動かします。

また、wheneverはバックグラウンドで処理されるものであるため、Railsとは関係のないプロセスである。

cronは.zshとrbenvの環境では動いてくれません!!

job_typeでPATHを通す必要があるので注意!

cronとは

UNIX系のOSに入っているプログラムの一つです。事前に「指定した時間に、指定したプログラム」を動かしてくれるものです。また、定期的にコマンドを実行するためにメモリ場で常に命令を待機しているプロセスです。

crontab(コマンド)とは

cronに指示するときに使うコマンドです。

wheneverのインストール

gem 'whenever', require: false
bundle install

アプリケーション側から使わないのでrequire: falseを付けます。これで、想定外の挙動を減らすことができます。

設定ファイルschedule.rbを作成

bundle exec wheneverize

これにより、config/schedule.rbファイルが作成されます(プロジェクト内にconfigフォルダが既に存在していればOK)。

wheneverの3つのジョブタイプ

Wheneverには、commandrunnerrakeの3つのジョブタイプがあらかじめ定義されています。job_typeで独自のジョブタイプを定義することができます。

これらを使って実行させます。

rake

rakeタスク実行する

rake 'タスクのファイル名:タスク名'

command

bashコマンド実行

runner

Rails内のメソッド実行

job_typeで独自のジョブタイプを定義

例)

# .zshrcとrbenvのパスを指定するrakeを定義
job_type :rake, "source /Users/docha/.zshrc; export PATH=\"$HOME/.rbenv/bin:$PATH\"; eval \"$(rbenv init -)\"; cd :path && RAILS_ENV=:environment bundle exec rake :task :output"

# 毎日9:00に実行
every 1.day, at: '9:00 am' do
  rake 'article_mailer:article_mailer'
end

schedule.rbの編集

cronは.zshとrbenvの環境では動いてくれません!!

job_typeでPATHを通す必要があるので注意!

例)config/schedule.rb

# whenever 定期実行
#  rake 'ファイル名':タスク名'
# Rails.root(Railsメソッド)を使用するために必要
require File.expand_path(File.dirname(__FILE__) + '/environment')

# .zshrcとrbenvのパスを指定するrakeを定義
job_type :rake, "source /Users/docha/.zshrc; export PATH=\"$HOME/.rbenv/bin:$PATH\"; eval \"$(rbenv init -)\"; cd :path && RAILS_ENV=:environment bundle exec rake :task :output"

# cronを実行する環境変数(:development, :product, :test)
# 環境変数'RAILS_ENV'にセットされている変数またはdevelopmentを指定
# 自分の環境では'RAILS_ENV'はセットされていないのでnilです
rails_env = ENV['RAILS_ENV'] || :development

# cronを実行する環境変数をセット
# 今回はdevelopmentをセット
set :environment, rails_env

# cronのログの吐き出し場所
set :output, "#{Rails.root}/log/cron.log"

# 一時間毎に実行
every :hour do
  rake 'article_state_check:article_state'
end

# 毎日9:00に実行
every 1.day, at: '9:00 am' do
  rake 'article_mailer:article_mailer'
end

Example schedule.rb file

GitHub - javan/whenever: Cron jobs in Ruby

every 3.hours do # 1.minute 1.day 1.week 1.month 1.year もサポートしています。
  # 以下のタスクは順番ではなく並行で実行されます
  runner "MyModel.some_process"
  rake "my:rake:task"
  command "/usr/bin/my_great_command"
end

# 毎日 指定の時間
every 1.day, at: '4:30 am' do
  runner "MyModel.task_to_run_at_four_thirty_in_the_morning"
end

# 毎日 指定の時間2回
every 1.day, at: ['4:30 am', '6:00 pm'] do
  runner "Mymodel.task_to_run_in_two_times_every_day"
end

# 他にも次のショートカットがあります。 :hour, :day, :month, :year, :reboot
every :hour do 
  runner "SomeModel.ladeeda"
end

# 任意の曜日を選択または、 :weekend, :weekday
every :sunday, at: '12pm' do 
  runner "Task.do_something_great"
end

# 出力先logの指定
set :output, 'log/crontab.log'

# 実行環境の指定
set :environment, :development

設定内容にエラーがないか確認

これはあくまでもscheduleの中身をcrontab用に翻訳してくれるだけで、まだcrontabに登録されたわけではありません。

bundle exec whenever

cronにデータを反映

上記で設定した内容をcronに反映します。

bundle exec whenever --update-crontab

以上で完了です。

その他コマンド

現状のcrontabの記述内容の確認

crontab -l

現状のcrontabの記述内容の編集

crontab -e

cronからデータを削除

bundle exec whenever --clear-crontab

※ cronから強制削除(取り扱い注意)ディレクトリ名を変えたときなどは上記では消せないときに使用。

crontab -r

参考

https://qiita.com/Esfahan/items/e7a924f7078faf3294f2

https://qiita.com/suzuki-koya/items/787b5562d2ae1a215d94

https://github.com/javan/whenever

https://marimomemo.hatenablog.jp/entry/2017/06/25/200415

エラー以外のログを出力する場合

https://qiita.com/fkm_y/items/00920f076d32c8c49670

Discussion