DockerでCeleryを稼働しているとき、設定によってはタスクを安全に完了(Graceful Shutdown)できていない場合があります。それぞれの登場人物の仕様を理解して間違いないようにしましょう。
コンテナランタイムの仕様
AWS Fargateではコンテナを停止する際、最初に SIGTERM
が送信され、それでも30秒以内にプロセスが停止しない場合は SIGKILL
が送信されます。この SIGTERM
が適切にCeleryのプロセスに届いていないと安全なシャットダウンができません。
CMDでシェル形式で書くとSIGTERMが届かない
Dockerでプロセスを起動する際、 CMD
コマンドを指定することがあります。
CMD celery -A myapp
CMD
をこのように指定する場合、Dockerは /bin/bash -c
を先頭につけてこのコマンドを実行します。この場合 SIGTERM
が CMD
に指定しているコマンドに届かず、 /bin/bash
で止まってしまいます。
その結果、Dockerコンテナにコンテナランタイムが停止するよう命令しても、Celeryのようなタスクが完了せずに中断されてしまいます。
修正方法1:exec形式で書く
シェル形式でなくexec形式で書くと /bin/bash
でラップされずに起動します。
CMD ["/usr/local/bin/celery", "-A", "myapp"]
Dockerfile リファレンス — Docker-docs-ja 24.0 ドキュメント
@naoina さんご指摘いただきありがとうございました!
修正方法2:ENTRYPOINTに書く
Celeryの起動コマンドをENTRYPOINT
に設定し、CMD
にはオプションパラメータを渡すように変更しても良いです。
ENTRYPOINT ["/usr/local/bin/celery"] CMD ["-A", "myapp", "worker", "-l", "ERROR"]
こちらでもDockerコンテナが直接Celeryを起動するので、コンテナランタイムからのSIGTERMを受け取れます。
ENTRYPOINTとCMDとは何か
ENTRYPOINTには実行されるべきコマンドを書き、CMDには docker run
で起動する際のデフォルトのオプションを書きます。デフォルトの実行コマンドを書き換えられたくない場合などにENTRYPOINTを使うことで、コンテナの用途を明確にできます。
CMDだけの場合は2種類の書き方があるので、気をつけましょう。
とはいえSIGTERMの話とENTRYPOINT or CMDの話は直接的に関係がないので、よくDocker、bash、コンテナランタイム(Fargate)の挙動を知っておく必要があります。
おわりに
今回はとくにタスクの扱いが繊細なCeleryで書きましたが、他のアプリケーションでも同じことです。たとえばGunicornやFastAPIのBackgroundTaskの挙動も関係します。
現在、Shodoではアドベントカレンダー応援クーポンを配布しております。80%オフでShodoを最長3ヶ月間使えるクーポンです。以下のクーポンコードをご購入時に入力して、このアドベントカレンダーの季節にShodoのAI校正をお役立てください。
XMAS2024