背景
CodeBuildで作成したバッチ1つと、ECS Taskで作成したバッチ2つを毎朝5:00に実行し、それら3つのバッチが終了した後に、CodeBuildで作成した別のバッチ1つを実行する必要があった。
この一連の処理を適切に制御するために、AWS Step Functionsを使用することにした。
実装概要
1. EventBridgeでStep Functionsを起動
- EventBridgeのルールを設定し、ターゲットとしてStep Functionsを指定。
- 毎朝5:00にStep Functionsが起動するように設定。
2. Step Functionsでの処理フロー
- CodeBuildバッチ1つとECS Taskバッチ2つを同時に実行。
- 各バッチの実行結果を監視し、エラーが発生した場合はキャッチする。
- 3つのバッチが全て終了した後、エラーの有無を確認。
- エラーが発生している場合、処理を終了。
- エラーが発生していない場合、CodeBuildのバッチを実行。
3. Terraformによる構築
EventBridgeのルール作成
resource "aws_cloudwatch_event_rule" "step_function_trigger" {
name = "step-function-trigger"
schedule_expression = "cron(0 5 * * ? *)"
}
resource "aws_cloudwatch_event_target" "step_function" {
rule = aws_cloudwatch_event_rule.step_function_trigger.name
arn = aws_sfn_state_machine.batch_workflow.arn
role_arn = aws_iam_role.eventbridge_role.arn
}
Step Functionsの定義
resource "aws_sfn_state_machine" "batch_workflow" {
name = "batch-workflow"
role_arn = aws_iam_role.step_function_role.arn
definition = jsonencode({
"Comment": "Batch processing workflow",
"StartAt": "StartBatchJobs",
"States": {
"StartBatchJobs": {
"Type": "Parallel",
"Branches": [
{
"StartAt": "CodeBuildJob1",
"States": {
"CodeBuildJob1": {
"Type": "Task",
"Resource": "arn:aws:states:::codebuild:startBuild.sync",
"Parameters": {
"ProjectName": "batch-job"
},
"Catch": [{
"ErrorEquals": ["States.ALL"],
"Next": "FailState"
}],
"End": true
}
}
},
{
"StartAt": "ECSTask1",
"States": {
"ECSTask1": {
"Type": "Task",
"Resource": "arn:aws:states:::ecs:runTask.sync",
"Parameters": {
"Cluster": "my-ecs-cluster",
"TaskDefinition": "ecs-task-1"
},
"Catch": [{
"ErrorEquals": ["States.ALL"],
"Next": "FailState"
}],
"End": true
}
}
},
{
"StartAt": "ECSTask2",
"States": {
"ECSTask2": {
"Type": "Task",
"Resource": "arn:aws:states:::ecs:runTask.sync",
"Parameters": {
"Cluster": "my-ecs-cluster",
"TaskDefinition": "ecs-task-2"
},
"Catch": [{
"ErrorEquals": ["States.ALL"],
"Next": "FailState"
}],
"End": true
}
}
}
],
"Next": "CheckErrors"
},
"CheckErrors": {
"Type": "Choice",
"Choices": [
{ "Variable": "$.errors", "BooleanEquals": true, "Next": "FailState" }
],
"Default": "RunFinalCodeBuild"
},
"RunFinalCodeBuild": {
"Type": "Task",
"Resource": "arn:aws:states:::codebuild:startBuild.sync",
"Parameters": {
"ProjectName": "final-batch-job"
},
"End": true
},
"FailState": {
"Type": "Fail"
}
}
})
}
ECSの定義
resource "aws_ecs_task_definition" "ecs_task_1" {
family = "ecs-task-1"
requires_compatibilities = ["FARGATE"]
network_mode = "awsvpc"
cpu = "512"
memory = "1024"
execution_role_arn = aws_iam_role.ecs_execution_role.arn
task_role_arn = aws_iam_role.ecs_task_role.arn
container_definitions = jsonencode([
{
"name": "ecs-task-1",
"image": "my-container-image",
"memory": 1024,
"cpu": 512,
"essential": true
}
])
}
ハマったポイント
IAM PassRole の設定ミス
- 初回の実行時にStep FunctionsからECS Taskの実行が失敗。
- 調査の結果、Step FunctionsがECSを実行するためには
iam:PassRole
の権限が必要であることが判明。 - 特にECSの実行には
iam:PassRole
の設定が必須。 - 必要なIAMロールに
iam:PassRole
を付与することで解決。
まとめ
AWS Step Functionsを使用することで、複数のバッチ処理を制御し、エラーハンドリングを含めたワークフローを構築することができた。
Terraformを活用して構築したため、再現性が高く、管理も容易になった。
IAMの PassRole
権限は忘れがちだが、ECSの実行権限には必須であるため、今後のAWS開発でも注意していきたい。