RailsにはRakeというgemが標準搭載されており、Railsで定期的に実行したい処理をRakeタスクとして定義しておくことで、必要なときに呼び出して実行することができます。
Rakeタスクを作成するメリットとしては、アプリケーションを起動せずにターミナル上で実行でき、サーバを起動していなくても定義した処理を実行できる点にあります。
そして、Rakeタスクをバックグラウンドで定期的に実行するためにはwheneverというgemを利用します。
Rakeタスクの処理を自動化したいなら、wheneverは是非とも導入しておきたい。
そこで、今回はRakeタスクをwheneverで定期的に実行する方法について覚書としてまとめました。
開発環境
- Ruby 3.1.2
- Ruby on Rails 7.0.4
- M1 Macbook Air 2020
- mac OS Monterey (ver. 12.4)
- ターミナル bash (Rosetta 2 使用)
Rakeタスクの作成&実行
まずはRakeタスクの作成&実行を進めていきます。
ここでは例として、ActiveStorageでDBにアップロード済みのファイル(画像)の中から、モデルに紐付けされなかった不要ファイルを削除するタスクを作成していきます。
Rakeタスクファイルの作成
以下のコマンドを実行してlib/tasks
以下にタスクファイルを作成します。
$ rails g task unattached_images
lib/tasks/unattached_images.rake
のように作成されていればOKです。
タスクファイルに処理内容を記述
lib/tasks/unattached_images.rake
に、以下のようにタスクの処理内容を記述します。
namespace :unattached_images do
desc "紐付けされなかったアップロード(active_storage_blobs)を削除する"
# タスク名(purge)を指定
task purge: :environment do
ActiveStorage::Blob.unattached.find_each(&:purge) # ←処理内容
end
end
タスクの実行
以下のコマンドでタスクを実行します。
$ rails unattached_images:purge
実際にタスクが実行できているか確認してみます。
Rakeタスク実行前のデータベース上のファイルは以下の通り。
Rakeタスクを実行すると、、
記述したタスクがちゃんと実行できていることが確認できました。
wheneverでRakeタスクを定期的に実行する
Rakeタスクの作成&実行ができたので、次はwheneverでRakeタスク処理を自動化していきます。
wheneverの導入
以下のように記述し、gemをインストールします。
gem "whenever", require: false
$ bundle install
設定ファイル(schedule.rb)の作成
以下のコマンドを実行すると設定ファイルconfig/schedule.rb
が生成されます。
$ bundle exec wheneverize
設定ファイル(schedule.rb)の編集
設定ファイルconfig/schedule.rb
を以下のように編集します。
require File.expand_path(File.dirname(__FILE__) + "/environment") # Rails.root(Railsメソッド)を使用するために必要
rails_env = ENV['RAILS_ENV'] || :development # cronを実行する環境変数(:development, :product, :test)
set :environment, rails_env # cronを実行する環境変数をセット
set :output, "#{Rails.root}/log/crontab.log" # cronのログ出力用ファイル
every 1.day do # タスクの実行間隔
rake "unattached_images:purge" # ← rake "タスクのファイル名 : タスク名"
end
以下、タスクスケジュールのサンプルです。
・・・
# ------- スケジュールのサンプル --------
every :hour do # 1時間ごとに実行
rake "unattached_images:purge"
end
every 1.day, at: '8:30 am' do # 毎日AM8:30に実行
rake "unattached_images:purge"
end
every 1.day, at: ['8:30 am', '5:00 pm'] # 毎日2回、指定した時間に実行
rake "unattached_images:purge"
end
every :sunday, at: '5:00 pm' do # 指定した曜日、時間に実行
rake "unattached_images:purge"
end
設定内容に間違いがないかチェック
設定ファイルの編集が終わったら、以下のコマンドを実行しエラーがないかチェックします。
問題なければ以下のような表示になるはずです。
$ bundle exec wheneverize
[skip] `./config/schedule.rb' already exists
[done] wheneverized!
cronにデータを反映&実行
最後に、cronにデータを反映かつ実行させるために以下のコマンドを実行します。
$ bundle exec whenever --update-crontab
[write] crontab file updated
これで、指定したスケジュールに沿ってRakeタスクが自動的に実行されるようになりました。
ちなみに、現状のcrontabの記述内容を確認する場合は以下のコマンドを実行します。
$ crontab -l
# Begin Whenever generated tasks for: /Users/xxxxxx/.../Railsアプリ/config/schedule.rb at: 2022-10-12 21:33:11 +0900
0 * * * * /bin/bash -l -c 'cd /Users/xxxxxx/.../Railsアプリ && RAILS_ENV=development bundle exec rake unattached_images:purge --silent >> /Users/xxxxxx/.../Railsアプリ/log/crontab.log 2>&1'
# End Whenever generated tasks for: /Users/xxxxxx/.../Railsアプリ/config/schedule.rb at: 2022-10-12 21:33:11 +0900
上記の/bin/bash -l -c 'cd ・・・ --silent'
のコマンドを実行することでも、cronを実行させることができます。
$ /bin/bash -l -c 'cd /Users/xxxxxx/.../Railsアプリ && RAILS_ENV=development bundle exec rake unattached_images:purge --silent'
【Mac】”Operation not permitted – getcwd (Errno::EPERM)” が出た時の対処法
Macの場合、cronを実行しようとすると以下のようなエラーが出ることがあります。
(以下、log/crontab.log
より一部抜粋)
cut: /Users/xxxxxxxx/……/.ruby-version: Operation not permitted
/System/Library/Frameworks/Ruby.framework/Versions/2.6/usr/lib/ruby/2.6.0/rubygems/bundler_version_finder.rb:80:in `pwd’: Operation not permitted – getcwd (Errno::EPERM)
これは、Macにインストールされているcronへのアクセスが許可されていないために起こっていると考えられます。
そこで、Railsアプリケーションからcronにアクセスできるよう設定します。
Macのシステム環境設定の「セキュリティとプライバシー」を開き、
以下のように「ターミナル」と「cron」の項目にチェックを入れてアクセスを許可します。
僕の環境では上記画面上に「cron」が表示されていなかったので、「+」より追加しました。
cronアプリケーションの場所は以下のコマンドで調べられます。
$ which cron
/usr/sbin/cron
(Command
+ Shift
+ .
で隠しフォルダ、隠しファイルを表示できます)
アクセス許可をしたら、ターミナルを再起動してから以下のコマンドを実行してみます。
$ bundle exec whenever --update-crontab
これでcronが実行できるようになるはず。
(cronのタスク実行時にlog/crontab.log
にエラーが吐き出されなければ成功です)
以上、参考になれば幸いです。
コメント