Amazon Web Services ブログ

Amazon ECS タスクのスケールイン保護の発表

この記事は Announcing Amazon ECS Task Scale-in protection (記事公開日: 2022 年 11 月 10 日) の翻訳です。

導入

本日、Amazon Elastic Container Service (Amazon ECS) タスクのスケールイン保護を発表します。この新しい機能を使用することで、Amazon ECS サービスのオートスケーリングやデプロイにおけるスケールインイベントによって Amazon ECS サービスのタスクが終了しないように保護することができます。この機能は、新しい Amazon ECS エージェントエンドポイントまたは新しい Amazon ECS API を使用して、ミッションクリティカルなタスクに属性を設定するだけで有効化できます。

背景

Amazon ECS の機能であるサービスオートスケーリングでは、トラフィックパターンの変化に応じて Amazon ECS サービスの希望タスク数を自動調整するポリシーを設定できます。これにより、ピーク時のトラフィックにも対応できるスケーラブルなアプリケーションを構築しつつ、利用率が低い時間帯のコンピュートコストを削減できます。Amazon ECS のサービスオートスケーリングでは、サービスの平均的な CPU 使用率およびメモリ使用量などの Amazon ECS が提供する Amazon CloudWatch メトリクスを利用できるほか、受け取った HTTP リクエスト数、キューやトピックから取得したメッセージ数などのアプリケーションの側面を表すカスタムメトリクスを独自に設定して利用することも可能です。

一部のアプリケーションでは利用率が低い時間帯にもタスクを保護する仕組みが必要、という声をお客様から頂いていました。例えば、動画のトランスコードジョブのようなキューを利用した非同期アプリケーションでは、サービスの累積利用率が低くても、一部のタスクが何時間も実行されることがあります。ゲームサーバーを Amazon ECS タスクとして実行するゲームアプリケーションの場合には、サーバー再起動のレイテンシーを減らすために、すべてのユーザーがログアウトした後も実行し続けたい場合があります。また、新しいコードバージョンをデプロイする際、まだ処理が完了していないタスクを終了して再実行するコストは高くつく可能性があります。これまで、サービスのオートスケーリングや新しいバージョンのデプロイによってタスクが終了しないように保護するための簡単な方法はありませんでした。そのため、ジョブを再起動した後に再開するための複雑なワークアラウンドであったり、利用率が低くなることを許容したカスタムスケーリングポリシーを使う必要がありました。

ソリューション概要

タスクのスケールイン保護の概要

Amazon ECS タスクのスケールイン保護では、新しい属性 protectionEnabled を使用して、サービスのデプロイまたはオートスケーリングのスケールインイベントによる終了から、ECS サービスのタスクを保護できます。お客様は、新しい Amazon ECS タスクのスケールイン保護エンドポイントを使用してコンテナ内から protectionEnabled 属性を設定するか、新しい Amazon ECS API である UpdateTaskProtection を使用できます。Amazon ECS は、オートスケーリングまたはデプロイイベントにおいて、スケールイン保護が設定されたタスクを終了させないようにします。タスクが必要な作業を終えると、お客様はその属性を解除することで、その後のスケールインイベントにおいてタスクを終了できるようになります。お客様は、独自のツールに投資することなく、Amazon ECS で長時間稼働するアプリケーションを運用、管理するためのシンプルな仕組みを実現し、サービスのオートスケーリングにおけるパフォーマンスとコスト削減のメリットも享受できます。お客様は、以下の 2 つの仕組みを使用して、タスクのスケールイン保護を設定できます。

  • Amazon ECS エージェントエンドポイントの使用 : この方法は、タスクが自分自身を保護する必要性を自己判断できる場合に推奨されるアプローチであり、キューベースまたはジョブ処理のワークロードに最適です。コンテナが SQS メッセージを消費するなどして処理を開始した後、コンテナ内からタスクのスケールイン保護エンドポイントのパス $ECS_AGENT_URI/task-protection/v1/state を介して protectionEnabled 属性を設定できます。Amazon ECS は、スケールインイベント中にこのタスクを終了させることはありません。タスクが処理を終了した後、同じエンドポイントを使用して protectionEnabled 属性を解除して、その後のスケールインイベント中にタスクを終了できるようになります。
  • Amazon ECS API の使用 : この方法は、アクティブなタスクの状態を追跡するコンポーネントがアプリケーションに存在する場合に使用できます。UpdateTaskProtection API を使用すると、1 つまたは複数のタスクを保護するように設定できます。このアプローチの例としては、アプリケーションが Amazon ECS タスクとしてゲームサーバーのセッションをホストしている場合があります。ユーザーがサーバー上のセッション (=タスク) にログインすると、タスクを保護するように設定します。ユーザーがログアウトした後は、アイドル状態のサーバーの維持に関する要件に応じて、そのタスクの保護を解除する、あるいはこのようなアクティブなセッションを持たなくなったタスクを定期的に確認し、その保護を解除することができます。この方法を Amazon ECS エージェントエンドポイントと組み合わせて、コンテナ内からタスクの保護を設定し、外部のコントローラーサービスから設定を解除することもできます。

タスクのスケールイン保護を利用したサンプルアプリケーション

Amazon ECS タスクのスケールイン保護の利用方法を紹介するために、2 つのオープンソースのデモアプリケーションを紹介します。これらのアプリケーションは Github で参照できます。Github のリポジトリには、サンプルアプリケーションの前提条件と後片付けの手順が含まれています。

SQS キューのコンシューマー

1 つ目のアプリケーションは、SQS キューのコンシューマーです。Amazon ECS タスクのスケールイン保護を使用しない場合、アプリケーションが SQS キューから取得したジョブを実行中であるかどうかを、Amazon ECS が知る術はないことになります。もし Amazon ECS がジョブ実行中のタスクをスケールインした場合、処理中の SQS メッセージはドロップされます。SQS は、可視性タイムアウトの後にメッセージを別のキューコンシューマーに送信するため、これを優雅に処理します。しかし、実行中のジョブが長時間要するものだった場合、作業の進捗が失われ、ジョブを再実行しなければならないのは望ましくないでしょう。

代わりに、キューコンシューマーが SQS メッセージを処理している間、Amazon ECS タスクのスケールイン保護を使用して常にタスクを保護します。この動作の流れをサンプルコードで確認できます。

async function pollForWork() {
  console.log('Acquiring task protection');
  await TaskProtection.acquire();

  var message = await receiveMessage();

  if (message) {
    await processMessage(message);
  }

  console.log('Releasing task protection');

  await TaskProtection.release();
  return maybeContinuePolling();
}

SQS からメッセージを取得する前に、ワーカーは API を呼び出し、自分自身にタスク保護を設定します。そして、SQS からメッセージを取得し、処理します。メッセージの処理を完了すると、ワーカーはタスク保護を解除します。各ジョブにタスク保護を設定、解除することによって、Amazon ECS はメッセージ処理の合間の古いタスクを停止する機会を得ますが、Amazon ECS がメッセージ処理中のタスクを早期に終了しないことを保証します。

サンプルアプリケーションでは、この処理フローの動作をアプリケーションログに表示します。

2022-11-07T17:11:18.593-05:00    Acquiring task protection
2022-11-07T17:11:18.617-05:00    Long polling for messages
2022-11-07T17:11:37.641-05:00    0744dc66-c746-4561-ad38-7c2d90717d4a - Received
2022-11-07T17:11:37.641-05:00    0744dc66-c746-4561-ad38-7c2d90717d4a - Working for 10000 milliseconds
2022-11-07T17:11:47.655-05:00    0744dc66-c746-4561-ad38-7c2d90717d4a - Done
2022-11-07T17:11:47.655-05:00    Releasing task protection
2022-11-07T17:11:47.698-05:00    Task protection released
2022-11-07T17:11:47.698-05:00    Acquiring task protection
2022-11-07T17:11:47.725-05:00    Long polling for messages
2022-11-07T17:11:47.731-05:00    c701269a-07cd-4a83-bb71-3734c5306d88 - Received
2022-11-07T17:11:47.731-05:00    c701269a-07cd-4a83-bb71-3734c5306d88 - Working for 10000 milliseconds
2022-11-07T17:11:57.739-05:00    c701269a-07cd-4a83-bb71-3734c5306d88 - Done
2022-11-07T17:11:57.739-05:00    Releasing task protection

検証のために、ボディが 360000 の SQS メッセージを送信することで、完了に 5 分要する擬似的なジョブを作成できます。キューコンシューマーは自身を保護した後、ジョブを取得し、このジョブを完了するまで 5 分間待機します。この間、タスクは保護されたままです。タスクが保護されていることを検証するために、Amazon ECS コンソールを使用して、サービスの希望タスク数を 0 にスケールダウンします。タスク保護がない場合、Amazon ECS はタスクが処理中であろうとなかろうと、すぐに停止処理を開始します。しかし、タスク保護が設定されている場合、タスクは実行されたままであることを確認できます。このとき、以下のようなサービスイベントメッセージが表示されます。

(service task-protection-test-queue-consumer-Service-tjlfn1NmI0yU, taskSet ecs-svc/6163164058718610164) was unable to scale in due to (reason 1 tasks under protection)

5 分間の擬似的なジョブが終了すると、そのタスクは自身のタスク保護を解除し、Amazon ECS はその時点でタスクを停止し、サービスのスケーリングを完了します。

WebSocket サービス

タスク保護のもう 1 つのユースケースは、長時間実行される WebSocket サービスです。この種の接続は、一般にチャットサーバーやゲームサーバーのような低遅延かつ双方向の通信に使用されます。これらの接続が使用中である場合、接続を妨げることは、プレイヤーのオンラインゲームへの接続が中断されたり、チャットクライアントが切断され、再接続する必要が生じるため望ましくありません。

サンプルアプリケーションのコードでは、同時接続中のクライアント数をカウントし、接続中のクライアントが存在する間は常にタスク保護を設定することでこの問題を解決しています。この動作は、アプリケーションログで確認することができます。

2022-11-07T16:53:26.124-05:00    New client connection opened. There are 1 connections
2022-11-07T16:53:26.170-05:00    Task protection acquired
2022-11-07T16:53:28.153-05:00    received: ping
2022-11-07T16:53:30.153-05:00    received: ping
2022-11-07T16:53:32.156-05:00    received: ping
2022-11-07T16:53:32.451-05:00    Task protection acquired
2022-11-07T16:53:34.156-05:00    received: ping
2022-11-07T16:57:04.160-05:00    received: ping
2022-11-07T16:57:05.557-05:00    Client connection closed. There are 0 connections
2022-11-07T16:57:05.605-05:00    Task protection released

タスク保護を検証するには、ブラウザでサービスにアクセスし、ブラウザとサーバーの間で WebSocket 接続を確立します。次に、Amazon ECS コンソールで、サービスの希望タスク数を 0 に変更します。キューコンシューマーサービスと同様に、Amazon ECS はタスクをすぐに停止せず、待機していることが分かります。また、サービスのイベントタブに、以下のようなメッセージが表示されます。

(service task-protection-test-websocket-Service-M9Q0bnYMjlQ4, taskSet ecs-svc/1888478596191510698) was unable to scale in due to (reason 1 tasks under protection)

ブラウザのタブを閉じると、サーバへのクライアント接続を終了します。WebSocket サーバーへの接続がゼロになると、クライアントは自身のタスク保護を解除し、Amazon ECS がタスクを停止できるようにします。

まとめ

この記事では、Amazon ECS タスクのスケールイン保護機能を使用して、スケールインイベントによってタスクが終了しないように保護する方法を紹介しました。新しい Amazon ECS タスクのスケールイン保護機能を使用すると、コストを削減しながら、ワークロードの信頼性をより柔軟に制御できます。タスクのスケールイン保護は最も要望の多かった ECS の機能の 1 つであり、これをお客様に提供できることを大変嬉しく思います。今後も AWS コンテナサービスロードマップの GitHub にて、皆様のご意見をお待ちしております。