SidekiqのGraceful Shutdownを理解する
みなさんこんにちは!
ワンキャリアのONE CAREER CLOUD採用管理(ATS)チームにて、ソフトウェアエンジニアをしています杉水(X:ltoppyl)です。
今回は、ECS タスクの切り替え時における Sidekiq ジョブの処理について、業務を通して学んだことを元に、詳しく解説します。
ECS で Sidekiq を運用する際、タスク切り替えによって実行中のジョブが中断されるリスクがあります。そのため、ジョブの正常な完了を確保するためには、適切な設定が欠かせません。
本記事では、特に Sidekiq のシャットダウンプロセスとジョブの強制終了に関わるタイムアウト設定の重要性、そして ENTRYPOINT 設定の落とし穴に焦点を当て、ジョブ消失を防ぐための運用方法を探ります。
ECS タスク切り替えと Sidekiq のシャットダウンプロセス
ECS 上でタスクが切り替えられる際、実行中のジョブが正常に処理されるかは、Sidekiq と ECS の設定に依存します。以下が基本的なジョブの処理フローです。
シャットダウンプロセスの開始 (「Sidekiq 停止処理開始」~「ジョブ停止処理開始」 に相当)
タスクの停止が開始されると、まず Sidekiq は新しいジョブの取得を停止します。
既存ジョブの実行続行 (「ジョブ停止処理」~「ジョブ強制終了」に相当)
実行中のジョブがあれば、それらを処理するのみになります。この間、ジョブが完了するまでの時間がタイムアウト時間以内であれば、通常通り完了します。
タイムアウトと強制終了 (「ジョブ強制終了」に相当)
タイムアウト時間までに完了しないジョブは、Sidekiq から強制終了され、ジョブはジョブキューに戻されます。この時、「Sidekiq::shutdown」のログが出力されるため、ログを確認することでシャットダウンの状態を把握できます。
タイムアウト設定の重要性
ECS 上で Sidekiq ジョブを確実に処理するためには、Sidekiq と ECS のタイムアウト設定を慎重に調整する必要があります。適切な設定により、ジョブが消失するリスクを最小限に抑えられます。
デフォルトのタイムアウト時間
ECS では stopTimeout や環境変数 ECS_CONTAINER_STOP_TIMEOUT のデフォルトが 30 秒です。コンテナがこのタイムアウト内に終了しない場合、SIGKILL が発行され、強制終了されます。これにより Sidekiq が強制終了され、ジョブ消失のリスクが生じることになります。
Sidekiq のシャットダウンタイムアウトは 25 秒で、これを超えるとジョブは強制終了されジョブキューに戻されます。Sidekiq の Issue に記載があるように、デフォルトだとジョブの消失を避けるため、ECS のデフォルトタイムアウト時間より Sidekiq のタイムアウトが ECS より短く設定されています。
タイムアウト設定の方法
1.Sidekiq のタイムアウト設定
実行時オプション -t を使用することで、Sidekiq のシャットダウン時にジョブの終了を待つ タイムアウト時間 を調整できます。
以下の例では、タイムアウトを30秒に設定しています。
参考:
ECS では以下の方法でタイムアウトを設定できます
タスク定義での stopTimeout 設定
タスク定義ファイルで、stopTimeout パラメータを追加することで、Sidekiq の設定より長く設定します。
環境変数 ECS_CONTAINER_STOP_TIMEOUT での設定
設定時のポイント
タイムアウト順序の確認
「Sidekiq のタイムアウト < ECS のタイムアウト」となるように設定します。順序が逆になると、ECS が Sidekiq を SIGKILL で強制終了し、ジョブ消失の恐れがあります。
ENTRYPOINT 設定の落とし穴
ECS で Sidekiq を運用する際に見落としがちなポイントの1つが、Docker の ENTRYPOINT の設定です。Docker の ENTRYPOINT はコンテナが起動したときに実行されるコマンドを指定するもので、適切に設定しないとアプリケーションの終了処理に影響を及ぼす可能性があります。 AWS のドキュメント でも推奨されているように、ENTRYPOINT を /bin/sh -c "my-app" のように設定することは避けるべきです。理由は、この設定が Sidekiq のシャットダウン処理において問題を引き起こし、ジョブが消失するリスクを増大させるためです。
/bin/sh -c "my-app" の設定が問題となる理由
通常、ECS ではコンテナを停止する際に、SIGTERM という終了信号をコンテナに送ります。この信号は、コンテナ内で最初に実行されるプロセスに届きます。このプロセスが終了することで、コンテナ全体が停止します。
ここで問題となるのは、ENTRYPOINT に /bin/sh -c "my-app" のように設定した場合です。この設定だと、コンテナ内で最初に動くプロセスは sh(シェル)になり、実際のアプリケーションである Sidekiq は sh の中で動く子プロセス という形になります。この構造が以下のようなシグナル伝播の問題を生じさせます。
1.シグナルが Sidekiq に届かない
sh は、受け取った SIGTERM をそのまま Sidekiq には伝えず、シグナルが Sidekiq に届きません。このため、Sidekiq はシャットダウン命令を受け取らないままとなり、停止シーケンスが実行されません。
2.強制終了(SIGKILL)のリスク
ECS 上で Sidekiq がシャットダウンせず動作し続けると、ECS のタイムアウトが過ぎたタイミングで強制的に SIGKILL がコンテナ全体に送られます。SIGKILL は Sidekiq も含めて全プロセスを強制終了するため、ジョブがジョブキューに戻る前に停止され、ジョブ消失のリスクが発生します。
ENTRYPOINT の適切な設定方法
この問題を回避するには、以下の方法を検討しましょう。
exec 形式で Sidekiq を実行
Sidekiq を正常にシャットダウンするためには、exec 形式で起動することが重要です。exec 形式を用いることで、シグナルが Sidekiq に直接伝わり、適切なシャットダウン処理が可能になります。
まず、シェル形式で Sidekiq を実行する例を見てみましょう。
この方法では、シェルがシグナルを受け取ってしまうため、Sidekiq に直接シグナルが届かず、正常なシャットダウンが妨げられる可能性があります。
次に、これを exec 形式 に書き換えた例を見てみます。
exec 形式を使用すると、シグナルが直接 Sidekiq に届くようになり、シャットダウン処理がスムーズに行われます。この変更は、シンプルながらも運用上非常に重要なポイントです。
プロセスマネージャーの利用
tini や dumb-init などのプロセスマネージャーを活用することで、シャットダウンシグナルを適切にアプリケーションに伝えることができます。
まとめ
ECS 上での Sidekiq ジョブの運用方法について、重要なポイントについて解説しました。
コンテナのシャットダウン時にジョブが失われないようにするためには、タイムアウト設定の調整 と ENTRYPOINT の設定 が肝心です
これらの設定を見直すことで、ジョブの消失リスクを防ぎ、安定した運用を実現できます。ECS 上での Sidekiq 運用をより安全でスムーズにしていきましょう。
最後までお読みいただき、ありがとうございました!
「人の数だけ、キャリアをつくる。」
ワンキャリアではミッション実現のために、事業・プロダクト開発を推進させる仲間を募集しています。弊社のエンジニア組織にご興味を持っていただけた方は、採用情報もチェックいただけると嬉しいです!
▼ワンキャリアのエンジニア組織のことを知りたい方はまずこちら
▼カジュアル面談を希望の方はこちら
▼エンジニア求人票