SQSキューのメッセージ数が0のときにECSタスクを0にする(ECS Auto Scaling)
SQSキューのメッセージ数をメトリクスに、Amazon ECSタスクをAuto Scalingしてみました。
ターゲット追跡スケーリングポリシーを使ってAmazon SQSキューのメッセージ数でAmazon ECSタスクをオートスケーリングしてみた | DevelopersIO
コスト削減のために、キューのメッセージ数が0になったらECSタスクも0にしたいです。
簡単に実現できる方法がないか調べてみました。
結論
前提として、上記のブログの設定を行っていることとします。
- タスクの最小数を0にする
- タスク数が0になると、SQSメッセージ数が増えてもスケーリングしないため、起動の仕組みを入れる必要がある
タスク数を0にする方法
タスクの最小数を0に設定(ターゲット追跡スケーリングポリシーのみ利用)
様々な方法はあると思いますが、タスクの最小数を0にするのが一番楽だと思います。
resource "aws_appautoscaling_target" "ecs_target" {
max_capacity = 5
min_capacity = 0 # タスクの最小数を0に
resource_id = "service/${aws_ecs_cluster.main.name}/${aws_ecs_service.main.name}"
scalable_dimension = "ecs:service:DesiredCount"
service_namespace = "ecs"
depends_on = [aws_ecs_service.main]
}
ターゲット追跡ポリシーでは、タスクの最小数が0でメトリクス(今回はSQSメッセージ数 /タスク数)の値が連続して0になるとタスク数を0までスケールインします。
これによって、メッセージ数が0になるとタスク数も0になります。
メトリクスが CloudWatch に実数 0 の値を出力する場合 (ALBRequestCountPerTarget など)、Application Auto Scaling は、長期間アプリケーションへのトラフィックがない場合に 0 にスケールインできます。スケーラブルターゲットにリクエストがルーティングされないときにターゲットを 0 にスケールインするには、スケーラブルターゲットの最小容量が 0 に設定されている必要があります。
Application Auto Scaling のターゲット追跡スケーリングの仕組み - Application Auto Scaling
タスク数を0にするステップスケーリングを設定
ステップスケーリングを併用する方法もあります。
SQSメッセージが0になったらアラートになるCloudWatch Alarmを作成して、ステップスケーリングのターゲットに使う方法です。
設定は直感的で分かりやすいですが、CloudWatch Alarmの設定やステップスケーリングの設定が必要で少し手間はかかります。
設定のシンプルさから個人的におすすめは、前述の「タスクの最小数を0に設定(ターゲット追跡スケーリングポリシーのみ利用)」です。
resource "aws_appautoscaling_policy" "scale_down_to_zero" {
name = "${local.prefix}-scale-down-to-zero"
policy_type = "StepScaling"
resource_id = aws_appautoscaling_target.ecs_target.resource_id
scalable_dimension = aws_appautoscaling_target.ecs_target.scalable_dimension
service_namespace = aws_appautoscaling_target.ecs_target.service_namespace
step_scaling_policy_configuration {
adjustment_type = "ExactCapacity"
cooldown = 300
metric_aggregation_type = "Average"
step_adjustment {
scaling_adjustment = 0
metric_interval_upper_bound = 0
}
}
}
resource "aws_cloudwatch_metric_alarm" "sqs_empty" {
alarm_name = "${local.prefix}-sqs-empty"
comparison_operator = "LessThanThreshold"
evaluation_periods = "2"
metric_name = "ApproximateNumberOfMessagesVisible"
namespace = "AWS/SQS"
period = "60"
statistic = "Sum"
threshold = "1"
alarm_description = "This metric monitors empty SQS queue"
alarm_actions = [aws_appautoscaling_policy.scale_down_to_zero.arn]
dimensions = {
QueueName = aws_sqs_queue.task_queue.name
}
}
注意点: タスク数が0になると、SQSメッセージが増えてもタスクが起動しない
タスク数0の状態では、メッセージが追加されてもタスクは起動しません。
今回のターゲットメトリクスでは、タスク数が0になった時点で、ターゲット追跡ポリシーがスケーリングの動作を停止するためです
ターゲットのメトリクスは、カスタムメトリクスを使用しています。
1タスクあたりのSQSメッセージ数(SQSメッセージ数 / ECSタスク数)のカスタムメトリクスを設定しています。
タスク数が0になると0で除算する形になり、スケーリング用のCloudWatchアラームがINSUFFICIENT_DATA
(データ不足)状態になるためです。
メトリクスにデータポイントがない場合、CloudWatch アラームの状態は INSUFFICIENT_DATA に変化します。この状態になると、Amazon EC2 Auto Scaling は、新しいデータポイントが見つかるまでグループをスケールできなくなります。
Amazon EC2 Auto Scaling のターゲットトラッキングスケーリングポリシー - Amazon EC2 Auto Scaling
タスク数を0までスケールインする場合は、別途タスクを起動する仕組みを設定する必要があります。
例えば、以下のような方法があると思います。
- スケジュールされたアクションで定期的に最小タスク数を1以上にする
- ステップスケーリングで一定のメッセージ数になったら、タスクを追加
スケジュールされたアクション
スケジュールされたアクションでは、時間や周期(cron)を指定して定期的にタスクの最小数と最大数を設定できます。
周期的な起動で良い場合は設定がシンプルなのでおすすめの方法です。
スケジュールされたアクションを使用して Amazon ECS サービスをスケールする - Amazon Elastic Container Service
ステップスケーリング
SQSメッセージ数がしきい値を超えたら起動したい場合は、ステップスケーリングを使います。
スケールアウトのステップスケーリングを入れる場合は、クールダウン・追加タスク数・最大タスク数の設定に注意が必要です。
INSUFFICIENT_DATA
を防ぐためには、別のメトリクスをターゲットにする必要があります。
例えば、SQSメッセージ数(1タスク毎ではなく全量)等です。
ステップスケーリングでは「1タスク追加して、その後タスクを追加しない。」という設定はできません。
アラーム状態が続くようであれば、クールダウン時間を待ってタスクが追加されます。
ターゲット追跡でもタスクが追加されることが考えられるため、必要以上にスケールアウトが発生し不要な課金が発生することが考えられます。
もし併用する場合は最初の1タスクの起動はステップスケーリング、その後はターゲット追跡でスケールアウトの方針で、ステップスケーリングのクールダウン時間を長めに設定したほうが良いと思います。
Amazon ECS サービスの自動スケーリングのステップスケーリングポリシーを作成する - Amazon Elastic Container Service
おわりに
SQSキューのメッセージが0になった際に、ECSタスクを0にスケールインする方法についてでした。
タスクの最小数を0に設定するだけ、メッセージ数が0になったときにタスクが停止します。
注意点としては、タスク数が0になるとメトリクスの計算ができなくなり、メッセージ数が増えてもタスクが起動しません。
スケジュールされたアクション等を使って、タスクを1以上にする仕組みを構築する必要があります。