こんにちは、インフラストラクチャーグループのyjszkです。2月から入社しました。
リブセンスにはバッチをECSとEventBridge Ruleで動かしている実装があります。EventBridge Ruleがなかなかの曲者で、UTCでしか時間を指定できません。
UTCで指定されたルールはいつ動くのかがわかりづらいですし、JSTでは1つのルールで済んだものがUTCでは2つ以上に分割されてしまうこともあります。
例えば、JSTで特定の曜日に10分ごとに実行するタスクがあるとします。
*/10 * * * 0-3,5-6 *
これがUTCだと3つになります。これはなかなかつらいです。
0/10 0-15 ? * 4 * 0/10 15-0 ? * 5 * 0/10 * ? * 1-3,6,7 *
一方、2022年11月にリリースされたAWSの新機能EventBridge Schedulerではタイムゾーン指定ができます。つまりJST指定が可能です!
また、あらゆるAPIをキックできECSの発火にも使えるので、こちらに移行することにしました。
変更点
現
新
以前はEventBridge RuleでECSをキックしていました。
これらをコードで管理しており、ルールの管理についてはecscheduleを採用していました。こちらはYAMLで設定を管理でき、既存のルールのエクスポート、差分出力、変更、実行ができる素晴らしいツールです。
しかし、ルールの削除に関して手動のオペレーションが必要となり、完全にコード化というわけには行きませんでした。 今回はこちらをTerraformにして完全にコード化します。
実装例
想定環境
VPC、サブネット、クラスター、タスク定義はあらかじめ作成されていることとします。
コード
Terraform
- Schedulerが使用するIAMロール
resource "aws_iam_role" "event_scheduler" { name = "event-scheduler" assume_role_policy = data.aws_iam_policy_document.sts_for_eventbridge_scheduler.json tags = { Name = "event-scheduler" } }
- IAMロールのassume_role_policy
data "aws_iam_policy_document" "sts_for_eventbridge_scheduler" { statement { effect = "Allow" actions = ["sts:AssumeRole"] principals { type = "Service" identifiers = ["scheduler.amazonaws.com"] } } }
- 管理ポリシーの定義とロールへのアタッチ
data "aws_iam_policy" "ecs_events_role" { arn = "arn:aws:iam::aws:policy/service-role/AmazonEC2ContainerServiceEventsRole" } data "aws_iam_policy" "ecs_scheduler_role" { arn = "arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy" } resource "aws_iam_role_policy_attachment" "event_scheduler_run_ecs" { role = aws_iam_role.event_scheduler.name policy_arn = data.aws_iam_policy.ecs_events_role.arn } resource "aws_iam_role_policy_attachment" "event_scheduler_get_ecr" { role = aws_iam_role.event_scheduler.name policy_arn = data.aws_iam_policy.ecs_scheduler_role.arn }
- 今回タスク定義はすでに存在している想定のためdataで取り込む
data "aws_ecs_task_definition" "batch_task_definition" { task_definition = "batch-task" }
- Scheduler本体
resource "aws_scheduler_schedule" "batch_rules" { # 珍妙な書き方だがfor_eachはmap型しか受け取れないのでそのための処理 # https://qiita.com/minamijoyo/items/3785cad0283e4eb5a188 for_each = { for i in local.scheduler_rules : i.name => i } name = each.value.name state = var.scheduler_state schedule_expression = each.value.schedule_expression schedule_expression_timezone = "Asia/Tokyo" flexible_time_window { mode = "OFF" } target { arn = aws_ecs_cluster.main.arn role_arn = aws_iam_role.event_scheduler.arn ecs_parameters { task_definition_arn = data.aws_ecs_task_definition.batch_task_definition.arn launch_type = "FARGATE" platform_version = "LATEST" enable_ecs_managed_tags = false enable_execute_command = false network_configuration { subnets = module.main_vpc.private_subnet_ids # 適宜自分の環境のサブネット類と置き換えてください security_groups = [aws_security_group.hoge.id] # 適宜自分の環境のセキュリティグループ類と置き換えてください } } input = jsonencode({ "containerOverrides" : [{ "name" : data.aws_ecs_task_definition.batch_task_definition.family, "command" : each.value.command }] }) } }
- 可変の値はYAMLに記載してForEach+Mapで回すことにより可読性をあげている
locals { scheduler_rules = yamldecode(file("${path.module}/scheduler.yaml")) }
Terraformに食わせるYAML
- Terraformに素でコードを書くと、JSONになって読みにくいので、可変の値についてはYAMLで記載をしています。
- name: task1 schedule_expression: cron(5 10 ? * SUN-WED,FRI-SAT *) command: - sh - /home/hoge/foo.sh - name: task2 schedule_expression: cron(10 11 ? * THU *) command: - sh - /home/bar/foo.sh
yamldecodeについて
yamldecode(file("${path.module}/scheduler.yaml"))
についてですが、terraform consoleで動きを確認することができます。
上記のYAMLがどうTerraformに取り込まれるか簡単に確認することができます。
$ terraform console > yamldecode(file("test.yaml")) [ { "command" = [ "sh", "/home/hoge/foo.sh", ] "name" = "task1" "schedule_expression" = "cron(5 10 ? * SUN-WED,FRI-SAT *)" }, { "command" = [ "sh", "/home/bar/foo.sh", ] "name" = "task2" "schedule_expression" = "cron(10 11 ? * THU *)" }, ] >
このようにデコード結果を確認することができます。便利ですね。
やってみて
リリースされたばかりのサービスということもあり、サンプルコードが日本語英語問わずほぼありませんでした。この場合は公式ドキュメントしか勝たんというのがあります。なので、日本語の情報を増やすために書きました、参考になれば幸いです。
また、入社したばかりかつリモートワークなのでビクビクしながらのコーディングでしたが、Slackで1を聞けば100くらい返ってくることもある優秀なエンジニアが揃っている会社なので、ガンガン聞いて非常に多くのことを吸収しました。
ぜひ皆さんも一緒に働きましょう!!!