時計仕掛けのHeroku


皆さんこんにちは。増田です。

研究開発GではBlog執筆レースが密かに繰り広げられており、記事数、PV数を競って各メンバーが空き時間を見つけては、涼しい顔して実は必死にBlogを書いています。スタートが出遅れた私ですが、Herokuというネタの金脈を掘り当てたので時間さえあればバンバン記事を書いていきたいと思います。

今回のテーマは、Heroku上でコマンドを定期実行するためにはどうするの?
といったテーマに触れてみたいと思います。

コマンドの定期実行

そもそも指定時刻にコマンドを実行するには、どのようにしていますか?まさか、PCの前にスタンバイし、時計とにらめっこして時間が来たらEnterをエイヤッ!と叩いてるなんて訳ないですよね。Unix系のOSには、コマンドを定時実行するためのとても便利なコマンドが備わっています。

crontab(クロンタブ、あるいはクローンタブ、クーロンタブとも)コマンドはUnix系OSにおいて、
コマンドの定時実行のスケジュール管理を行うために用いられるコマンドである。
標準入力からコマンド列を読み取り、crontabと呼ばれるファイルにそれを記録する。
この記録を元に定時になると、その命令内容を読み取り、実行が行われる。

たとえば、 crontabに以下のジョブを登録すると...

0 12 * * * eject && eject -t

12時丁度にCDトレイが開いたり閉まったり! まるで鳩時計。大切なランチタイムを逃すことはありません。

アプリケーションなら、定時刻にバッチを実行したり、メールを送ったりといった処理をしたくなるのが世の常。実際の案件では、Cronにコマンドを登録して後はお任せ!とするわけですが、Herokuで定期的にコマンドを実行するにはどうしたらいいでしょうか。

Herokuで定期実行

PaaSであるHerokuでは、crontabを触ることができません...
どうやって定時刻にコマンドを実行するかというと、「Heroku Scheduler」というアドオンで対応できるんです。
Heroku Schedulerではダッシュボード上で、10分ごとから、1時間ごと、毎日の間隔で自由に実行するように設定でき、そしてCron アドオンとは違い(Cronの場合はアドオンを有効化した時間に依存していた)いつに実行されるかを10分単位で指定できます。
※ Cronアドオンは柔軟性が乏しく、Heroku Schedulerへの移行が推奨されています

それでは、実際にHeroku上でスケジュールをセットしてみましょう。

アドオンをインストールする

まず、Herokuのスケジューラを使用するには、アドオンをインストールします。heroku コマンドのインストールされた端末で、アプリケーションフォルダに移動し以下のコマンドを実行してください。

cd Herokuにデプロイされたアプリ
heroku addons:add scheduler:standard

タスクの定義

タスクは、アプリケーションで実行できる任意のコマンドです。

Railsの場合は、rakeタスクを用意しましょう。Railsでスケジュールされたタスクを作成するには、以下のコードをコピーして、カスタマイズします。 ファイルはlib/tasks/scheduler.rake に配置しましょう。

desc "This task is called by the Heroku scheduler add-on"
task :update_feed => :environment do
    puts "Updating feed..."
    NewsFeed.update
    puts "done."
end

task :send_reminders => :environment do
    User.send_reminders
end

他のフレームワークや他の言語で作られたアプリケーションは、binにスクリプトを追加することで、そのタスクを実行することが可能です。次の例はDBに格納されたSession情報を定期的に消すスクリプトです。 bin/clean_sessionってファイル名にしました。

#!/usr/bin/env ruby
require "sequel"
DB = Sequel.connect ENV["DATABASE_URL"]
puts "Cleaning old sessions..."
DB["DELETE FROM sessions WHERE last_seen_at < ?", Time.now - 24*60*60]
puts "done."

タスクの動作確認

次のステップは、アプリケーションをデプロイし、Herokuの上でタスクが動くかテストしてみましょう。テストを行うには、heroku runをHeroku上で実行します。

$ heroku run rake update_feed

スケジューラは、一回限りのプロセスで起動されます。heroku runコマンドを実行することで、手動でジョブの動作を確認できます。

ジョブのスケジューリング

ジョブの頻度と時間をスケジュールリングするには、スケジューラのダッシュボードを開いてAdd-onsのドロップダウンメニューから"スケジューラ"を選択しましょう。ダッシュボードは、コマンドラインから開くこともできますよ。

heroku addons:open scheduler

スケジューラのダッシュボードで、 "ジョブの追加..."をクリックし、タスクを入力し、頻度と次の実行時間を選択します。

毎日のジョブの実行時間がUTCであることに注意してください!!!
注意 : 日本時間に合わせるには、実施したい時間よりも9時間早い時間を設定して下さいね。

UTC時間を確認するためにはirbなどで、以下のコマンドを使うと簡単に確認できますよ!

# localtimeの24:00はUTCで15:00
1.9.3p194 :001 > Time.parse("24:00").utc
 => 2012-07-25 15:00:00 UTC

たとえば、rake update_feedを追加する場合 、 "Hourly"を選択し、"30"を選択して更新。30分ごとにフィードを更新します。
毎晩リマインダーを送信するような、デイリータスクを登録するには rake send_reminders を追加し、"Daily"と"15:00"(日本の場合)を選択します。

ログの確認

スケジュールされたジョブのログは、以下のように出力されます。

$ heroku logs --ps run
2011-02-04T14:10:16-08:00 heroku[run.7]: State changed from created to starting
2011-02-04T14:10:16-08:00 app[run.7]: Starting process with command `bin/clean_sessions`
2011-02-04T14:10:19-08:00 app[run.7]: Deleting stale sessions...
2011-02-04T14:10:27-08:00 app[run.7]: done.
2011-02-04T14:10:28-08:00 heroku[cron.1]: State changed from up to complete

また、スケジュールされたプロセスはheroku psコマンドを実行すると表示可能です。

$ heroku ps
=== run: one-off processes
run.5: complete for 2h: `bin/clean_sessions`
run.6: complete for 1h: `bin/clean_sessions`
run.7: complete for 5m: `bin/clean_sessions`

=== web: `bundle exec thin start -p $PORT -e production`
web.1: idle for 3h

長時間実行されるジョブについて

完了するまで数分よりも長い時間がかかるジョブを実行する際にはワーカープロセスを使用する必要があります。

また、すべてのスケジュールされたジョブは、以前にスケジュールされたジョブが終了するかどうかにかかわらず実行されることに注意してくださいね。

Cronのアドオンからの移行

既にCronのアドオンを使用している場合は、代わりにアドオンをスケジューラに切り替えたほうがいいらしいですよ!できるだけ今まで動いていたcronジョブと似た動作をさせるために、新しいジョブを設定するには、cronのダッシュボードに移動し、cronのスケジュール時刻を見てください。
その後、スケジューラのダッシュボードで、新しいタスクを作成。毎時または毎日のいずれかを選び、cronで設定していた時刻をUTC(9時間早め)に変換した時刻を設定します。コマンドには rake cron と記述し保存します。

移行元のcronの例

スケジューラのダッシュボード

最後に、Cronのアドオンを削除することを忘れないでください。以下のコマンドで削除可能です。

$ heroku addons:remove cron

以上でスケジューリングの設定は完了です。 指定した設定に基づいて、Herokuがせっせとタスクをこなしてくれることでしょう ;>

いかがでしたでしょうか?? 次回はSSLの設定について書きたいと思います。


About 増田 茂樹

Ruby, Scala, JavaScript, アジャイル開発, Herokuがお好きなプログラマー。
This entry was posted in PaaS, 技術 and tagged , , . Bookmark the permalink.