こんにちは、現役沖縄フリーランスエンジニアのmahです。
このブログでは、
僕がIT未経験から約1年でフリーランスエンジニアになるまでの過程、
ノウハウなどを書いていきます。
今回は、
- Railway.appでWheneverを使わずHeroku Schedulerのように定期処理を実行する
についてです。
【Heroku無料廃止】Railway.appでWheneverを使わずHeroku Schedulerのように定期処理を実行する
背景
HerokuからRailway.appに移行するため、
gem Whenever を使ってcron設定をしていましたが、
設定したrakeタスクが動かない。
以下、
Railway.appで設定しているビルド時、ビルド完了後に実行するコマンド。
- Build Command
$ mkdir -p tmp/pids && sudo apt-get -y update && sudo apt-get -y upgrade && sudo apt-get -y install cron
- Start Command
$ sudo cron && sudo service cron restart && bundle exec whenever --set environment=production --update-crontab && crontab -l && bundle exec puma -C config/puma.rb
Railway.appに出ているログは下記。
- Deploy Logs
restarting periodic command scheduler: cronStopping periodic command scheduler: cron. Starting periodic command scheduler: cron. task1 task2 [write] crontab file updated # Begin Whenever generated tasks for: /app/config/schedule.rb at: 2022-09-18 00:49:52 +0000 2 * * * * /bin/bash -l -c 'cd /app && RAILS_ENV=production bundle exec rake task1 --silent >> log/cron_log.log 2>&1' */1 * * * * /bin/bash -l -c 'cd /app && RAILS_ENV=production bundle exec rake task2--silent >> log/cron_log.log 2>&1' # End Whenever generated tasks for: /app/config/schedule.rb at: 2022-09-18 00:49:52 +0000 [1] Puma starting in cluster mode... [1] * Version 4.3.12 (ruby 2.6.7-p197), codename: Mysterious Traveller [1] * Min threads: 5, max threads: 5 [1] * Environment: production [1] * Process workers: 1 [1] * Phased restart available [1] * Listening on tcp://0.0.0.0:6535 [1] Use Ctrl-C to stop [1] - Worker 0 (pid: 194) booted, phase: 0 [1] - Gracefully shutting down workers...
ローカルで検証している時はこれで各rakeタスクがスケジュール毎に実行されるのですが、
Railway.appの環境だと実行されません。
Wheneverのcron設定ファイルやRailway.app側の実行コマンドを変えて検証していましたが、
なかなか終わらないので発想を変えます。
rakeタスクを実行するエンドポイントを作り、
そこにNew Relicから一定時間毎にpingを送るようにすることで、
実質Herokuスケジューラと同様の挙動をさせました。
これでWheneverを使わずHeroku Schedulerのような定期処理が可能です。
手順
1. ping先のルーティング定義
何でも良いですが、
今回は /ping_tasks/タスク名
というURLにpingでリクエストされたらrakeタスクを実行するようにします。
# config/routes.rb resources :ping_tasks, only: :none do collection do get :task1 # GET /ping_tasks/task1 get :task2 # GET /ping_tasks/task2 end end
2. コントローラ作成
/ping_tasks/タスク名
というルーティングに倣って ping_tasks_controller
というコントローラを作成します。
# app/controllers/ping_tasks_controller.rb class PingTasksController < ApplicationController end
3. rakeタスク毎にアクション定義
rakeタスク毎の名前でアクションを作成します。
# app/controllers/ping_tasks_controller.rb class PingTasksController < ApplicationController def task1 `RAILS_ENV=#{Rails.env} bundle exec rake task1 --trace` head :no_content end def task2 `RAILS_ENV=#{Rails.env} bundle exec rake task2 --trace` head :no_content end def health_check `RAILS_ENV=#{Rails.env} bundle exec rake health_check --trace` head :no_content end end
4. New Relicからpingの設定
New Relicからpingを送る設定をします。
まずNew Relicで新規登録してログインしたら、
左サイドバーの synthetic monitoring
選択し、
右上にある create monitor
をクリック。
次に ping
を選択。
続いてmonitorの名称、ping先のURL、時間間隔などを入力します。
最後にlocationの設定。
済んだら save monitor
をクリックして作成完了。
run check
を実行して成功していたら、
Railway.app側のログをチェックしてrakeタスクが実行されていればOKです。
5. New Relicのリクエストの場合のみ実行するように簡易なバリデーションをつける
パラメータに new_relic='true'
がある時だけ実行するようにします。
New Reric側のpingURLの末尾に ?new_relic=true
を付けます。
コントローラーにコードを追加します。
class PingTasksController < ApplicationController before_action :from_new_relic? <= 追加 def task1 `RAILS_ENV=#{Rails.env} bundle exec rake task1 --trace` head :no_content end def task2 `RAILS_ENV=#{Rails.env} bundle exec rake task2 --trace` head :no_content end def health_check `RAILS_ENV=#{Rails.env} bundle exec rake health_check --trace` head :no_content end private <= 追加 def from_new_relic? <= 追加 return head :no_content if params[:new_relic].blank? || params[:new_relic].to_s != 'true' end end
この状態で、
リクエストしたパスが /ping_tasks/task1
や /ping_tasks/task1?new_relic=
の時にはtask1は実行されず、
/ping_tasks/task1?new_relic=true
の時にのみ task1 が実行されていればOK。
以上です。