ECS+FargateのAutoScaling設定をTerraformで定義する


はじめに

ECS+Fargateでサーバレス&運用省エネにシステムを構築しているのだから、やはりここは負荷量に応じた運用負荷も軽減したい。

TerraformでサクッとAutoScaling設定をしてみよう。

なお、本記事ではキャパシティプロバイダは使用しないケースで対応している。
また、マルチAZ構成を前提としてTerraformを書いている。Fargateの前段に配置するELBは、クロスゾーン負荷分散を有効にしておかないと、AutoScalingで1台増設が走ったときに正しく分散されないので注意(クロスゾーン負荷分散はNLBのみデフォルトでOFF)。

ECS+FargateのTerraform設定

今回はこの部分は本筋ではないので割愛する。
自分の過去記事では、↓このあたりが参考になると思う。

Terraformの初心者がAmazon EC2に実行環境を作ってECS Fargateなアプリの自動構築をしてみる

上記の通り、マルチAZ前提であるため、タスクの desired_count は2にしておく。

AutoScaling Policyの設定

以下のように、aws_appautoscaling_targetaws_appautoscaling_policy で構成されている。今回は、平均CPU使用率に応じたスケールアウト、スケールインを設定する。

resource "aws_appautoscaling_target" "ecsfargate" {
  service_namespace  = "ecs"
  resource_id        = "service/${aws_ecs_cluster.ecsfargate.name}/${aws_ecs_service.ecsfargate_service.name}"
  scalable_dimension = "ecs:service:DesiredCount"
  min_capacity       = 2
  max_capacity       = 4
}

resource "aws_appautoscaling_policy" "ecsfargate_scale_out" {
  name               = "scale_out"
  policy_type        = "StepScaling"
  service_namespace  = aws_appautoscaling_target.ecsfargate.service_namespace
  resource_id        = aws_appautoscaling_target.ecsfargate.resource_id
  scalable_dimension = aws_appautoscaling_target.ecsfargate.scalable_dimension

  step_scaling_policy_configuration {
    adjustment_type         = "ChangeInCapacity"
    cooldown                = 60
    metric_aggregation_type = "Average"

    step_adjustment {
      metric_interval_lower_bound = 0
      scaling_adjustment          = 1
    }
  }
}

resource "aws_appautoscaling_policy" "ecsfargate_scale_in" {
  name               = "scale_in"
  policy_type        = "StepScaling"
  service_namespace  = aws_appautoscaling_target.ecsfargate.service_namespace
  resource_id        = aws_appautoscaling_target.ecsfargate.resource_id
  scalable_dimension = aws_appautoscaling_target.ecsfargate.scalable_dimension

  step_scaling_policy_configuration {
    adjustment_type         = "ChangeInCapacity"
    cooldown                = 60
    metric_aggregation_type = "Average"

    step_adjustment {
      metric_interval_upper_bound = 0
      scaling_adjustment          = -1
    }
  }
}

上記が理解できたら、今度は、AutoScaling を発動するための CloudWatchメトリクスのアラームを設定する。
ecsfargate_cpu_high が、ECSのサービスのCPU使用率が75%以上になったときに発動、ecsfargate_cpu_low が、ECSのサービスのCPU使用率が75%以下になったときに発動する。

resource "aws_cloudwatch_metric_alarm" "ecsfargate_cpu_high" {
  alarm_name          = "cpu_utilization_high"
  comparison_operator = "GreaterThanOrEqualToThreshold"
  evaluation_periods  = "2"
  metric_name         = "CPUUtilization"
  namespace           = "AWS/ECS"
  period              = "60"
  statistic           = "Average"
  threshold           = "75"

  dimensions = {
    ClusterName = aws_ecs_cluster.ecsfargate.name
    ServiceName = aws_ecs_service.ecsfargate_service.name
  }

  alarm_actions = [
    aws_appautoscaling_policy.ecsfargate_scale_out.arn
  ]
}

resource "aws_cloudwatch_metric_alarm" "ecsfargate_cpu_low" {
  alarm_name          = "cpu_utilization_low"
  comparison_operator = "LessThanOrEqualToThreshold"
  evaluation_periods  = "2"
  metric_name         = "CPUUtilization"
  namespace           = "AWS/ECS"
  period              = "60"
  statistic           = "Average"
  threshold           = "25"

  dimensions = {
    ClusterName = aws_ecs_cluster.ecsfargate.name
    ServiceName = aws_ecs_service.ecsfargate_service.name
  }

  alarm_actions = [
    aws_appautoscaling_policy.ecsfargate_scale_in.arn
  ]
}

設定後の動作

設定すると、以下のようなCloudWatchアラームの設定が入る。
※キャプチャは、実際に負荷を入れてスケールアウトが発動したもの

実際にスケールアウトが発動すると、マネージメントコンソールに以下のログが出力される。
一番上の行は、スケールアウトによりECSサービス全体のCPU使用率が下がり、OKの状態になった際のものだ。

スケールインするときも同様、以下のようになる。

ECSのイベントログも、以下のような感じで、2タスク単位で動いているのが分かる。

これで、トラフィック量に応じて自由にスケールアウト/インできるようになった!