Make組ブログ

Python、Webアプリや製品・サービス開発についてhirokikyが書きます。

Dockerで稼働しているCeleryが停止時にタスクを完了しない問題と修正方法

DockerでCeleryを稼働しているとき、設定によってはタスクを安全に完了(Graceful Shutdown)できていない場合があります。それぞれの登場人物の仕様を理解して間違いないようにしましょう。

コンテナランタイムの仕様

AWS Fargateではコンテナを停止する際、最初に SIGTERM が送信され、それでも30秒以内にプロセスが停止しない場合は SIGKILL が送信されます。この SIGTERM が適切にCeleryのプロセスに届いていないと安全なシャットダウンができません。

aws.amazon.com

CMDでシェル形式で書くとSIGTERMが届かない

Dockerでプロセスを起動する際、 CMD コマンドを指定することがあります。

CMD celery -A myapp

CMD をこのように指定する場合、Dockerは /bin/bash -c を先頭につけてこのコマンドを実行します。この場合 SIGTERMCMD に指定しているコマンドに届かず、 /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を使うことで、コンテナの用途を明確にできます。

www.docker.com

CMDだけの場合は2種類の書き方があるので、気をつけましょう。

とはいえSIGTERMの話とENTRYPOINT or CMDの話は直接的に関係がないので、よくDocker、bash、コンテナランタイム(Fargate)の挙動を知っておく必要があります。

おわりに

今回はとくにタスクの扱いが繊細なCeleryで書きましたが、他のアプリケーションでも同じことです。たとえばGunicornやFastAPIのBackgroundTaskの挙動も関係します。

現在、Shodoではアドベントカレンダー応援クーポンを配布しております。80%オフでShodoを最長3ヶ月間使えるクーポンです。以下のクーポンコードをご購入時に入力して、このアドベントカレンダーの季節にShodoのAI校正をお役立てください。

XMAS2024

shodo.ink

執筆:@hirokiky
Shodoで執筆されました