【AWS】SQSによるECSオートスケーリング設定方法

はじめに

本投稿は TECOTEC Advent Calendar 2025 の2日目の記事です。

システム開発事業部の大泉です。普段はエンジニアとして Python でバックエンドのシステムの開発を行なっています。

この記事ではメッセージキューの増加・減少による ECS オートスケーリングを構築する方法を紹介します。設定手順のみ知りたい方は 設定手順 をご覧ください。

キューによるスケーリング

メッセージキューを使ったシステムではメッセージ量に応じて処理サーバ(コンシューマー)の数を動的に増減させるケースがよくあります。では実際にどのくらいメッセージが溜まったら、どれだけの処理サーバを用意すればよいのでしょうか。単純にキューサイズ(キューに溜まっているメッセージ数)に応じてサーバ数を決めればよいように思えますが、実はこれでは正しく測ることができません。現在のサーバがどの程度の処理性能を持っているかが考慮されていないためです。1サーバが1メッセージあたり何秒で処理できるか、アプリケーション上1メッセージを遅くても何秒で処理してほしいか(許容時間)という情報が必要です。なおここでは ECS を想定しているためサーバ数をタスク数と書きます。より一般的にはコンシューマーのノード数を意味します。

  • 現在のキューサイズ  s (メッセージ)
  • 1タスクあたりの処理速度  v (メッセージ/秒)
  • 1メッセージを処理するのに許容される時間  t (秒)
  • タスク数  n (タスク)

とすると、1タスクが許容時間内に処理できるメッセージ数は  v \times t であり、1タスクに割り当てられたキューサイズは  s / n です。これらが等しければ、1タスクが処理できるメッセージ数と実際に割り当てられたメッセージ数が釣り合っている状態です。つまり  s / n = vt を実現できれば過不足のないスケーリングとなります。

  •  s / n : 実バックログ(1タスクに割り当てられたキューサイズ)
  •  v \times t : 適正バックログ(1タスクが処理できるメッセージ数)

ECS のオートスケーリングでは実バックログ  s / n の値を CloudWatch でリアルタイムに追跡し、あらかじめ決めておいた適正バックログ  vt と比較することでタスクを動的に増減させます。

  •  s / n \gt vt の場合: 実バックログが適正バックログを上回っているため、スケールアウトが必要( n を増やす)
  •  s / n \le vt の場合: 処理能力が追いついている(または過剰な)ため、現状維持かスケールインが適切( n を減らす)

例1)処理が追いついている場合

  • キューサイズ  s = 1,000 (メッセージ)
  • 1タスクあたりの処理速度  v = 1,000 (メッセージ/秒)
  • 許容時間  t = 10 (秒)
  • タスク数  n = 1 (タスク)

この場合、 s / n \lt vt であるためスケールアウトは不要です。

例2)処理が追いつかずスケールアウトが必要な場合

  • キューサイズ  s = 20,000 (メッセージ)
  • 1タスクあたりの平均処理速度  v = 1,000 (メッセージ/秒)
  • 許容時間  t = 10 (秒)
  • タスク数  n = 1 (タスク)

この場合、 s / n \gt vt であるためタスク数が足りておらず  n = 2 にスケールアウトする必要があります。

設定概要

ECS でオートスケーリングを適用するには Application Auto Scaling を使用してクラスターのサービスにオートスケーリングを設定します。ここではすでにサービスは作成済みとします。まずスケーラブルターゲットをサービスに登録し、次にスケーリングポリシーを作成します。ECS では次のスケーリングポリシーを利用可能です。

  • ターゲット追跡スケーリング
    • ターゲットメトリクス値に基づいてアプリケーションを自動的にスケール
  • ステップスケーリング
    • ステップ調整値にしたがって段階的にスケール
  • 予測スケーリング
    • 過去の負荷データを分析し、予想される負荷に合わせて事前にスケール

ここではターゲット追跡スケーリングを利用します。先ほどの適正バックログをターゲット値として設定します。

設定手順

  1. サービスにスケーラブルターゲットを登録
  2. スケーリングポリシーを作成

スケーリングポリシーを作成すると自動で CloudWatch アラームが作成され、オートスケーリングが始まります。

1. スケーラブルターゲットの登録

CLUSTER_NAME=autoscaling-demo-cluster
SERVICE_NAME=autoscaling-demo-service
aws application-autoscaling register-scalable-target \
--service-namespace ecs \
--scalable-dimension ecs:service:DesiredCount \
--resource-id service/$CLUSTER_NAME/$SERVICE_NAME \
--min-capacity 0 \
--max-capacity 10

2. スケーリングポリシーの作成

CLUSTER_NAME=autoscaling-demo-cluster
SERVICE_NAME=autoscaling-demo-service
POLICY_NAME=sqs-tracking-policy
aws application-autoscaling put-scaling-policy \
--policy-name $POLICY_NAME \
--service-namespace ecs \
--resource-id service/$CLUSTER_NAME/$SERVICE_NAME \
--scalable-dimension ecs:service:DesiredCount \
--policy-type TargetTrackingScaling \
--target-tracking-scaling-policy-configuration file://config.json

この config.json ではカスタムメトリクス(上記説明では実バックログ  s / n)を定義しています。TargetValue フィールドにターゲット値(上記説明では適正バックログ  v \times t )を設定します。

NOTE スケールアウトの容量は自動で決定されます。

{
  "CustomizedMetricSpecification": {
    "Metrics": [
      {
        "Label": "キューサイズ(処理可能なメッセージ数)",
        "Id": "s",
        "MetricStat": {
          "Metric": {
            "MetricName": "ApproximateNumberOfMessagesVisible",
            "Namespace": "AWS/SQS",
            "Dimensions": [
              {
                "Name": "QueueName",
                "Value": "autoscaling-demo-queue"
              }
            ]
          },
          "Stat": "Sum"
        },
        "ReturnData": false
      },
      {
        "Label": "サービス内の実行中のタスク数(RUNNINGステータスのタスク数)",
        "Id": "n",
        "MetricStat": {
          "Metric": {
            "MetricName": "RunningTaskCount",
            "Namespace": "ECS/ContainerInsights",
            "Dimensions": [
              {
                "Name": "ClusterName",
                "Value": "autoscaling-demo-cluster"
              },
              {
                "Name": "ServiceName",
                "Value": "autoscaling-demo-service"
              }
            ]
          },
          "Stat": "Average"
        },
        "ReturnData": false
      },
      {
        "Label": "タスクあたりの実バックログ",
        "Id": "e1",
        "Expression": "s / n",
        "ReturnData": true
      }
    ]
  },
  "TargetValue": 2,
  "ScaleOutCooldown": 60,
  "ScaleInCooldown": 60
}

NOTE config.json のId: n (タスク数)のメトリクスを取得するには Container Insights を有効化する必要があります。ここでは有効化手順を省略しますが、以下のドキュメントが参考になります。

docs.aws.amazon.com

docs.aws.amazon.com

リソース確認

オートスケーリングポリシー作成後、マネジメントコンソールからターゲット追跡スケーリングポリシーが設定されたことが確認できます。

スケーラブルターゲットの確認

スケーリングポリシーの確認

また以下 2 つの CloudWatch アラームが自動で作成されます。

  • {サービス名}-AlarmHigh-*
  • {サービス名}-AlarmLow-*

動作確認

以下は動作確認結果です。キューサイズ  s の増減に伴ってタスク数  n が増減し、実バックログ  s / n が以下のように緑の折れ線グラフで表現されています。赤線が TargetValue (適正バックログ)です。 s / n \gt TargetValue (実バックログ  \gt 適正バックログ)の場合アラーム状態に遷移し、スケールアウトしたことが確認できました。

スケーリングの確認

サンプルコード

以下は動作確認で使用したサンプルコードです。

FROM public.ecr.aws/docker/library/python:3.11-slim

COPY main.py main.py

CMD ["python", "-m", "main"]
import time
import logging

logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
logger = logging.getLogger(__name__)

def main():
    """数分コンテナを起動しておくための処理"""
    for i in range(36):
        logger.info(f"[{i+1}/36] Hello, World!")
        time.sleep(10)

if __name__ == "__main__":
    main()

参考

docs.aws.amazon.com

docs.aws.amazon.com

docs.aws.amazon.com

docs.aws.amazon.com

テコテックの採用活動について

テコテックでは新卒採用、中途採用共に積極的に募集をしています。
採用サイトにて会社の雰囲気や福利厚生、募集内容をご確認いただけます。
ご興味を持っていただけましたら是非ご覧ください。 tecotec.co.jp