Railsのcronジョブ:ベストプラクティス?


295

Rails環境でスケジュールされたタスクを実行する最良の方法は何ですか?スクリプト/ランナー?レーキ?数分ごとにタスクを実行したいと思います。


149
グーグルからここに来る人のために、より良いアプローチのために受け入れられた答えを超えて見てください。
jrdioko

4
いつでも答えは受け入れられた答えよりも合理的であるように見えます。これは古いハックです。
Rob

2
また、少なくとも1つの回答は、特定のgemがインストールされていることを前提としています。
Tass

いくつかの(私が知った)良い実践方法をここにまとめますwisecashhq.com/blog/writing-reliable-cron-jobs
ThibautBarrèreSep

多くの場合、cronジョブは悪臭を放ちます。sidekiq / resque(または他のバックグラウンドワーカー)を介したスケジューラの書き込み、またはデーモン(機能が少なく監視可能)の書き込みを改善します。Cronジョブには少なくともいくつかの悪い点があります。1)1つのインスタンスのロックは苦痛です。2)モニタリングが簡単にできない。3)例外処理は手動で再度記述する必要があります。4)再起動は簡単ではありません。5)バックグラウンドワーカーが簡単に解決できる上記のすべての問題。
Dmitry Polushkin 2015年

回答:


110

私はレイクアプローチを使用しています(herokuでサポートされています

lib / tasks / cron.rakeというファイルを使用します。

task :cron => :environment do
  puts "Pulling new requests..."
  EdiListener.process_new_messages
  puts "done."
end

コマンドラインから実行する場合、これは単に "rake cron"です。このコマンドは、必要に応じてオペレーティングシステムのcron /タスクスケジューラに配置できます。

更新これはかなり古い質問と答えです!いくつかの新しい情報:

  • 私が参照したheroku cronサービスは、Herokuスケジューラに置き換えられました
  • 頻繁なタスク(特に、Rails環境の起動コストを回避したい場合)の推奨アプローチは、システムcronを使用して、(a)セキュア/プライベートwebhook APIをポークして、必要なタスクをバックグラウンドで呼び出すスクリプトを呼び出すことですまたは(b)選択したキューイングシステムのタスクを直接エンキューする

この場合、cronエントリはどのようにする必要がありますか?OSはrakeタスクへの正しいパスを知っていますか?
jrdioko

13
注:最近はいつでも使用しています(ジムガービンの回答を参照)。ただし、rakeタスクを実行するための生のcronエントリは次のようになります。30 4 * * * / bin / bash -l -c 'cd / opt / railsapp && RAILS_ENV = production rake cron --silent '
tardate

1
コンソールからこれをどのように呼び出しますか?私はそうload "#{Rails.root}/lib/tasks/cron.rake"しましrake cronたが、NameError:undefined local variable or method `cron 'for main:Object
B Seven

3
このアプローチの問題は:environment依存関係です。起動に時間がかかる非常に重いRailsアプリケーションがあり、Rakeは毎分呼び出され、タスクを実行するRails環境を起動するためにより多くのリソースを消費します。すでに起動されている Rails環境をcronから呼び出すようにしたいのですが、コントローラーアプローチとrake環境の間の何かである必要があります
fguillen 2012年

このタスクの期間はどれくらいですか?if条件を使用しています。これが定期的に実行されていることを知りたい。これに関する情報はherokuのWebサイトにはありません。
Shubham Chaudhary、2015

254

私は非常に人気のあるWheneverを、スケジュールされたタスクに大きく依存するプロジェクトで使用してきました。crontab形式を処理する必要がなく、スケジュールされたタスクを定義するための優れたDSLを提供します。READMEから:

いつでも、cronジョブを記述およびデプロイするための明確な構文を提供するRuby gemです。

READMEの例:

every 3.hours do
  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

22
毎分実行すると、環境は毎回再起動され、コストがかかる可能性があります。と思われgithub.com/ssoroka/scheduler_daemonはこれを回避することができます。
lulalala、2011年

3
バージョン管理システムでcron構成を維持するための+1
brittohalloran

3
これが最善の解決策だと思います。Railsを使用している場合は、すべてをRailsで記述する方が良いと思います。このアプローチを使用すると、サーバーを変更するときにcronタスクを忘れることもできます。これはアプリとともに移動します。
エイドリアン・マッテオ

それが本当に役立つときはいつでも素晴らしいRailscastがあります(古い無料バージョンもアップしています)。
aceofbassgreg 2013

@ Tony、Wheneverは基本的にcronジョブを作成するためのドメイン固有の言語です。Railsサーバーで通常のcron構文にコンパイルされ、指定したジョブを実行するのはcronです(通常はrails runnerを介して)。
グレッグ

19

私たちのプロジェクトでは、最初にいつでも宝石を使用しましたが、いくつかの問題に直面しました。

次に、RUFUS SCHEDULER gemに切り替えました。このgemは、Railsでのタスクのスケジューリングにとって非常に簡単で信頼できることがわかりました。

毎週および毎日メールを送信するために、そしていくつかの定期的なレーキタスクまたは任意の方法を実行するためにさえ、それを使用しました。

この中で使用されるコードは次のようなものです:

    require 'rufus-scheduler'

    scheduler = Rufus::Scheduler.new

    scheduler.in '10d' do
      # do something in 10 days
    end

    scheduler.at '2030/12/12 23:30:00' do
      # do something at a given point in time
    end

    scheduler.every '3h' do
      # do something every 3 hours
    end

    scheduler.cron '5 0 * * *' do
      # do something every day, five minutes after midnight
      # (see "man 5 crontab" in your terminal)
    end

詳細:https : //github.com/jmettraux/rufus-scheduler


1
単純なルビープロジェクトまたは完全なRailsアプリの両方に使用したので、ルフスのために。
Paulo Fidalgo 2013年

8
Wheneverで遭遇した問題についてもう少し具体的に教えてください。
デューク

史上最高の答え
Darlan Dieterich

17

タスクの完了にそれほど時間がかからないと想定して、各タスクのアクションを持つ新しいコントローラーを作成してください。タスクのロジックをコントローラーコードとして実装してから、wgetを使用して適切な時間間隔でこのコントローラーのURLとアクションを呼び出すOSレベルでcronjobを設定します。この方法の利点は次のとおりです。

  1. 通常のコントローラーと同じように、すべてのRailsオブジェクトに完全にアクセスできます。
  2. 通常のアクションと同じように開発およびテストできます。
  3. 単純なWebページからタスクアドホックを呼び出すこともできます。
  4. 追加のruby / railsプロセスを起動して、これ以上メモリを消費しないでください。

12
他のユーザーがこのタスクにアクセスできないようにするにはどうすればよいですか?CPUを使用してそれを頻繁に呼び出すタスクが問題を引き起こす場合。
sarunw 09/12/12

44
私はこれが少し前だったことを知っていますが、これは間違いなくもはやcronジョブを実行する最良の方法ではありません。Rails環境にアクセスする方法が他にたくさんあるのに、なぜWebインターフェースを通過し、インターフェースが実際に表しているものに違反しているのですか?
マッチ

6
「タスクが完了するまでに時間がかからないことを前提としている」という条件は、非常に大きなもののようです。タスクが非常に高速である場合だけでなく、より一般的に役立つアプローチを使用するほうがよいのではないでしょうか。この方法では、このタスクまたはそのタスクを別のアプローチを使用して書き換える必要があるかどうかを常に再評価しているわけではありません。
iconoclast

77
この古い質問は、「rails cron」のGoogleでの上位の結果です。この答えは、最善のアプローチとはほど遠いものです。より健全な提案については、他の応答を参照してください。
ジム・ガービン

2
最善の方法ではありません。RESTサービスを呼び出さずにcronジョブを介してRails envにアクセスする方法は他にもたくさんあります。レーキアプローチは確かに優れています
シャイン

10

スクリプト/ランナーおよびrakeタスクは、cronジョブとして実行するのに完全に適しています。

ここでは、cronジョブを実行するときに覚えておく必要がある非常に重要な事項を1つ示します。おそらくアプリのルートディレクトリから呼び出されることはありません。これは、(ライブラリではなく)ファイルに必要なすべてのことを明示的なパスで行う必要があることを意味します:たとえば、File.dirname(__ FILE__)+ "/ other_file"。これはまた、別のディレクトリから明示的に呼び出す方法を知っている必要があることも意味します:-)

コードが別のディレクトリからの実行をサポートしているかどうかを確認してください

# from ~
/path/to/ruby /path/to/app/script/runner -e development "MyClass.class_method"
/path/to/ruby /path/to/rake -f /path/to/app/Rakefile rake:task RAILS_ENV=development

また、cronジョブはおそらくあなたのように実行されないので、.bashrcに入れたショートカットに依存しないでください。しかし、それは単なる標準のcronヒントです;-)


任意のユーザーとしてジョブを実行できます(必要なユーザーのcrontabエントリを設定するだけです)が、プロファイルとログインスクリプトが実行されず、ホームディレクトリで起動しないのは正しいことです。したがって、@ luke-franciのコメントに示されているように、コマンドを「cd」で開始するのが一般的です
Tom Wilson

10

いつでも(およびcron)の問題は、実行されるたびにRails環境がリロードされることです。これは、タスクが頻繁に行われる場合や、初期化作業が大量に行われる場合に実際の問題です。このため、生産に問題があり、警告する必要があります。

Rufusスケジューラーがそれを行います(https://github.com/jmettraux/rufus-scheduler

実行するジョブが長い場合は、delayed_job(https://github.com/collectiveidea/delayed_job)で使用します。

これが役に立てば幸いです!



10

興味深いのは、誰もSidetiqについて言及していないことです。Sidekiqを既に使用している場合は、これは素晴らしい追加です。

Sidetiqは、Sidekiqの定期的なワーカーを定義するためのシンプルなAPIを提供します。

ジョブは次のようになります。

class MyWorker
  include Sidekiq::Worker
  include Sidetiq::Schedulable

  recurrence { hourly.minute_of_hour(15, 45) }

  def perform
    # do stuff ...
  end
end

8

どちらも正常に動作します。私は通常スクリプト/ランナーを使用します。

次に例を示します。

0 6 * * * cd /var/www/apps/your_app/current; ./script/runner --environment production 'EmailSubscription.send_email_subscriptions' >> /var/www/apps/your_app/shared/log/send_email_subscriptions.log 2>&1

データベースに接続するための適切な構成ファイルをロードする場合、pure-Rubyスクリプトを記述してこれを行うこともできます。

メモリが貴重な場合に留意すべきことの1つは、スクリプト/ランナー(または「環境」に依存するRakeタスク)がRails環境全体をロードすることです。一部のレコードをデータベースに挿入するだけでよい場合は、実際には必要のないメモリを使用します。独自のスクリプトを作成すると、これを回避できます。実際にこれを行う必要はまだありませんが、検討中です。


8

Craken(Rake中心のcronジョブ)を使用する


1
cronジョブの作成は非常に難しいので、そのためのgemをダウンロードすることをお
勧め

1
難しいことではありませんが、それらをgitに保存し、デプロイ時に常に最新の状態にしておくことは、チームで作業するときに大きなプラスになります。
ThibautBarrère2014

5

私はbackgroundrbを使用しています。

http://backgroundrb.rubyforge.org/

これを使用して、スケジュールされたタスクや、通常のクライアント/サーバー関係では時間がかかりすぎるタスクを実行します。


3

cronタスクを設定する方法は次のとおりです。SQLデータベースのバックアップを毎日(rakeを使用して)作成するものと、月に1回キャッシュを期限切れにするものがあります。出力はファイルlog / cron_logに記録されます。私のcrontabは次のようになります。

crontab -l # command to print all cron tasks
crontab -e # command to edit/add cron tasks

# Contents of crontab
0 1 * * * cd /home/lenart/izziv. whiskas.si/current; /bin/sh cron_tasks >> log/cron_log 2>&1
0 0 1 * * cd /home/lenart/izziv.whiskas.si/current; /usr/bin/env /usr/local/bin/ruby script/runner -e production lib/monthly_cron.rb >> log/cron_log 2>&1

最初のcronタスクは、毎日のdbバックアップを作成します。cron_tasksの内容は次のとおりです。

/usr/local/bin/rake db:backup RAILS_ENV=production; date; echo "END OF OUTPUT ----";

2番目のタスクは後でセットアップされ、スクリプト/ランナーを使用して月に1回キャッシュを期限切れにします(lib / monthly_cron.rb):

#!/usr/local/bin/ruby
# Expire challenge cache
Challenge.force_expire_cache
puts "Expired cache for Challenges (Challenge.force_expire_cache) #{Time.now}"

私は他の方法でデータベースをバックアップできると思いますが、今のところそれは私にとってはうまくいきます:)

rakeおよびrubyへのパスは、サーバーによって異なる場合があります。以下を使用して、それらがどこにあるかを確認できます。

whereis ruby # -> ruby: /usr/local/bin/ruby
whereis rake # -> rake: /usr/local/bin/rake

3

SidekiqまたはResqueを使用すると、はるかに堅牢なソリューションになります。どちらもジョブの再試行、REDISロックによる排他性、監視、およびスケジューリングをサポートしています。

Resqueは死んだプロジェクト(積極的にメンテナンスされていない)なので、Sidekiqがより良い代替手段であることを覚えておいてください。また、パフォーマンスも向上しています。Sidekiqは単一のマルチスレッドプロセスで複数のワーカーを実行し、Resqueは各ワーカーを個別のプロセスで実行します。


それが正解です。などを絞ると制限、ジョブの数を失敗したか、スケジュール、実行しているユニークな労働者のために、簡単にそれらを再起動ロック:そのsidekiqまたはresqueは何が起こっているかを監視するためのWebインターフェイスのように、提供されている、素晴らしい機能を忘れることができます多くの
ドミトリーをPolushkin

3

私は最近取り組んでいるプロジェクトのためにいくつかのcronジョブを作成しました。

時計仕掛けの宝石がとても便利だと思いました。

require 'clockwork'

module Clockwork
  every(10.seconds, 'frequent.job')
end

この宝石を使用してバックグラウンドジョブをスケジュールすることもできます。ドキュメントと詳細なヘルプについては、https://github.com/Rykian/clockworkを参照してください



2

かつて同じ決断をしなければならなかったとき、今日その決断に本当に満足しています。使用resqueスケジューラを別々のRedisはあなたのDBから負荷を取るだけでなくので、あなたはまた、優れたユーザーインターフェースを提供しresque、ウェブのような多くのプラグインにアクセスする必要があります。システムの開発に伴い、スケジュールするタスクが増え、1つの場所からそれらを制御できるようになります。


1

おそらくそれを行うための最良の方法は、rakeを使用して必要なタスクを記述し、コマンドラインから実行することです。

railscastsで非常に役立つビデオを見ることができます

この他のリソースもご覧ください。


このチュートリアルで構文を使用しようとして失敗しました。タスクは実行されませんでした。
Tass

1

私は時計仕掛けの宝石を使用しましたが、それは私にはかなりうまくいきます。clockworkdスクリプトをデーモンとして実行できるようにするgem もあります。


0

よくわかりません。タスクに依存していると思います。実行する頻度、複雑さ、そしてRailsプロジェクトとの直接通信がどれだけ必要かなどです。何かを行うには、「最善の方法」1つだけあるのではないかと思います。、それを行うにはそれほど多くの異なる方法はありません。

Railsプロジェクトでの最後の仕事では、サーバーに時間があるときはいつでも計画されたメールを送信するバッチ招待メーラー(スパムではなく調査招待)を作成する必要がありました。デーモンツールを使用して、作成したRakeタスクを実行するつもりだったと思います。

残念ながら、私たちの会社にはいくつかのお金の問題があり、主要なライバルによって「購入」されたため、プロジェクトは完了しなかったため、最終的に何を使用したのかわかりません。


0

スクリプトを使用してcronを実行します。これは、cronを実行する最良の方法です。ここにcronのいくつかの例があります、

CronTabを開きます—> sudo crontab -e

そしてベローズラインを貼り付けます:

00 00 * * * wget https:// your_host / some_API_end_point

ここにいくつかのcron形式があります。

::CRON FORMAT::

cron形式テーブル

Examples Of crontab Entries
15 6 2 1 * /home/melissa/backup.sh
Run the shell script /home/melissa/backup.sh on January 2 at 6:15 A.M.

15 06 02 Jan * /home/melissa/backup.sh
Same as the above entry. Zeroes can be added at the beginning of a number for legibility, without changing their value.

0 9-18 * * * /home/carl/hourly-archive.sh
Run /home/carl/hourly-archive.sh every hour, on the hour, from 9 A.M. through 6 P.M., every day.

0 9,18 * * Mon /home/wendy/script.sh
Run /home/wendy/script.sh every Monday, at 9 A.M. and 6 P.M.

30 22 * * Mon,Tue,Wed,Thu,Fri /usr/local/bin/backup
Run /usr/local/bin/backup at 10:30 P.M., every weekday. 

これがあなたに役立つことを願っています:)

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.