AWS ECSを1インスタンス1タスク構成でスケールするための工夫

L小川です。
ECS on EC2ではインスタンス・タスクそれぞれがスケールできるので、スケーリング方法についてもいろいろなやり方があるかと思います。
ECSのスケーリングについて検討する際、情報があまり見当たらず難儀したので、本ブログで一例をご紹介します。
今回はAWS ECS(以下ECS)で1インスタンス上に1つのタスクが載った構成でスケールする方法についてご紹介します。
今回の仕組みを構築するにあたり、以下サイトの情報を参考にしました。
こちらもご参照ください。

ロマサガRSの大規模負荷を処理するAmazon ECS & Docker運用知見

Amazon ECS におけるコンテナ インスタンス ドレイニングの自動化方法
ECS運用に悩む人

前提

  • ECS on EC2を利用し、通常稼働時は各インスタンス上に1タスクが動いている状態です。
  • タスクの配置戦略は「Spread – InstanceId」で、インスタンスに均等にタスクが割り振られる設定にしています。

ASGとECSを連動させない場合

Auto Scaling グループ(以下ASG)とECSを連動させず、1インスタンス・1タスクの構成のままスケールする場合は、以下のような手順になるかと思います。

増やす場合

  1. ASGでインスタンスを増やす
  2. ECSインスタンスに表示されるまで待つ
  3. ECSのサービスで「タスクの数」をASGで変更したインスタンス数に合わせる

減らす場合

  1. ECSのサービスで「タスクの数」を減らす
  2. ASGでインスタンス数をECSのタスク数に合わせる

ECSのサービスでタスクの数を減らした後、そのままASGでインスタンスを減らすと、タスクが載っているインスタンスが停止される可能性があります。その場合、停止されるインスタンス上のタスク停止と、残ったインスタンス上でタスクの作成が行われます。

Auto Scalingグループでインスタンスを減らした際のECSタスクの挙動

最終的に1インスタンス1タスクの構成になりますが、タスクの作成にはある程度の負荷がかかり、それなりに時間もかかるので避けたい作業です。
この場合、ASGのスケールイン保護を使うとタスク再作成の余計な処理を省けます。

  1. ECSのサービスで「タスクの数」を減らす
  2. タスクが載っているインスタンスをASGでスケールイン保護の対象にする(タスクの載っていないインスタンスだけがスケールイン保護されていない状態)
  3. ASGで「希望する容量」「最小キャパシティ」「最大キャパシティ」を、ECSのタスク数に合わせる

スケールイン保護の設定

インスタンスがスケールイン保護される図

しかし、上記の方法だと作業の手数も多く、操作ミスも起きそうです。
インスタンスとタスクの数を合わせてスケールできるよう、下記の仕組みを構築しました。

ASGとECSを連動してスケールする仕組み

増やす場合

Auto Scaling グループとECSを連動してスケールアウトする仕組みの概要図

  1. ASGでインスタンスを増やす
  2. CloudWatch Eventsの”EC2 Instance Launch Successful”をトリガーにしたルールからSNSトピックに通知する
  3. SNSトピックからLambdaを起動する
  4. LambdaはECSにインスタンスが登録されるまで、一定時間のsleepと呼び出し元のSNSへのメッセージ発行を繰り返す
  5. ECSにインスタンスが登録されたら、ECSの「希望するタスクの数」をASGのインスタンス数と同じ数に変更する

手動での作業と同様に、ECSにインスタンスが登録されるまで待機してからタスク数を増やしています。

減らす場合

Auto Scaling グループとECSを連動してスケールインする仕組みの概要図

  1. ASGでインスタンスを減らす
  2. CloudWatch Eventsの”EC2 Instance-terminate Lifecycle Action”をトリガーにしたルールからSNSトピックに通知する
  3. SNSトピックからLambdaを起動する
  4. 停止するインスタンス上のタスクをELBのターゲットグループから外す。同時にECSのタスク数を−1する
  5. Lambdaはタスクがターゲットグループから解除されるまで、一定時間のsleepと呼び出し元のSNSへのメッセージ発行を繰り返す
    • SNSのメッセージにフラグを追加してLambda内の処理を切り分けています。
    • ターゲットグループからの解除はすぐに行われるわけではなく、ターゲットグループ のAttributes > Deregistration delayで指定した時間の後に解除されます。これは受け取ったリクエストを返すための猶予時間になります。
  6. タスクがターゲットグループから解除できたら、タスクを停止し、インスタンスをドレイニングする
  7. インスタンスがドレイニング状態になり、インスタンス上のタスク数が0になったら、ライフサイクルを「完了」状態にする
    • インスタンスはその後削除されます。

※SNS・Lambdaの処理を実行するため、インスタンスが停止・削除されるまでの猶予時間として300秒のライフサイクルフックを設定しています。

やってみて

ASGとECSの画面を行ったり来たりしない分、運用の煩雑さを軽減できました。
負荷に応じてインスタンスを増減するオートスケーリングでも使える仕組みだと思います。
とてもピンポイントな内容ですが、この記事がECSのスケール運用でお悩みの方の参考になれば幸いです。