FURYU Tech Blog - フリュー株式会社

フリュー株式会社の開発者が技術情報を発信するブログです。

スケジュールを利用したECSのスケール設定

この記事はフリューAdvent Calendar 2024の4日目の記事となります。

はじめに

フリュー株式会社ピクトリング事業部で、SREエンジニアをしている山根です。

プッシュ通知やイベント開始時は、急激なアクセスが来ることがあるかと思います。
弊社では、そういった急激なアクセスに対応するために、スケジュールを利用したECSのスケール設定を行っています。

スケール設定の流れ

プッシュ通知を例にあげると、以下のような流れになります。

  1. プッシュ通知を送信する前に、スケールアウト、(必要であれば)スケールインの設定をしておく
  2. プッシュ通知の送信開始に合わせて、スケールアウト実行
  3. プッシュ通知によるアクセスが落ち着くくらい(弊社の場合はだいたい1時間くらい)で、スケールイン実行

必要なRole設定

スケジュールを利用したECSのスケール設定を行うためには、application-autoscaling:PutScheduledActionを許可するRoleを準備します。
弊社では、スケールアウト時は、minCapacityを現在の稼働中のタスク数+加算するタスク数 とし、それがmaxCapacityを超える場合はmaxCapacityのタスク数としています。
また、スケールイン時は、元のminCapacityに戻すようにしています。
そのため、現在の稼働中のタスク数を取得するためにecs:DescribeServices、 現在のminCapacity、maxCapacityを取得するためにapplication-autoscaling:DescribeScalableTargetsの許可も追加しています。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Action": "ecs:DescribeServices",
            "Resource": [
                "arn:aws:ecs:ap-northeast-1:0123456789:service/${clusterName}/${serviceName}"
            ],
            "Effect": "Allow",
            "Sid": "Statement1"
        },
        {
            "Action": [
                "application-autoscaling:DescribeScalableTargets",
                "application-autoscaling:PutScheduledAction"
            ],
            "Resource": "arn:aws:application-autoscaling:ap-northeast-1:0123456789:scalable-target/*",
            "Effect": "Allow",
            "Sid": "Statement2"
        }
    ]
}

単純にスケールするだけであれば、application-autoscaling:PutScheduledActionだけの許可で問題ないです。

実例

では、2025年1月1日の18時に稼働中のタスク数に対して10タスク加算し、19時に元の最小タスク数に戻す場合を例にあげます。
シェルスクリプトでは以下のような感じになります。

#!/bin/bash

clusterName="対象のクラスター名"
serviceName="対象のサービス名"

# 起動中のタスク数を取得
runningCount=`aws ecs describe-services \
  --cluster ${clusterName} \
  --services ${serviceName} \
  --output json | jq -r '.services[] | .runningCount'`

# 稼働中のタスク数に10タスク加算した値
targetCount=$runningCount+10

# ターゲットの取得(スケール設定がすでにある前提)
scalableTargets=`aws application-autoscaling describe-scalable-targets \
  --service-namespace ecs \
  --resource-ids service/${clusterName}/${serviceName} \
  --scalable-dimension ecs:service:DesiredCount \
  --output json | jq -r '.ScalableTargets[0]'`

# 現在の最小キャパシティを取得(元に戻す際に使用)
minCapacity=`echo $scalableTargets | jq -r '.MinCapacity'`
# 現在の最大キャパシティを取得
maxCapacity=`echo $scalableTargets | jq -r '.MaxCapacity'`
# スケールアウトで利用する最小キャパシティを計算
newMinCapacity=$(( (targetCount) < maxCapacity ? targetCount : maxCapacity ))

# スケールアウトのスケジュール設定(UTC)
scaleOutScheduleTime="2025-01-01T18:00:00"
# スケールアウトのスケジュール登録
aws application-autoscaling put-scheduled-action --service-namespace ecs \
  --scalable-dimension ecs:service:DesiredCount \
  --resource-id service/${clusterName}/${serviceName} \
  --scheduled-action-name ecs-scale-out \
  --schedule "at(" + ${scaleOutScheduleTime} + ")" \
  --scalable-target-action MinCapacity=${newMinCapacity}

# スケールインのスケジュール設定(UTC)
scaleInScheduleTime="2025-01-01T19:00:00"
# スケールインのスケジュール登録
aws application-autoscaling put-scheduled-action --service-namespace ecs \
  --scalable-dimension ecs:service:DesiredCount \
  --resource-id service/${clusterName}/${serviceName} \
  --scheduled-action-name ecs-scale-in \
  --schedule "at(" + ${scaleInScheduleTime} + ")" \
  --scalable-target-action MinCapacity=${minCapacity}

put-scheduled-actionのオプションについては、以下に詳細を記載します。
put-scheduled-actionのオプション説明

  • --service-namespace(必須)
    • 対象のサービスの名前空間を指定します。 今回は、ECSなので、ecsを指定します。
  • --scalable-dimension(必須)
    • スケール対象を指定します。 ECSの場合は、ecs:service:DesiredCountを指定します。
  • --resource-id(必須)
    • スケール対象のリソースを指定します。${clusterName}には、クラスタ名を、${serviceName}には、サービス名を指定します。
  • --scheduled-action-name(必須)
    • スケジュールの名前を指定します。この名前と全く同じ名前のスケジュールが既に登録されている場合は、既存設定の上書きとなります。
  • --schedule
    • スケジュールを指定します。
      日時は、UTCで指定する必要があります。UTC以外の日時を入れる場合は、--timeszoneを利用して、タイムゾーンを指定する必要があります。
      • at(2025-01-01T18:00:00)の場合
        一度のみの実行となり、指定した時刻に実行されます。
        この場合は、--start-time--end-timeを指定することができませんので、ご注意ください。
      • rate(5 minutes)cron(0 18 * * ? *)の場合
        これらの設定の場合は、--start-time--end-timeを指定することができます。
  • --scalable-target-action
    • スケール設定を指定します。 ECSの場合は、MinCapacityとMaxCapacityが設定できます。
      元々のMaxCapacityを超えたMinCapacityを設定すると、MaxCapacityは、MinCapacityと同値に設定されるようになっています。

これで、put-scheduled-actionを利用して、プッシュ通知開始時、終了時にスケールの設定をすることができました。
put-scheduled-actionを活用することで、プッシュ通知、イベント、深夜帯等のスケジュールに沿ったスケール設定が簡単にできて便利かと思います。
また、predictive scaling(予測スケーリング)がECSに追加されたので、近々触ってみたいと思います。

おまけ

rate(5 minutes)cron(0 18 * * ? *)を利用して、
--start-time--end-timeを指定する場合のユースケースを考えてみました。

あくまで個人の見解ではありますが、イベント期間中(例えば、12/01~12/10の間だけとか)に対して、毎日16時に負荷があがり、23時に落ち着くような傾向があるとわかっている場面等で利用するのかなと思います。

上記の例の場合、

  • 12/01~12/10の間、毎日16時にMinCapacityを増やす
  • 12/01~12/10の間、毎日23時にMinCapacityを元に戻す

のようなスケール設定をしておく感じです。
ここでは、--timeszoneAsia/Tokyoを設定して、JSTで時刻を設定していきます。
スケールアウトの設定は、

aws application-autoscaling put-scheduled-action --service-namespace ecs \
  --scalable-dimension ecs:service:DesiredCount \
  --resource-id service/${clusterName}/${serviceName} \
  --scheduled-action-name ecs-scale-out \
  --schedule "cron(0 16 * * ? *)" \
  --start-time 2024-12-01T00:00:00 \
  --end-time 2024-12-11T00:00:00 \
  --timezone Asia/Tokyo \
  --scalable-target-action MinCapacity=10

スケールインの設定は、

aws application-autoscaling put-scheduled-action --service-namespace ecs \
  --scalable-dimension ecs:service:DesiredCount \
  --resource-id service/${clusterName}/${serviceName} \
  --scheduled-action-name ecs-scale-in \
  --schedule "cron(0 23 * * ? *)" \
  --start-time 2024-12-01T00:00:00 \
  --end-time 2024-12-11T00:00:00 \
  --timezone Asia/Tokyo \
  --scalable-target-action MinCapacity=5

こんな感じになるかなと思います。社内ツール等で業務の稼働状態に合わせてスケール設定するとかでも使えそうです。