dyno のシャットダウン動作
最終更新日 2024年12月03日(火)
Table of Contents
この記事では、Heroku で dyno がシャットダウンするときの動作について説明します。
SIGTERM シグナル
Dyno Manager によって dyno が再起動されると、Dyno Manager は SIGTERM
シグナルを送信してプロセスが正常にシャットダウンするようにリクエストします。このシグナルは dyno 内のすべてのプロセスに送信されます。
シャットダウン中のプロセスが複数の SIGTERM
を受信する場合があります。
アプリケーションプロセスがクリーンにシャットダウンするには 30 秒かかり、速く行われるほど良いとされています。この時間が経過した後もプロセスが残っている場合、Dyno Manager は SIGKILL
で強制的にそれらのプロセスを終了します。制御された再起動または定期的な再起動の実行中にシャットダウンシグナルが古い dyno に送信されるとすぐに、Dyno Manager が新しい dyno を起動します。
30 秒の間にシグナルを使用して正常にシャットダウンします。プロセスが新しいリクエストやジョブを受け入れるのを回避してください。現在のリクエストを完了させるか、ジョブをキューに戻して他のワーカープロセスが処理できるようにします。
グレースフルシャットダウンの例
サンプル worker
プロセスで正常にシャットダウンする手順を確認できます。この例では 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
このコードを適切な Gemfile および Procfile と共にデプロイし、heroku ps:scale worker=1
を実行すると、dyno worker.1
で実行中のループでプロセスを確認できます。
heroku logs
2024-10-31T23:31:16+00:00 heroku[worker.1]: Starting process with command: `bundle exec ruby worker.rb`
2024-10-31T23:31:17+00:00 heroku[worker.1]: State changed from starting to up
2024-10-31T23:31:17+00:00 app[worker.1]: Starting up
2024-10-31T23:31:17+00:00 app[worker.1]: Pretending to do work
2024-10-31T23:31:20+00:00 app[worker.1]: Pretending to do work
2024-10-31T23:31:23+00:00 app[worker.1]: Pretending to do work
dyno で heroku restart
を実行すると、dyno が SIGTERM
を受け取ります。
heroku restart --dyno-name worker.1
Restarting worker.1 process... done
heroku logs
2024-10-31T23:31:26+00:00 app[worker.1]: Pretending to do work
2024-10-31T23:31:28+00:00 heroku[worker.1]: State changed from up to starting
2024-10-31T23:31:29+00:00 heroku[worker.1]: Stopping all processes with SIGTERM
2024-10-31T23:31:29+00:00 app[worker.1]: Graceful shutdown
2024-10-31T23:31:29+00:00 heroku[worker.1]: Process exited
コードの想定どおり、app[worker.1]
に「Graceful shutdown」と記録されます。
SIGKILL シャットダウンの例
worker.rb
を修正して TERM
シグナルを無視すると、プロセスが 30 秒以内に正常にシャットダウンしない場合に何が起こるかをシミュレートできます。
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
動作が変更されているのがわかります。
heroku restart --dyno-name worker.1
Restarting worker.1 process... done
heroku logs
2024-10-31T23:40:57+00:00 heroku[worker.1]: Stopping all processes with SIGTERM
2024-10-31T23:40:57+00:00 app[worker.1]: Ignoring TERM signal - not a good idea
2024-10-31T23:40:58+00:00 app[worker.1]: Pretending to do work
2024-10-31T23:41:01+00:00 app[worker.1]: Pretending to do work
...
2024-10-31T23:41:25+00:00 app[worker.1]: Pretending to do work
2024-10-31T23:41:27+00:00 heroku[worker.1]: Error R12 (Exit timeout) -> Process failed to exit within 30 seconds of SIGTERM
2024-10-31T23:41:27+00:00 heroku[worker.1]: Stopping all processes with SIGKILL
2024-10-31T23:41:28+00:00 heroku[worker.1]: Process exited
プロセスによって SIGTERM
が無視され、処理が続行されます。30 秒後、Dyno Manager はプロセスが緩やかにシャットダウンするまで待機するのをあきらめ、SIGKILL
で強制終了します。エラー R12 - Exit Timeout が記録され、プロセスが適切に動作していないことを示します。
heroku restart --dyno-name web-abc123cde1
CLI と API の動作
Common Runtime では、ps:stop
を実行するか、拡張されたプロセスの一部である dyno で dyno Stop API 呼び出しを実行すると、Dyno Manager はそれらを自動的に停止して再起動します。
Cedar 世代の Private Space では CLI コマンドと API 呼び出しが終了し、代わりにdyno を実行している専用インスタンスに置き換えられます。
Fir スペースでは、dyno は Kubernetes ポッドによって実行されます。CLI コマンドと API 呼び出しが開始されると、対応する dyno が終了して置き換えられます。
dyno を永続的に停止するには、プロセスを heroku ps:scale
または Formation Batch Update API 呼び出しを使用して縮小します。
その他の資料
- dyno の起動動作
- dyno の再起動
- dyno の動作カテゴリ