📅

ECS Scheduled Taskで定期実行するバッチを作成する

2023/11/03に公開

今回はECS Scheduled Task(スケジュールされたタスク)を使って定期実行するバッチを作成していきます。

AWS公式ページは以下です。

スケジュールされたタスク - Amazon ECS

他のソリューション

ECS Scheduled Taskを使って定期実行するバッチを作成する場合、LambdaやAWS Batchなど他の候補も出てくると思います。

Lambdaは実行時間や、実行リソースに制限があったりします。実行時間は15分までと決まっております。そのため、実行時間が15分以上かかる処理は実行することができません。小規模なバッチ処理に有効です。

AWS Batchは大規模なバッチや複数のバッチを連携して実行させる際に有効な手段です。

バッチ処理をスケーラブルかつ、簡単に実行することができるようです。

公式ドキュメントを見ると、計算科学や金融サービスなどでの本当に大規模なバッチ処理で使用するようです。

今回は「中規模なバッチ処理で、実行時間は15分少し超えるくらい」としたいと思います。

なので、LambdaやAWS Batchは使用せずにECS Scheduled Taskを使ってバッチ処理をしたいと思います。

ECSの作成

ECSを作っていきます。

起動タイプはFargateを使い、cpuやメモリについては今回はサンプルなので一番低い値(cpuが256、memoryが512)とします。

ECRはすでに登録済みで、タスク定義にはリポジトリのURIを指定するだけで良いとします。

IAMロール

まずはIAMロールです。

iam.tf

resource "aws_iam_role" "ecs_role" {
  name               = "EcsTaskExecutionRole-sample"
  assume_role_policy = file("policies/ecs-task-assume-role.json")
}

resource "aws_iam_policy" "ecs_task_execution_policy" {
  name   = "EcsTaskExecutionPolicy-sample"
  policy = file("${path.module}/policies/ecs-task-execution-policy.json")
}

resource "aws_iam_role_policy_attachment" "task_execution_policy_attachment" {
  role       = aws_iam_role.ecs_role.name
  policy_arn = aws_iam_policy.ecs_task_execution_policy.arn
}

policies/ecs-task-assume-role.json

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "",
      "Effect": "Allow",
      "Principal": {
        "Service": "ecs-tasks.amazonaws.com"
      },
      "Action": "sts:AssumeRole"
    }
  ]
}

policies/ecs-task-execution-policy.json

CloudWatchにログを吐き出す権限と、ECRのFullAccessと同等の権限を入れています。

(FullAccessは不要な権限も入れてしまうのでセキュリティ上あまりよくないです。。。)

{
  "Version": "2012-10-17",
  "Statement": [
      {
          "Effect": "Allow",
          "Action": [
              "logs:CreateLogStream",
              "logs:PutLogEvents",

              "ecr:GetAuthorizationToken",
              "ecr:BatchCheckLayerAvailability",
              "ecr:GetDownloadUrlForLayer",
              "ecr:GetRepositoryPolicy",
              "ecr:DescribeRepositories",
              "ecr:ListImages",
              "ecr:DescribeImages",
              "ecr:BatchGetImage",
              "ecr:GetLifecyclePolicy",
              "ecr:GetLifecyclePolicyPreview",
              "ecr:ListTagsForResource",
              "ecr:DescribeImageScanFindings"
          ],
          "Resource": "*"
      }
  ]
}

タスク定義

ここで、実際に実行されるタスクの定義をしていきます。

ここで指定した情報からEventBridgeがタスクを立ち上げて、バッチを実行してくれます。

task_def.tf

resource "aws_ecs_task_definition" "task_def" {
  family                   = "ecs-scheduled-batch-task"
  network_mode             = "awsvpc"
  cpu                      = 256
  memory                   = 512
  requires_compatibilities = ["FARGATE"]
  container_definitions = file("./container_def/container_def.json")
  execution_role_arn = aws_iam_role.ecs_role.arn
  task_role_arn      = aws_iam_role.ecs_role.arn
}

container_def/container_def.json

[
  {
    "name": "<<Task Name>>",
    "image": "<<ECR Repository>>",
    "cpu": 256,
    "memory": 512,
    "logConfiguration": {
      "logDriver": "awslogs",
      "options": {
        "awslogs-group": "/ecs/fargate-task-definition",
        "awslogs-region": "ap-northeast-1",
        "awslogs-stream-prefix": "ecs"
      }
    },
    "essential": true
  }
]

EventBridgeの作成

続いて、EventBridgeの作成をしていきます。

先ほどまでは実際に実行される側の定義をしてきましたが、今度は実行する側の定義をしていきます。

IAMロール

実行する側はどんなことをするか、権限を決めていきます。

基本的にタスクを実行するだけなので、RunTaskのみを権限として指定しています。

iam.tf

最初に作成したiam.tfの続きに書いていきます。

resource "aws_iam_role" "events_role" {
  name               = "EcsEventsRole-sample"
  assume_role_policy = file("./policies/events-assume-role.json")
}

resource "aws_iam_policy" "events_policy" {
  name   = "EcsRunTaskEventsPolicy"
  policy = templatefile("./policies/events-policy.json", { task_definition_arn = aws_ecs_task_definition.task_def.arn })
}

resource "aws_iam_role_policy_attachment" "events_role_attachment" {
  role       = aws_iam_role.events_role.name
  policy_arn = aws_iam_policy.events_policy.arn
}

policies/events-assume-role.json

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "",
      "Effect": "Allow",
      "Principal": {
        "Service": "events.amazonaws.com"
      },
      "Action": "sts:AssumeRole"
    }
  ]
}

policies/events-policy.json

templateの記法を使って、tfファイルからtask_definition_arnという変数を受け取っています。

ここでは先ほど作成したタスク定義のARNを指定しています。

先ほど作成したタスク定義のみをRunTaskで実行できる権限を持っているということになります。

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": "iam:PassRole",
      "Resource": "*"
    },
    {
      "Effect": "Allow",
      "Action": "ecs:RunTask",
      "Resource": "${task_definition_arn}"
    }
  ]
}

EventBridge

events.tf

最後にEventBridgeを作っていきます。

task_definition_arnで先ほど作成したタスク定義のARNを指定します。

また、実際にどこで実行されるのか、ネットワークやセキュリティグループの指定もする必要があります。

また、aws_cloudwatch_event_targetにて、inputでcontainerOverridesを指定しています。

ここで指定したコマンドがタスク起動時に実行されます。

resource "aws_cloudwatch_event_target" "scheduled_batch" {
  target_id = "hello"
  arn       = var.ecs_cluster_arn
  rule      = aws_cloudwatch_event_rule.scheduled_batch.name
  role_arn  = aws_iam_role.events_role.arn

  ecs_target {
    task_count             = 1
    task_definition_arn    = aws_ecs_task_definition.task_def.arn
    launch_type            = "FARGATE"
    platform_version       = "1.4.0"
    enable_execute_command = true
    network_configuration {
      subnets          = "<<Subnet ID>>"
      security_groups  = ["<<>Security Group ID>>"]
      assign_public_ip = true
    }
  }

  input = jsonencode(
    {
      containerOverrides = [
        {
          name    = "hello",
          command = ["echo", "Hello,World!"]
        }
      ]
    }
  )
}

resource "aws_cloudwatch_event_rule" "scheduled_batch" {
  name                = "hello"
  is_enabled          = true
  schedule_expression = "cron(*/30 * * * ? *)"
}

動作確認

実際にAWSに反映させてみます。以下のコマンドで反映させます。

terraform apply

EventBridgeのルールを確認すると、作成したイベントがリストに表示されていることがわかります。

中を確認すると、30分ごとにスケジューリングされていることがわかります。

30分ごとに実行されるのを待ちます。

ログはCloudWatch Logsに出力されるように設定しました。CloudWatch Logsに出力されることを確認してみます。

無事実行されました!

GitHubで編集を提案

Discussion