ECS タスクスケジューリングを Terraform を使って構築してみます。
Laravel の artisan コマンドを定期的に実行させてみます。
ECS タスクスケジューリングは、 AWS コンソールの以下の画面から作成できます。
ECS タスクスケジューリング = CloudWatch Events
AWS ドキュメント » Amazon ECS » AWS Fargate 用ユーザーガイド » Amazon ECS タスクのスケジューリング » タスクのスケジューリング (cron)
CloudWatch Events で ECS ターゲットを起動することを、 ECS タスクスケジューリングと呼んでいるようです。
コンソールから作成すると画面のところどころに CloudWatch Events という言葉が見られます。
実際、 ECS コンソールからタスクスケジューリングを作成すると、 CloudWatch Events にも同じものが出てきます。
Terraform からは aws_cloudwatch_event_rule/aws_cloudwatch_event_target で構築
Terraform で ECS タスクスケジューリングを構築する際は CloudWatch Event Rule, Event Target で ECS タスクスケジューリングを作ります。
Resource: aws_cloudwatch_event_rule
Resource: aws_cloudwatch_event_target
Terraform の AWS 系リソースのドキュメントで ECS 配下を探してもタスクスケジューリングはありませんでした。
サンプルコード
ECS タスクスケジューリングを動かせるサンプル一式を用意しました。
ikasam/terraform-ecs-task-scheduler-with-laravel
Terraform v0.12.15 で動作確認。
$ terraform version
Terraform v0.12.15
+ provider.aws v2.40.0
+ provider.template v2.1.2
VPC や ECR, ECS タスク定義など、必要なものも一式作成してるので、まっさらな環境でも動くと思います。1
既存環境で試す場合は VPC の CIDR 被りやリソース名被りがあった箇所を変更したり、あるいは既存のリソースを利用するよう、置き換えてください。
あるいは、この中から必要なものを抜き取って、それぞれのプロジェクトに適用してみてください。
以下では、 ECS タスクスケジューリングに関わるところだけを紹介します。
aws_cloudwatch_event_rule
resource "aws_cloudwatch_event_rule" "inspire_every_minutes" {
description = "run php artisan inspire every minutes"
is_enabled = true
name = "inspire_every_minutes"
schedule_expression = "cron(* * * * ? *)"
}
aws_cloudwatch_event_rule
では、タスクを起動する条件を書きます。
ここでは、 cron 式でのスケジューリングを定義しています。
GMT として解釈されるので、日本時間との時差に注意してください。
たとえば、日本時間で平日の AM 8:00 に起動する場合は以下の式です。
cron(0 23 ? * 1-5 *)
詳しくは AWS のドキュメント を参照ください。
aws_cloudwatch_event_target
data "template_file" "php_artisan_inspire" {
template = file("ecs_container_overrides.json")
vars = {
container_name = "inspire"
command = "inspire"
}
}
resource "aws_cloudwatch_event_target" "inspire" {
rule = aws_cloudwatch_event_rule.inspire_every_minutes.name
arn = aws_ecs_cluster.inspire.arn
target_id = "inspire"
role_arn = aws_iam_role.ecs_events_run_task.arn
input = data.template_file.php_artisan_inspire.rendered
ecs_target {
launch_type = "FARGATE"
task_count = 1
task_definition_arn = replace(aws_ecs_task_definition.inspire.arn, "/:[0-9]+$/", "")
network_configuration {
assign_public_ip = true
security_groups = [aws_vpc.inspire.default_security_group_id]
subnets = [aws_subnet.inspire.id]
}
}
}
Event Rule で起動する ECS タスクを定義しています。
ecs_container_overrides.json
の中身は以下のようになっていて、 Docker コンテナの CMD
を上書きしています。
{
"containerOverrides": [
{
"name": "${container_name}",
"command": [ "php", "artisan", "${command}" ]
}
]
}
template_file
の引数として command = inspire
を渡しているので、
最終的に php artisan inspire
が ECS タスクで実行されます。
今回は試していませんが、うまくやればログの出力先を変えたりといったこともできそうです。
最新のタスク定義の指定の仕方
task_definition_arn = replace(aws_ecs_task_definition.inspire.arn, "/:[0-9]+$/", "")
このように書くことで、タスク定義の ARN を arn:aws:ecs:us-east-2:123456789012:task-definition/inspire
と指定します。この指定方法で、タスクスケジューリングで起動するタスク定義の 最新 リビジョンを利用してくれます。バッチの更新によるデプロイでタスク定義が更新された場合も、タスクスケジューリングを更新しなくて良いので楽です。
ECS コンソールからタスクスケジューリングを作成するとリビジョン固定となる
つまり、タスク定義の新しいリビジョンが作成されてもタスクスケジューリングは追従してくれません。
バッチだけ古いソースで動き続けるということです。怖い。
コンソールでは、 CloudWatch Events から最新のタスク定義を設定できる
コンソールからタスクスケジューリングの ECS タスク定義に最新を設定する場合は CloudWatch Events から設定しましょう。ただし、 CloudWatch Events からはコンテナコマンドの上書きはできません。
CloudWatch Events IAM ロール
data "aws_iam_policy_document" "events_assume_role" {
statement {
sid = "CloudWatchEvents"
effect = "Allow"
actions = ["sts:AssumeRole"]
principals {
identifiers = ["events.amazonaws.com"]
type = "Service"
}
}
}
resource "aws_iam_role" "ecs_events_run_task" {
name = "ECSEventsRunTask"
assume_role_policy = data.aws_iam_policy_document.events_assume_role.json
}
resource "aws_iam_role_policy_attachment" "ecs_events_run_task" {
role = aws_iam_role.ecs_events_run_task.name
policy_arn = "arn:aws:iam::aws:policy/service-role/AmazonEC2ContainerServiceEventsRole"
}
CloudWatch Events から ECS タスクを起動するためのロールです。
ECS タスクスケジューリングをコンソールからポチポチ作成する際、ロールを新規作成すると上記と同等のものができます。
aws_cloudwatch_event_target
リソースの role_arn
に、ここで作成したロールの ARN が入ります。
このロールの権限を適切に設定しないと、 Event Rule が起動しても ECS タスクを起動できずに終了してしまいます。
動作確認
サンプルでは、毎分ごとに php artisan inspire
を実行する ECS タスクスケジューリングを構築しました。
php artisan inspire
は、実行すると 1 行の格言を表示するだけのコマンドです。
約 1 分ごとに実行されていることがわかりますね。2
終わったら後片付けを忘れずに。ECS タスクが起動しているとクラスターの削除に失敗して、下手するとデッドロックがかかるので、必ずタスクスケジューリングを DISABLE
にしてから destroy
しましょう。
$ terraform destroy
まとめ
- Terraform で ECS タスクスケジューリングを構築する場合は、 CloudWatch Events として構築すること
- タスクスケジューリングでタスク定義の最新を利用したい場合はリビジョン番号を指定しない
- コンソールから作成する際は、 CloudWatch Events の画面から作成する
- IAM ロールの権限大事
- 1 日 1 回、感謝の
php artisan inspire
参考
- AWS ドキュメント » Amazon ECS » AWS Fargate 用ユーザーガイド » Amazon ECS タスクのスケジューリング » タスクのスケジューリング (cron)
- AWS Documentation » Amazon CloudWatch » User Guide » Schedule Expressions for Rules
- Terraform: Resource: aws_cloudwatch_event_rule
- Terraform: Resource: aws_cloudwatch_event_target