Skip to content
iwhurtafly edited this page Dec 10, 2012 · 9 revisions

Herokuは、dyno manifoldを経由して配信されるdynoの範囲内で、アプリケーションのweb、worker、その他プロセスを実行し、管理しています。

dynoを起動・停止する、スケールアップ・ダウンに関する情報は、プロセスの編成をスケーリングするを参照下さい。

プロセスのリスト化

アプリケーションのプロセスをリスト化することが可能です。:

:::term
$ heroku ps
=== web: `bundle exec thin start -p $PORT -e production`
web.1: up for 8h
web.2: up for 3m

=== worker: `bundle exec stalk worker.rb`
worker.1: up for 1m

Unixのwatch utilityが、psコマンドとの組み合わせで、とても使い勝手が良いでしょう。dynoを追加したり、リムーブしたり、アプリケーションをデプロイしたり、再起動させる場合と同じターミナル上で、watch heroku psコマンドを実行してみて下さい。

プロセスの再起動

通常のオペレーションでは、dynoがdyno自身を終了させるようなことは起こり得ません。新たなコードをデプロイしたり、変数設定を変更したり、heroku restartを実行した時に、dynoは再起動されることとなります。dyno内で実行されているプロセスが、dynoを終了させるケースとして、以下のものがあります。:

  • 起動時のコードにおける欠陥 - もしアプリケーションが、クリティカルな依存性を欠いていた場合、あるいは、起動時にその他の問題を抱えていた場合、スタックトレースを伴い、すぐに終了が実行されます。

  • 起動時に使われるリソースの一時的エラー - 起動時にアプリケーションがあるリソースへアクセスし、そのリソースがオフラインであった場合、終了が実行されます。例えば、データベースにAmazonのRDSを使用していて、Heroku側のアプリケーションにRDSのセキュリティグループに対するイングレスを作成していなかった場合、アプリケーションを起動しようとする際に、エラー、またはタイムアウトを生成することとなります。

  • バイナリのライブラリにおけるセグメンテーション違反 - アプリケーションがバイナリのライブラリを使用していて(例えば、XMLのパーサーのような)、そのライブラリがクラッシュした場合、それに伴ってアプリケーション全体が影響します。例外ハンドリングは、この事象をトラップすることが出来ません。そのため、プロセスが終了することとなります。

  • インタープリター、またはコンパイラーのバグ - レアケースではありますが、インタープリター(Ruby、 Python)のバグ、または、コンパイル(Java、 Scala)のバグは、プロセスを壊すこととなります。

アプリケーションの開発者として、次の2つのエラーを見ることが度々あります。1つ目は、"boot crashes"で、2つ目は、"runtime crashes"です。しかしながら、Herokuでは、これらを区別する方法がありません。プラットフォームの観点からは、プロセスがクラッシュすることは、全て同じようなもので区別がつかないのです。

Herokuにおけるプロセスの再起動ポリシーは、10分に1回新しいdynoを産み出すことによって、クラッシュしたプロセスを再起動しようと試みることです。このことは、もし起動を妨げるような悪いコードをプッシュした場合、アプリケーションのdynoは一度開始された後、再起動され、それから10分間の停止期間を取ることを意味します。時々クラッシュが発生する長期間実行のwebやworkerプロセスの一般的なケースでは、開発者側へのいかなる介入無しに、即、再起動が実施されることとなります。もし、プロセスが2回連続でクラッシュした場合、Herokuがリトライする前に、10分間の停止期間を取ることになります。

SIGTERMでのきれいなシャットダウン(Graceful shutdown)

dyno manifoldは、しばしばプロセスを停止したり、再起動したりする必要があります。- 新たなリリースを作成した場合や、dyno manifoldがdynoを再配置する必要が発生した場合、または、heroku restartコマンドで手動での再起動を要求した場合等です。全てのケースにおいて、dyno manifoldは、プロセスへSIGTERMを送信することで、プロセスをきれいにシャットダウンすることを要求します。

アプリケーションのプロセスは、シャットダウンしきるのに、10秒を要します。(実際には、もう少し早くシャットダウンします。)この間、新たなリクエストやジョブを受け付けることはせず、現在のリクエストを全て終わらせようと試みたり、または他のworkerプロセスのキューへジョブを追加しようとしたりします。もし、10秒後に、何かしらのプロセスが残っていた場合、dyno manifoldは、SIGKILLを使い、これらのプロセスを強制終了します。

これらが実際にどのように動作するかを、サンプルのworkerプロセスで確認することが可能です。ここでは、説明としてRubyを使いますが、メカニズムはどの言語でも一緒です。ループし、メッセージを継続的にプリントアウトすること以外は何もしないプロセスをイメージして下さい。:

:::ruby
STDOUT.sync = true
puts "Starting up"

trap('TERM') do
  puts "Graceful shutdown"
  exit
end

loop do
  puts "Pretending to do work"
  sleep 3
end

このコードをデプロイし(適切なGemfileProcfileで)、heroku ps:scale worker=1コマンドを実行すると、ループ内にプロセスを見ることが出来ます。:

:::term
$ heroku logs
2011-05-31T23:31:16+00:00 heroku[worker.1]: Starting process with command: `bundle exec ruby worker.rb`
2011-05-31T23:31:17+00:00 heroku[worker.1]: State changed from starting to up
2011-05-31T23:31:17+00:00 app[worker.1]: Starting up
2011-05-31T23:31:17+00:00 app[worker.1]: Pretending to do work
2011-05-31T23:31:20+00:00 app[worker.1]: Pretending to do work
2011-05-31T23:31:23+00:00 app[worker.1]: Pretending to do work

dynoを再起動して下さい。dynoがSIGTERMを受信します。:

:::term
$ heroku restart worker.1
Restarting worker.1 process... done

$ heroku logs
2011-05-31T23:31:26+00:00 app[worker.1]: Pretending to do work
2011-05-31T23:31:28+00:00 heroku[worker.1]: State changed from up to starting
2011-05-31T23:31:29+00:00 heroku[worker.1]: Stopping all processes with SIGTERM
2011-05-31T23:31:29+00:00 app[worker.1]: Graceful shutdown
2011-05-31T23:31:29+00:00 heroku[worker.1]: Process exited

app[worker.1]が"きれいなシャットダウン(Graceful shutdown)"とログを出力していることに注目して下さい。(記述したコードから期待される通りです)dyno manifoldの全メッセージは、heroku[worker.1]としてログ出力されます。

TERMシグナルを無視するためにworker.rbを修正するのであれば、以下のようになります。:

:::ruby
STDOUT.sync = true
puts "Starting up"

trap('TERM') do
  puts "Ignoring TERM signal - not a good idea"
end

loop do
  puts "Pretending to do work"
  sleep 3
end

挙動の変化を確認出来ます。:

:::term
$ heroku restart worker.1
Restarting worker.1 process... done

$ heroku logs
2011-05-31T23:40:57+00:00 heroku[worker.1]: Stopping all processes with SIGTERM
2011-05-31T23:40:57+00:00 app[worker.1]: Ignoring TERM signal - not a good idea
2011-05-31T23:40:58+00:00 app[worker.1]: Pretending to do work
2011-05-31T23:41:01+00:00 app[worker.1]: Pretending to do work
2011-05-31T23:41:04+00:00 app[worker.1]: Pretending to do work
2011-05-31T23:41:07+00:00 heroku[worker.1]: Error R12 (Exit timeout) -> Process failed to exit within 10 seconds of SIGTERM
2011-05-31T23:41:07+00:00 heroku[worker.1]: Stopping all processes with SIGKILL
2011-05-31T23:41:08+00:00 heroku[worker.1]: Process exited

プロセスは、SIGTERMを無視します。そして盲目的に処理を続けます。10秒後、dyno manifoldは、プロセスがきれいにシャットダウンされるのを諦めます。そして、SIGKILLでプロセスを強制終了します。プロセスが正常に動作していないことを伝えるError R12のログが出力されます。

Clone this wiki locally