Terraformで Fargateのサービスに日付と時刻に基づいたスケジュールを設定する

##はじめに
本記事では、Terraformで Fargateのサービスに、日付と時刻に基づいてスケールイン/アウトするスケジュールを設定するサンプルについて記載しています。

サービスのスケジュールの設定自体は、 AWS CLIでも設定可能です。
参考：

https://gist.github.com/toricls/fea9d8a4eb606a27f6666a1abc6a6fd8

##Terraform で構築するサンプルの全体構成図
![00_fargate-autoscaling-sched.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/283246/3c6649ad-497d-6f3b-e9b5-9747761d9f22.png)

#Terraform のサンプルコードと構成
https://github.com/okubo-t/aws-tf-fargate-autoscaling-sched.git

```
$ tree aws-tf-fargate-autoscaling-sched
aws-tf-fargate-autoscaling-sched
├── modules
│   ├── autoscaling
│   │   ├── ecs_autoscaling.tf
│   │   ├── outputs.tf
│   │   └── variables.tf
│   ├── ecs
│   │   ├── alb.tf
│   │   ├── alb_listener.tf
│   │   ├── alb_security_group.tf
│   │   ├── alb_target_group.tf
│   │   ├── ecr_repository.tf
│   │   ├── ecs_cluster.tf
│   │   ├── ecs_security_group.tf
│   │   ├── ecs_service.tf
│   │   ├── ecs_task.tf
│   │   ├── ecs_task_definition.json
│   │   ├── ecs_task_role.tf
│   │   ├── ecs_task_trust_policy.json
│   │   ├── outputs.tf
│   │   └── variables.tf
│   └── network
│       ├── internet_gateway.tf
│       ├── outputs.tf
│       ├── route_table.tf
│       ├── subnet.tf
│       ├── variables.tf
│       └── vpc.tf
├── main.tf
├── provider.tf
├── Dockerfile
├── outputs.tf
├── terraform.tfvars-
└── variables.tf
```

Fargateのサービスに、日付と時刻に基づいてスケールイン/アウトするスケジュールを設定しているtfファイル
参考：

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

```./modules/autoscaling/ecs_autoscaling.tf
resource "aws_appautoscaling_target" "ecs" {
  resource_id        = "service/${var.cluster_name}/${var.service_name}"
  scalable_dimension = "ecs:service:DesiredCount"
  service_namespace  = "ecs"
  min_capacity       = 1
  max_capacity       = 1

  lifecycle {
    ignore_changes = [min_capacity, max_capacity]
  }

}

resource "aws_appautoscaling_scheduled_action" "scale_out" {
  name               = var.scale_out_action.name
  resource_id        = aws_appautoscaling_target.ecs.resource_id
  scalable_dimension = aws_appautoscaling_target.ecs.scalable_dimension
  service_namespace  = aws_appautoscaling_target.ecs.service_namespace
  schedule           = var.scale_out_action.schedule
  timezone           = "Asia/Tokyo"

  scalable_target_action {
    min_capacity = var.scale_out_action.min_capacity
    max_capacity = var.scale_out_action.max_capacity
  }
}

resource "aws_appautoscaling_scheduled_action" "scale_in" {
  name               = var.scale_in_action.name
  resource_id        = aws_appautoscaling_target.ecs.resource_id
  scalable_dimension = aws_appautoscaling_target.ecs.scalable_dimension
  service_namespace  = aws_appautoscaling_target.ecs.service_namespace
  schedule           = var.scale_in_action.schedule
  timezone           = "Asia/Tokyo"

  scalable_target_action {
    min_capacity = var.scale_in_action.min_capacity
    max_capacity = var.scale_in_action.max_capacity
  }
}
```

main.tf でパラメータを設定しています。（schedule に任意の日時を設定します。）

```main.tf
// 上記省略

module "autoscaling" {
  source     = "./modules/autoscaling"
  depends_on = [module.ecs]

  cluster_name = module.ecs.cluster_name
  service_name = module.ecs.service_name

  scale_out_action = {
    name         = "${module.ecs.service_name}-scale-out"
    schedule     = "cron(50 8 * * ? *)" # every day at 8:50 JST
    min_capacity = 2
    max_capacity = 2
  }

  scale_in_action = {
    name         = "${module.ecs.service_name}-scale-in"
    schedule     = "cron(10 18 * * ? *)" # every day at 18:10 JST
    min_capacity = 1
    max_capacity = 1
  }

}
```

#サンプルを構築する前の準備
・Docker Desktop のインストール
・AWS CLI のインストール
・Terraform のインストール
・tfenv のインストール
・[Terraform を実行するための IAM のアクセスキーとシークレットキー](https://aws.amazon.com/jp/premiumsupport/knowledge-center/create-access-key/)

#サンプルの動作確認環境
Terraform

```
$ terraform -version
Terraform v1.0.4
on darwin_amd64
+ provider registry.terraform.io/hashicorp/aws v3.54.0
+ provider registry.terraform.io/hashicorp/null v3.1.0
+ provider registry.terraform.io/hashicorp/template v2.2.0
```

AWS CLI

```
$ aws --version
aws-cli/1.19.92 Python/3.7.4 Darwin/18.7.0 botocore/1.20.92
```

docker

```
docker --version
Docker version 20.10.7, build f0df350
```

#サンプルを構築する手順
本手順は、Mac環境で実施することを想定しています。
Terraformのコードをダウンロードします。

```
git clone https://github.com/okubo-t/aws-tf-fargate-autoscaling-sched.git
```

Terraform のコードがあるディレクトへ移動します。

```
$ cd aws-tf-fargate-autoscaling-sched
```

terraform.tfvars ファイルを作成します。

```
cp -p terraform.tfvars- terraform.tfvars
```

作成した terraform.tfvars 内の各パラメータを環境に応じて、任意で変更します。

```terraform.tfvars
# リージョン
aws_region = "ap-northeast-1"

# リソース名のプレフィックス
prefix = "fargat-as-sched"

# リソースの環境
env = "demo"

# ALBに HTTPアクセスする時の送信元 IPアドレス
my_remote_ip = "0.0.0.0/0"
```

続いて、コマンドを実行していきます。

```
$ export AWS_ACCESS_KEY_ID=""
$ export AWS_SECRET_ACCESS_KEY=""
$ export AWS_DEFAULT_REGION=ap-northeast-1
```

```
$ terraform init
```

```
$ terraform plan
```

```
$ terraform apply

Apply complete! Resources: 30 added, 0 changed, 0 destroyed.

Outputs:

alb_dns_name = "fargat-as-sched-demo-alb-1234567890.ap-northeast-1.elb.amazonaws.com"
ecr_repository = "1234567890.dkr.ecr.ap-northeast-1.amazonaws.com/fargat-as-sched-demo-app"
scale_in_action_name = "fargat-as-sched-demo-svc-scale-in"
scale_out_action_name = "fargat-as-sched-demo-svc-scale-out"
```
これで、Terraformによる環境の構築は完了です。
デプロイ後に出力される Outputsの内容をメモします。

メモした alb_dns_name の値 にHTTPで動作確認します。(コンテナの起動に少し時間がかかります。)

```
$ curl -I http://fargat-as-sched-demo-alb-1234567890.ap-northeast-1.elb.amazonaws.com

HTTP/1.1 200 OK
Date: Sat, 14 Aug 2021 05:40:08 GMT
Content-Type: text/html
Content-Length: 612
Connection: keep-alive
Server: nginx/1.21.1
Last-Modified: Tue, 06 Jul 2021 15:21:03 GMT
ETag: "60e474df-264"
Accept-Ranges: bytes
```

下記コマンドで、日付と時刻に基づいてスケールイン/アウトするスケジュールが登録されていることを確認します。

```
$ aws application-autoscaling describe-scheduled-actions \
    --service-namespace ecs \
    --scheduled-action-names fargat-as-sched-demo-svc-scale-in fargat-as-sched-demo-svc-scale-out
```

```json
{
    "ScheduledActions": [
        {
            "ScheduledActionName": "fargat-as-sched-demo-svc-scale-in",
            "ScheduledActionARN": "arn:aws:autoscaling:ap-northeast-1:1234567890:scheduledAction:*********-**********-***********:resource/ecs/service/fargat-as-sched-demo-cluster/fargat-as-sched-demo-svc:scheduledActionName/fargat-as-sched-demo-svc-scale-in",
            "ServiceNamespace": "ecs",
            "Schedule": "cron(10 18 * * ? *)",
            "Timezone": "Asia/Tokyo",
            "ResourceId": "service/fargat-as-sched-demo-cluster/fargat-as-sched-demo-svc",
            "ScalableDimension": "ecs:service:DesiredCount",
            "ScalableTargetAction": {
                "MinCapacity": 1,
                "MaxCapacity": 1
            },
            "CreationTime": 1628919499.148
        },
        {
            "ScheduledActionName": "fargat-as-sched-demo-svc-scale-out",
            "ScheduledActionARN": "arn:aws:autoscaling:ap-northeast-1:1234567890:scheduledAction:*********-**********-***********:resource/ecs/service/fargat-as-sched-demo-cluster/fargat-as-sched-demo-svc:scheduledActionName/fargat-as-sched-demo-svc-scale-out",
            "ServiceNamespace": "ecs",
            "Schedule": "cron(50 8 * * ? *)",
            "Timezone": "Asia/Tokyo",
            "ResourceId": "service/fargat-as-sched-demo-cluster/fargat-as-sched-demo-svc",
            "ScalableDimension": "ecs:service:DesiredCount",
            "ScalableTargetAction": {
                "MinCapacity": 2,
                "MaxCapacity": 2
            },
            "CreationTime": 1628919499.144
        }
    ]
}
```

スケジュールした日時で、サービスがスケールイン/アウトすることを確認します。

#後片付け
下記コマンドで、Terraform で作成したAWS環境を削除します。

```
$ terraform destroy
```

#さいごに
Terraform でFargateのサービスにスケジュール設定をする際の参考になれば幸いです。