ECS Fargate Spotの中断時に、安全にALBから登録解除する方法

こんにちは。 エキサイト株式会社の三浦です。

Amazon ECS でFargateを使用する際、通常のFargateとは別にFargate Spotを選択することが出来ます。

Fargate Spotは通常のFargateに比べて非常に安く、ぜひとも使っていきたい一方で、AWS側の都合でコンテナが中断されてしまう恐れがあります。

そのため、何も考慮せずにALBに接続してWebアプリケーションとして使っていると、コンテナ中断のタイミングでリクエスト元に対してエラーが返されてしまう原因になります。

今回は、Fargate SpotをALBと接続して使用するときに、Fargate Spotの中断に際して安全にALBから解除する方法を説明します。

Fargate Spotと中断

Fargate Spotは、格安でFargateを使用できる代わりに、AWSの都合のタイミングでコンテナの中断が起こりうるサービスです。

docs.aws.amazon.com

Fargate Spotを使用すると、割り込み許容のあるAmazon ECSタスクを、Fargate料金と比較して割引料金で実行できます。Fargate Spot は、予備のコンピュートキャパシティーでタスクを実行します。AWSキャパシティーを戻す必要がある場合、タスクは中断され、2 分間の警告が表示されます。

aws.amazon.com

Fargate Spot では、割り込み耐性のある Amazon ECS タスク* を予備の容量で実行することができ、通常の Fargate 料金の最大 70% 割引で購入することができます。

コストの観点から言えば可能な限りSpotを使いたいところですが、FargateにALBを接続してWebアプリケーションとして使っているサービスの場合、コンテナが中断されたタイミングでリクエスト元に対してエラーを返してしまう恐れがあるという問題があります。

コンテナ中断対策

この問題はAWSも認識しており、以下のブログでその対処方法を説明しています。

aws.amazon.com

FARGATE_SPOT として実行されるタスクでは、ロードバランサーのターゲットグループから登録解除されてからタスクが STOPPED 状態に移行するという保証はありません。望ましくないエラーを回避するために、必要な API を呼び出して、ロードバランサーのターゲットグループからタスクを登録解除することをお勧めします。


FARGATE_SPOT タスクが終了としてマークされると、タスク内のすべてのコンテナがすぐに SIGTERM シグナルを受信します。SIGTERM を受信したタイミングで、登録解除の遅延期間が満了するまでシグナルハンドラをスリープさせておくのも一つのアイデアです。

端的に書くと、

  • Fargate Spotの中断イベントが発生したタイミングで、ALBから対象コンテナの登録を解除する
  • 中断イベントが発生するとコンテナにSIGTERMが送られるが、それを受け取ったらシグナルハンドラをスリープさせる

という対応が必要、ということになります。

こちら特に後半がミソで、これを「SIGTERMを受け取ったらちゃんとGraceful Shutdownが起きるようにする」という形で対応すると問題が起こりえます

(以前以下の記事で「Graceful Shutdownにすれば問題ない」と書きましたが、ALBと接続している場合ではエラーが起きてしまうケースがありました。)

tech.excite.co.jp

というのも、上記の対応をしても、順番的に

  1. ALBからコンテナが登録解除される
  2. 解除後、SIGTERMがコンテナに送信される

という順番が保証されていないようで、

  1. SIGTERMがコンテナに送信される
  2. ALBからコンテナが登録解除される

という順番になる可能性もあるようなのです。

そうなった場合、SIGTERMの送信によってコンテナのアプリケーションがGraceful Shutdownに入る、すなわち新規のアクセスを受け取らない状態になっているにも関わらず、ALBからはまだ新規のアクセスが送られてくるために、その間の新規アクセスにはエラーが返されてしまいます。

ここで必要なのは、「SIGTERMが送られてきたらGraceful Shutdownにする」ではなく、「SIGTERMが送られてきても無視する(もしくはそれによる処理を遅延させる)」ということになります。

こうすることで、アクセス制御がALBのみに委ねられることになり、ALBが適切に新規アクセスを中断が起きていないコンテナに割り振ってくれるため、リクエスト元にエラーが発生しません。

SIGTERMの無視には、例えば以下のような方法があります。

tech.excite.co.jp

注意点として、SIGTERMでコンテナが終了しないため、コンテナが終了するタイミングはSIGTERM送信の指定時間後に発生するSIGKILLの送信による強制終了になります。

そのため、SIGKILLによる強制終了でも問題なくアプリケーションが終了するように調整する必要があります。

また、ALBからの登録解除が終了する前にコンテナが強制終了してしまわないよう、stopTimeout(SIGTERMが送られた何秒後にSIGKILLが送られるかの設定値)を適切に設定しておく必要があります。

docs.aws.amazon.com

最後に

そのWebアプリケーションに対してアクセスが多ければ多いほど、Fargate Spotの中断による影響範囲は大きくなります。

Spotの中断自体がそこまで頻繁に起きるわけではないので軽視する場合もあるかもしれませんが、上記のように適切に対処すればエラーも起きなくなるので、ぜひ設定してみてください。