[AWS]ECS+EventBridgeで最新リビジョンでタスクを実行できるようにするためのTerraformの設定
Opt Fitエンジニアの@KAZYPinkSaurusです。
先日自分用のメモに書いた記事がそれなりに見てもらえて喜んでいるこの頃です。
やりたいこと
今回はAWS ECS+EventBridgeで常に最新リビジョンでタスクを実行できるようにするためのTerraformの設定について書き記します。
背景
これまでAWS Lambdaを用いてバッチ処理しているシステムがありました。
サービスの急成長に伴いバッチの実行時間がLambdaの最大実行時間である15分に近づいてきました。
そこでバッチ処理をECSに移行することにしました[1]。
アーキテクチャ
ECSのタスクをEventBridgeから定期実行してもらいます。
実行環境
❯❯❯❯ terraform -v
Terraform v1.1.8
on darwin_amd64
+ provider registry.terraform.io/hashicorp/aws v4.25.0
マシンはM1のMacです。
実装
イベントのルールはaws_cloudwatch_event_ruleリソースを使って作成します。
今回は毎晩深夜2時に定期実行するようにcron記法で設定しました。
タイムゾーンはUTS+0なのでJSTでは9時間前にする必要があります。
スケジュールされたイベントはすべて UTC+0 のタイムゾーンを使用
スケジュールに従って実行する Amazon EventBridge ルールの作成
resource "aws_cloudwatch_event_rule" "this" {
name = "everyday2am"
schedule_expression = "cron(0 17 * * ? *)" # JST 2AM
}
次に定期実行する対象の設定をしていきます。
aws_cloudwatch_event_targetリソースで作成します。
TerraformでECSのタスク定義を作成するとついついそのリソースのARNを指定してしまいがちなのですがそれでは最新のリビジョンが常に実行されません。
なぜならタスク定義はリソースごとにリビジョンを持つ
からです。
これがポイントです。
例えばsample_defというタスク定義であれば
arn:aws:ecs:<region>:<account id>:task-definition/sample_def
# ecs_task_definitionのARNを直接渡すと以下のようになってしまう。
# arn:aws:ecs:<region>:<account id>:task-definition/sample_def:1
を渡します。
assign_public_ipをTrueとする場合はECSの起動タイプの方はタスク定義で指定していたとしてもlaunch_typeをFARGATEと指定しましょう[2]。
resource "aws_cloudwatch_event_target" "this" {
rule = aws_cloudwatch_event_rule.this.name # 先程作ったルール
arn = var.aws_ecs_cluster_arn
role_arn = aws_iam_role.eventbridge.arn # 下で作成しているIAM Role
ecs_target {
# NOTE: リビジョンなしのARNを指定することで常に最新になる
task_definition_arn = replace(aws_ecs_task_definition.this.arn, "/:\\d+$/", "")
launch_type = "FARGATE"
network_configuration {
subnets = var.subnet_ids
security_groups = var.security_groups
assign_public_ip = true # 必ずtrueにする必要はない
}
}
}
タスクのリビジョンが常に最新になっていることがコンソール上で確認できました。
IAM Role
EventBridgeがECSタスクを実行を実行できるうよなIAM Roleを作っていきます。
権限の制限を間違えるとイベントブリッジが失敗してしまうのでご注意ください。
resource "aws_iam_role" "eventbridge" {
name = "eventbridge-role"
assume_role_policy = data.aws_iam_policy_document.assume_role.json
inline_policy {
name = "ecs-task-execution"
policy = data.aws_iam_policy_document.inline_policy.json
}
}
data "aws_iam_policy_document" "inline_policy" {
statement {
actions = ["ecs:RunTask"]
# 実行できるタスク定義を制限
resources = [replace(aws_ecs_task_definition.this.arn, "/:\\d+$/", "")]
condition {
test = "ArnLike"
variable = "ecs:cluster"
# タスクを実行するクラスターを制限
values = [var.aws_ecs_cluster_arn]
}
}
statement {
actions = ["iam:PassRole"]
# PassRoleできるIAM Roleを制限
resources = [
var.task_execution_role, # ECSタスクの実行ロール
var.task_role, # ECSのタスクロール
]
condition {
test = "StringLike"
variable = "iam:PassedToService"
# PassRoleする先をecsタスクに制限
values = ["ecs-tasks.amazonaws.com"]
}
}
}
data "aws_iam_policy_document" "assume_role" {
statement {
principals {
type = "Service"
# eventbridgeがこのIAM Roleを使えるようにする
identifiers = ["events.amazonaws.com"]
}
actions = ["sts:AssumeRole"]
}
}
Amazon EventBridge でのアイデンティティベースのポリシー (IAM ポリシー) の使用
さいごに
タスク定義のARN指定方法をちょっと変えるだけで最新のリビジョンが常に反映されるようになりました。デプロイが楽になった反面、誤ったタスク定義をデプロイしてしまった際は注意が必要です。
次回はタスク失敗時に再実行するための仕組みをテーマにしたいなと思っています。
それではまた。
🔔採用情報
ジム施設向けDXソリューションGYMDXではエンジニアを積極採用中です。
ジュニア層のエンジニアからリーダー職まで幅広く募集しています。
2022年7月にプレシリーズAラウンドにて資金調達を実施しました。
リードエンジニア
バックエンドエンジニア
Discussion