ECS Scaduled Taskを使用したバッチ処理の実施

## はじめに

ECS Scheduled TaskはAmazon EventBridge ルールを使用し、スケジュールに基づいて、
または EventBridge イベントへの応答として、タスクを実行する機能です。

https://docs.aws.amazon.com/ja_jp/AmazonECS/latest/userguide/scheduled_tasks.html

ECSで動作している環境でECS Scaduled Taskを使ったバッチ処理を導入する機会がありましたので実施する手順を紹介します。

## 前提

- すでにECSで動作している環境
- すでに動作している環境のコンテナにバッチ処理用のスクリプトが配置されている
- terraformで設定

## ディレクトリ構造

terraform versionやバックエンド設定などはconfig.tfに定義。
container-overrides/schedule.jsonはcontainerOverridesの設定を定義してます。
ローカル変数はlocals.tfに定義。
今回説明するECS Scheduled Taskメインの処理はmain.tfに定義してます。
projectに合わせてよしなに分割ください。

```
.
├── config.tf
├── container-overrides
│   └── schedule.json
├── locals.tf
└── main.tf
```

## terrafom設定

使用バージョンは下記の通り。
| Name | Version|
|:--|:--|
| terraform | 1.2.0|
| hashicorp/aws | 4.15.1 |

## terraform

### IAM

ECS Scaduled TaskではEventBridgeが使用されるため信頼されたエンティティにevents.amazonaws.comを設定します。
作成したロールにAmazonEC2ContainerServiceEventsRoleのマネージドポリシーを設定。このポリシーはEventBridgeからECSを使用するためのポリシーです。
```
data "aws_iam_policy_document" "schedule" {
  statement {
    actions = ["sts:AssumeRole"]

    principals {
      type        = "Service"
      identifiers = ["events.amazonaws.com"]
    }
  }
}

resource "aws_iam_role" "schedule" {
  name               = "schedule-${terraform.workspace}"
  assume_role_policy = data.aws_iam_policy_document.schedule.json
}

resource "aws_iam_role_policy_attachment" "schedule" {
  role       = aws_iam_role.schedule.name
  policy_arn = "arn:aws:iam::aws:policy/service-role/AmazonEC2ContainerServiceEventsRole"
}
```

### EventBridge Rule

ECS Scaduled TaskではEventBridgeを使用して定期実行を設定します。
EventBridgeではタイムゾーンがUTCになるのでお気をつけください。
JSTの月初00時00分で使用する場合、月末を表すワイルドカード`L`を使用して`00 15 L * ? *`のような形にする必要があります。

EventBridgeで使用するCron式の詳細についてはこちら

https://docs.aws.amazon.com/ja_jp/AmazonCloudWatch/latest/events/ScheduledEvents.html#CronExpressions

```
resource "aws_cloudwatch_event_rule" "schedule" {
  name                = "schedule-${terraform.workspace}"
  description         = "ECS Scaduled Task"
  schedule_expression = "cron(00 15 L * ? *)"
}
```

### EventBridge Target

EventBridge Targetを使用して、ルールに定義されたイベントパターンにイベントがマッチしたときに、イベントを送信する先のリソース（ECS Task）を定義します。

既存のECSのecs cluster arnの指定が必要です。

inputでECSに渡すjsonを定義します。
ここではoverridesを使用することで、コンテナのデフォルトコマンドを上書きし、入力をタスクに渡します。

overridesの詳細はこちら

https://docs.aws.amazon.com/AmazonECS/latest/APIReference/API_RunTask.html#ECS-RunTask-request-overrides

ecs_targetの項目ではタスクを呼び出すときに使用するパラメータを設定します。
ここでは既存のECSの設定値を渡しております。
task_definition_arnで最新版を指定しておきたい場合、terraformではarn最後に記載されているリビジョンを除外して設定する必要があります。
```
resource "aws_cloudwatch_event_target" "schedule" {
  target_id = "schedule-${terraform.workspace}"
  arn       = local.ecs_cluster_arn
  input     = file("container-overrides/schedule.json")
  rule      = aws_cloudwatch_event_rule.schedule.name
  role_arn  = aws_iam_role.schedule.arn

  ecs_target {
    launch_type         = "FARGATE"
    task_count          = 1
    task_definition_arn = local.ecs_task_definition_arn
    platform_version    = "1.4.0"
    network_configuration {
      subnets          = local.subnets
      security_groups  = local.security_groups
      assign_public_ip = false
    }
  }
}
```

上記inputとして渡しているcontainer-overrides/schedule.jsonには、containerOverrides（タスクの実行時に、タスク定義の一部を上書きする機能）を使って実行コマンドを定義してます。
nameでoverrides対象のコンテナを指定してます。
commandの項は","区切りでコマンドを渡すことができます。
```
{
    "containerOverrides": [
        {
            "name": "app",
            "command": [
                "xxx",
                "yyy"
            ]
        }
    ]
}
```

## おわりに

こちらでECS使ったバッチの定期実行が可能になりました。
containerOverridesを複数用意し別個に登録すれば同じコンテナイメージを複数のジョブで使用できます。


## 参考

こちら参考にさせていただいたサイトです。

https://docs.aws.amazon.com/ja_jp/AmazonECS/latest/userguide/scheduled_tasks.html

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

https://dev.classmethod.jp/articles/terraform-ecs-scheduled-task-latest-md/

https://cha-shu00.hatenablog.com/entry/2022/02/11/221542