ECS Fargate Terraform オートスケーリングの２つの方法について、まとめてみた(コードあり)

# この記事の内容

既存のECSについて、オートスケーリングの設定をすることになりました。そこでオートスケーリングの種類やTerraformでの実装コードがまとまった記事が見つからなかったので記事にしようと思いました。

# 対象

オートスケーリングの種類や実装方法がわからない。

Terraformで構築したい。

# ECSサービスのオートスケーリングについて

### ターゲット追跡スケーリングポリシー

- 指定したメトリクス(CPU使用率やメモリ)が、指定した数値(**しきい値**)に収まるようにスケールアウト／スケールインを行うオートスケーリング
- AWSは、このポリシーに基づいて必要なCloudWatchアラームを自動的に作成し、管理する。これにより、メトリックが目標値から逸脱したときにスケーリングアクションが自動的に発生する。
- 以下のポリシーに応じてスケーリング（ｘ分間連続の部分は指定不可）
    
    ・指定したメトリクスで3分間連続で**しきい値**を超過→スケールアウト
    
    ・指定したメトリクスで15分間連続で**しきい値**-3%に収束→スケールイン
    

採用シーン：アプリケーションのリソース消費に基づいて、AWS側にスケールアウト数を任せたい場合。アプリケーションの使用率が緩やかに増加していく傾向がある場合。


上：スケールアウト、下：スケールイン
![ターゲットアウト.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1874224/75d4a003-53dc-1467-27fb-2f2d109af29b.png)
![ターゲットイン.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1874224/c8922563-5694-065b-5469-33ad9deb0658.png)


## ターゲット追跡スケーリングポリシーのコード

```ruby
# aws_appautoscaling_targetはスケーリングを適用する対象を定義
resource "aws_appautoscaling_target" "appautoscaling_ecs_target" {
  service_namespace  = "ecs"  # 今回はecsがターゲット
  resource_id        = "service/${aws_ecs_cluster.api_server.name}/${aws_ecs_service.api_server.name}" # クラスター名/サービス名で定義
  scalable_dimension = "ecs:service:DesiredCount"
  min_capacity       = 1      # スケーリングする最小タスク数
  max_capacity       = 4      # スケーリングする最大タスク数
}

#具体的なスケーリングポリシーを定義
resource "aws_appautoscaling_policy" "ecs_service_cpu_tracking" {
  name               = "${aws_ecs_service.api_server.name}_cpu_tracking" # ポリシーの名前(任意だが、環境がわかるように設定したい)
  service_namespace  = "ecs"
  resource_id        = "service/${aws_ecs_cluster.api_server.name}/${aws_ecs_service.api_server.name}"
  scalable_dimension = "ecs:service:DesiredCount"
  policy_type        = "TargetTrackingScaling" # スケーリングのポリシータイプ。ターゲット追跡スケーリングを適用

  target_tracking_scaling_policy_configuration {
    predefined_metric_specification {
      predefined_metric_type = "ECSServiceAverageCPUUtilization"
    } # 予め定義されたメトリクスを定義する。今回はECSサービスの平均CPU使用率。
# 参考：https://docs.aws.amazon.com/ja_jp/AmazonECS/latest/developerguide/service-configure-auto-scaling.html

    target_value       = 50.0  # 目標CPU使用率のパーセンテージ
    scale_in_cooldown  = 300
    scale_out_cooldown = 300   #スケールイン（リソースを減らす）とスケールアウト（リソースを増やす）の際のクールダウン時間（秒）
  }
}
```

### ステップスケーリングポリシー

- 指定したメトリクスのしきい値に基づいてスケールアウト／スケールインを行うオートスケーリングする
- メトリクスのしきい値超えによるスケールアウト/インで一度に増減する台数など、細かい設定が可能
- ステップスケーリングポリシーではユーザーがCloudWatchアラームを手動で設定する必要がある
- アラームがトリガーされると、事前に定義されたステップ調整に基づいてスケーリングアクションが発生する。(ステップ調整：特定のメトリックのしきい値が超えられたときのインスタンス数の増減量のこと)

上：スケールアウト、下：スケールイン

![ステップアウト.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1874224/b86fcfa0-4652-94e5-2c8f-c3d54e3e7019.png)

![ステップイン.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1874224/a58b0c7c-14f9-61ba-f012-7f8375d24206.png)



採用シーン：スケールする条件を細かく設定したい場合。アプリケーションの使用率が急激にあがる傾向がある場合。

## ステップスケーリングポリシーのコード

```ruby
# aws_appautoscaling_targetはスケーリングを適用する対象を定義
resource "aws_appautoscaling_target" "appautoscaling_ecs_target" {
  service_namespace  = "ecs"
 
  resource_id        = "service/${aws_ecs_cluster.api_server.name}/${aws_ecs_service.api_server.name}" # クラスター名/サービス名で定義
  scalable_dimension = "ecs:service:DesiredCount"

  min_capacity       = 1      # スケーリングする最小タスク数
  max_capacity       = 4      # スケーリングする最大タスク数
}

# スケールアウトの定義(タスク増加数)
resource "aws_appautoscaling_policy" "appautoscaling_scale_out" {
  name               = "${aws_ecs_service.api_server.name}_scale_out"  # ポリシーの名前(任意だが、環境がわかるように設定したい)
  service_namespace  = "ecs"
  
  resource_id        = "service/${aws_ecs_cluster.api_server.name}/${aws_ecs_service.api_server.name}"
  scalable_dimension = "ecs:service:DesiredCount"

	# ステップスケーリングポリシーの設定
  step_scaling_policy_configuration {
    adjustment_type         = "ChangeInCapacity"
    
    cooldown                = 120    # クールダウンタイム。120秒後に再度スケーリングする。
    metric_aggregation_type = "Average"  # メトリクスの集計タイプ、今回は「平均値」

    # ステップ調整の設定(タスクの増加数)
    step_adjustment {
      metric_interval_lower_bound = 0
      scaling_adjustment          = 1    # 一度に増加させたいタスク数を設定
    }
  }
}

# スケールインの定義(タスク減少数)
resource "aws_appautoscaling_policy" "appautoscaling_scale_in" {
  name               = "${aws_ecs_service.api_server.name}_scale_in"
  service_namespace  = "ecs"
  
  scalable_dimension = "ecs:service:DesiredCount"

	# ステップスケーリングポリシーの設定
  step_scaling_policy_configuration {
    adjustment_type         = "ChangeInCapacity"
    
    cooldown                = 120
    metric_aggregation_type = "Average"

    # ステップ調整の設定(タスクの減少数)
    step_adjustment {
      metric_interval_upper_bound = 0
      scaling_adjustment          = -1    # 一度に減少させたいタスク数を設定
    }
  }
}

# スケールアウトするためのアラーム
resource "aws_cloudwatch_metric_alarm" "alarm_cpu_high" {
  alarm_name          = "${aws_ecs_service.api_server.name}_cpu_utilization_high" # アラームの名前(任意だが、環境がわかるように設定したい)
  
  # しきい値（threshold）以上の時にアラートが鳴るように設定
  comparison_operator = "GreaterThanOrEqualToThreshold"
  evaluation_periods  = "1"
  metric_name         = "CPUUtilization"
  namespace           = "AWS/ECS"
  period              = "60"
  statistic           = "Average"
  
  threshold           = "80"  # CPU使用率が80%以上のとき、アラーム

  # ターゲットのECSクラスター名とサービス名を指定
  dimensions = {
    ClusterName = aws_ecs_cluster.api_server.name
    ServiceName = aws_ecs_service.api_server.name
  }
  
  # アラート発生時に実行するpolicyを設定。スケールアウトの定義のものを記載する。
  alarm_actions = [aws_appautoscaling_policy.appautoscaling_scale_out.arn]
}

resource "aws_cloudwatch_metric_alarm" "alarm_cpu_low" {
  alarm_name          = "${aws_ecs_service.api_server.name}_cpu_utilization_low"
  
  # しきい値（threshold）以下の時にアラートが鳴るように設定
  comparison_operator = "LessThanOrEqualToThreshold"
  evaluation_periods  = "1"
  metric_name         = "CPUUtilization"
  namespace           = "AWS/ECS"
  period              = "60"
  statistic           = "Average"
  
  threshold           = "30"   # CPU使用率が30%以下のとき、アラーム

  dimensions = {
    ClusterName = aws_ecs_cluster.api_server.name
    ServiceName = aws_ecs_service.api_server.name
  }

  # アラート発生時に実行するpolicyを設定。スケールインの定義のものを記載する。
  alarm_actions = [aws_appautoscaling_policy.appautoscaling_scale_in.arn]
}
```

# 終わりに

初めてECSのオートスケーリングの設定をしましたが、CPU使用率に応じて自動で調整してくれて率直に便利だなと思いました。

設定する際に、AWSのコンソール画面からGUIで設定する方法も調査したのですがUIがコロコロ変わっているのでIaCで管理すると迷うことが少なく保守がしやすいと感じます。

Terraformのドキュメントも充実していたので、参考にのせておきます。

# 参考

https://docs.aws.amazon.com/ja_jp/AmazonECS/latest/developerguide/service-configure-auto-scaling.html

https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/appautoscaling_target

https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/appautoscaling_policy

https://zenn.dev/spacemarket/articles/4eb067a734cacd