ターゲット追跡スケーリングポリシーを使ってAmazon SQSキューのメッセージ数でAmazon ECSタスクをオートスケーリングしてみた
SQSメッセージ数に応じてECSタスクのオートスケーリング
スケーリングポリシー
ECSのスケーリングポリシーの内ターゲット追跡とステップスケーリングでは、任意のCloudWatchメトリクスをターゲットメトリクスとすることができます。
ターゲット追跡では、しきい値を維持するようにスケールイン・スケールアウトを自動的に行ってくれます。
一方ステップスケーリングでは、スケールイン・スケールアウトのタスク数をユーザー側で指定する必要があります。
設定の手間はかかりますが、柔軟な設定ができるため急激な負荷増大にも対応しやすいです。
今回のケースでは、ターゲット追跡では、「1タスクで何件処理したいかを設定する」だけで良いです。
一方ステップスケーリングでは、メッセージ数ごとに設定が必要です。
例:
- 0-5件/タスク: 何もしない
- 5-15件/タスク: +1タスク
- 15-25件/タスク: +3タスク
- 25件/タスク超: +5タスク
設定がシンプルかつ直感的にできるため、ターゲット追跡を選択します。
ターゲットメトリクス
ターゲットメトリクスにSQSメッセージ数を指定することで、メッセージ数に応じてECSのタスク数を増やすことができます。
CloudWatch Amazon SQSメトリクスのApproximateNumberOfMessagesVisible
でメッセージ数を取得できます。
しかし、直接SQSメッセージ数を使用すると、現在のタスク数を考慮しない判断となります。
例: 閾値50件、現在60件の場合
- 2タスク稼働中 → 30件/タスク(スケールアウト必要)
- 10タスク稼働中 → 6件/タスク(スケールアウト不要)
同じメッセージ数でも、適切な判断ができません。
1タスクあたりのメッセージ数(メッセージ数 / タスク数
)をしきい値とすることで、比率ベースでのスケーリングが可能です。
目標値10件/タスクの場合:
- 60件、2タスク → 30件/タスク → スケールアウト
- 60件、10タスク → 6件/タスク → スケールイン
EC2ベースの記事になりますが、もっと詳しく知りたい場合は以下が参考になると思います。
Scaling policy based on Amazon SQS - Amazon EC2 Auto Scaling
構成
以下のブログとほぼ同じ構成です。
カスタムメトリクスを用いた Amazon Elastic Container Service (ECS) のオートスケーリング | Amazon Web Services ブログ
異なる部分はタスクあたりのバックログの計算をLambdaではなく、CloudWatch Metric Mathを使っている点です。
やってみた
リソースのデプロイ
リソースはTerraformで作成しました。
1タスクあたり10件のメッセージ処理を目標に、ターゲット追跡のしきい値を10としました。
10メッセージ/1タスクを超えたら、スケールアウトします。最大タスク数は5タスクとしました。
resource "aws_appautoscaling_target" "ecs_target" {
max_capacity = 5
min_capacity = 1
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]
}
# ECS Target Tracking Policy
resource "aws_appautoscaling_policy" "ecs" {
name = "${local.prefix}-target-tracking"
policy_type = "TargetTrackingScaling"
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
target_tracking_scaling_policy_configuration {
target_value = 5.0
scale_out_cooldown = 60
scale_in_cooldown = 120
customized_metric_specification {
metrics {
label = "Get the queue size (the number of messages waiting to be processed)"
id = "m1"
metric_stat {
metric {
metric_name = "ApproximateNumberOfMessagesVisible"
namespace = "AWS/SQS"
dimensions {
name = "QueueName"
value = aws_sqs_queue.task_queue.name
}
}
stat = "Sum"
}
return_data = false
}
metrics {
label = "Get the ECS running task count (the number of currently running tasks)"
id = "m2"
metric_stat {
metric {
metric_name = "RunningTaskCount"
namespace = "ECS/ContainerInsights"
dimensions {
name = "ClusterName"
value = aws_ecs_cluster.main.name
}
dimensions {
name = "ServiceName"
value = aws_ecs_service.main.name
}
}
stat = "Average"
}
return_data = false
}
metrics {
label = "Calculate the backlog per instance"
id = "e1"
expression = "m1 / m2"
return_data = true
}
}
}
}
コードの全量は以下から確認できます。
terraform-sample/sqs-ecs-autoscaling at main · msato0731/terraform-sample · GitHub
Terraformを実行してリソースを作成します。
terraform init
terraform plan
terraform apply
設定確認
ECSのAuto Scaling設定は以下です。
スケールイン・スケールアウト 2つのCloudWatch アラームが自動的に作成されました。
メトリクスにはCloudWatch Metric Mathで、SQSメッセージ数/ECSタスク数をした値が設定されています。
動作確認
実際にスケールアウトを確認してみます。
SQSにてメッセージ送信します。テスト用に50件程度送信します。
スケールアウトアラームをみるとステータスがAlarmに変わり、AutoScalingアクションが実行されました。
ECSサービスのイベントからもAuto Scalingが開始されたことを確認できました。
タスク数も5個になっていました。
最後にスケールインを確認します。
SQSのメッセージを削除します。メッセージ数を0にします。
15分程度待つと、スケールイン用のCloudWatchアラームがAlarm状態になり、Auto Scalingアクションが行われました。
1タスクずつスケールインが行われ、9分程度で1タスクになりました。
おわりに
SQSメッセージ数に応じて、ECS Fargateをターゲット追跡スケーリングポリシーでオートスケーリングしてみました。
マネジメントコンソール上では、ターゲット追跡スケーリングのメトリクスタイプが以下から選ぶ形になり任意のメトリクスを渡すことができません。
- ECSServiceAverageCPUUtilization
- ECSServiceAverageMemoryUtilization
- ALBRequestCountPerTarget
Application Auto ScalingのAPIは任意のメトリクスに対応しています。
PutScalingPolicy - Application Auto Scaling
そのためAWS CLIやTerraform上からは、任意のメトリクスをターゲットメトリクスにすることができます。