[アップデート] aws_ecs_task_definition に CI/CD との競合を防ぐ track_latest 引数がリリースされました
こんにちは! AWS 事業本部コンサルティング部のたかくに(@takakuni_)です。
aws_ecs_task_definition で CI/CD との競合を防ぐために、 track_latest
引数がリリースされました。
今まで
Terraform で ECS のタスク定義を作成するケースを想定します。(側だけ作ってタスク定義の中身はアプリ側で管理するイメージ)
以下のように、 CI/CD パイプラインからの更新を上書きしないよう、変更を検知しない表現をよくするのではないでしょうか。
resource "aws_ecs_task_definition" "main" {
family = "track-latest"
requires_compatibilities = ["FARGATE"]
cpu = "256"
memory = "512"
network_mode = "awsvpc"
execution_role_arn = aws_iam_role.task_exec.arn
runtime_platform {
cpu_architecture = "X86_64"
operating_system_family = "LINUX"
}
container_definitions = <<TASK_DEFINITION
[
{
"name": "nginx",
"image": "XXXXXXXXXXXX.dkr.ecr.ap-northeast-1.amazonaws.com/track-latest:1.0",
"portMappings": [
{
"name": "nginx-80-tcp",
"protocol": "tcp",
"containerPort": 80,
"hostPort": 80
}
]
}
]
TASK_DEFINITION
lifecycle {
ignore_changes = all # または ignore_changes = [container_definitions]
}
}
何のためにやっているのか
この表現を行わない場合、何が起こるのかをおさらいしましょう。
まず、コンテナイメージの CI/CD パイプラインから、新しいイメージをデプロイするべく、以下のタスク定義の部分が少なからず更新されていきます。
resource "aws_ecs_task_definition" "main" {
family = "track-latest"
requires_compatibilities = ["FARGATE"]
cpu = "256"
memory = "512"
network_mode = "awsvpc"
execution_role_arn = aws_iam_role.task_exec.arn
runtime_platform {
cpu_architecture = "X86_64"
operating_system_family = "LINUX"
}
container_definitions = <<TASK_DEFINITION
[
{
"name": "nginx",
"image": "XXXXXXXXXXXX.dkr.ecr.ap-northeast-1.amazonaws.com/track-latest:1.0",
"portMappings": [
{
"name": "nginx-80-tcp",
"protocol": "tcp",
"containerPort": 80,
"hostPort": 80
}
]
}
]
TASK_DEFINITION
}
CI/CD から上記の変更が行われた後に terraform apply
を打つと、次のようにコードとリソースの変更を検知し、コンテナイメージのバージョンが v1.1 から、 v1.0 に巻き戻ろうとしてしまいます。
takakuni@taskdef-update % terraform apply
aws_ecr_repository.main: Refreshing state... [id=track-latest]
aws_ecs_cluster.main: Refreshing state... [id=arn:aws:ecs:ap-northeast-1:XXXXXXXXXXXX:cluster/track-latest]
aws_vpc.main: Refreshing state... [id=vpc-03f80213655df4c60]
aws_iam_role.task_exec: Refreshing state... [id=track-latest-task-exec]
aws_subnet.public_c: Refreshing state... [id=subnet-0d95295b1edb19a41]
aws_internet_gateway.main: Refreshing state... [id=igw-0124e471c35dd03f7]
aws_subnet.public_a: Refreshing state... [id=subnet-097529c6bf6e81dd5]
aws_security_group.main: Refreshing state... [id=sg-0acabaf720ab6f8c6]
aws_route_table.main: Refreshing state... [id=rtb-070f4239a361d3be2]
aws_vpc_security_group_egress_rule.main_allow_all_traffic_ipv4: Refreshing state... [id=sgr-04f28007c1fbd257b]
aws_route.main: Refreshing state... [id=r-rtb-070f4239a361d3be21080289494]
aws_route_table_association.public_c: Refreshing state... [id=rtbassoc-09fec389983334f70]
aws_route_table_association.public_a: Refreshing state... [id=rtbassoc-0eab83b46dcfdc306]
aws_ecs_task_definition.main: Refreshing state... [id=track-latest]
aws_ecs_service.main: Refreshing state... [id=arn:aws:ecs:ap-northeast-1:XXXXXXXXXXXX:service/track-latest/track-latest]
Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
-/+ destroy and then create replacement
Terraform will perform the following actions:
# aws_ecs_task_definition.main must be replaced
-/+ resource "aws_ecs_task_definition" "main" {
~ arn = "arn:aws:ecs:ap-northeast-1:XXXXXXXXXXXX:task-definition/track-latest:9" -> (known after apply)
~ arn_without_revision = "arn:aws:ecs:ap-northeast-1:XXXXXXXXXXXX:task-definition/track-latest" -> (known after apply)
~ container_definitions = jsonencode(
~ [
~ {
- cpu = 0
- environment = []
- essential = true
~ image = "XXXXXXXXXXXX.dkr.ecr.ap-northeast-1.amazonaws.com/track-latest:1.1" -> "XXXXXXXXXXXX.dkr.ecr.ap-northeast-1.amazonaws.com/track-latest:1.0"
- mountPoints = []
name = "nginx"
- volumesFrom = []
# (1 unchanged attribute hidden)
},
] # forces replacement
)
~ id = "track-latest" -> (known after apply)
~ revision = 9 -> (known after apply)
- tags = {} -> null
~ tags_all = {} -> (known after apply)
# (8 unchanged attributes hidden)
# (1 unchanged block hidden)
}
Plan: 1 to add, 0 to change, 1 to destroy.
Do you want to perform these actions?
Terraform will perform the actions described above.
Only 'yes' will be accepted to approve.
Enter a value:
ECS サービス側でタスク定義リビジョンの更新が行われなければ、イメージバージョンが巻き戻ったタスク定義が使われることはないですが、あまりいい状態ではないですよね。
また、 Terraform の仕様上、更新前のタスク定義リビジョンに関しては「非アクティブ」な状態に変更するため、見栄えもあまり良くないです。
この辺りの issue は以下の URL でディスカッションされているので興味がある方はご覧ください。
この事象を解決するためのワークアラウンドとして、先ほどのような ignore_changes
によって、引数の変更検知を無視する表現が必要でした。
今から
AWS Provider v5.37.0 のアップデートで、新たに track_latest
引数が増えました。
terraform-provider-aws/CHANGELOG.md at main · hashicorp/terraform-provider-aws · GitHub
この引数は、 aws_ecs_task_definition
の container_definitions
を追跡するかどうかを指定する引数です。デフォルトは false になります。
track_latest
が false の場合、 tfstate へ container_definitions
の追跡及び、最新状態の反映を行わない挙動になります。
aws_ecs_task_definition | Resources | hashicorp/aws | Terraform | Terraform Registry
ignore_changes と何が違うのか?
ignore_changes
は、指定した引数の tfstate の値と、コードの差分を無視するかどうかを決めるものです。したがって、コードの差分がどうであれ、基本的には tfstate には最新のリソースの設定値が反映されるような挙動をとります。
- コードと tfstate で差分があった場合
- 指定した引数は apply 時に ignore される
- 指定していない場合は apply 時にコードの値が反映される
- tfstate の状況
- 最新状況が反映される
詳しくは ちゃだいん さんがまとめてくれた、以下の記事をご覧ください。
[Terraform] 誤解されがちなignore_changesの動き・機密情報はstateに保持されるのか? | DevelopersIO
今回の track_latest
引数は、 tfstate 側の反映を行うかどうかの引数であるため、コードと最新状況を追跡しなくなった時点の tfstate に差分があった場合は、変更が検知されます。
track_latest が false の場合
- コードと tfstate で差分があった場合
- 変更検知が発生し、 apply 時にコードの値が反映される
- tfstate の状況
aws_ecs_task_definition
のcontainer_definitions
引数の最新状況が反映されなくなる
ちょっと小難しい話をしましたが、以下のような対応で OK だと個人的には思います。
新規で作る場合
- 特に要件なければ track_latest はデフォルトの false で OK
- ここでいう要件とは、 ECS タスク及びタスク定義のデプロイは Terraform 側で完全に管理したいパターン
- その他、変更を検知したくない(Git 側に寄せたい)項目は、 ignore_changes を反映させる
- ほぼほぼ意味はないけど container_definitions に ignore_changes していてもお守りにはなりそう
既存から更新する場合
- 特に要件なければ track_latest はデフォルトの false で OK
- ここでいう要件とは、 ECS タスク及びタスク定義のデプロイは Terraform 側で完全に管理したいパターン
- アップデート時にコードと実物の値を合わせてあげる
- その他、変更を検知したくない(Git 側に寄せたい)項目は、 ignore_changes を反映させる
- ほぼほぼ意味はないけど container_definitions に ignore_changes していてもお守りにはなりそう
まとめ
以上、「CI/CD との競合を防ぐ track_latest 引数がリリースされました」でした。 ECS 使いの方々には、嬉しいアップデートなのではないでしょうか。
なお、 ECS タスクのデプロイを CI/CD で管理したい場合は、 aws_ecs_service の task_definition
および desired_count
あたりは引き続き、 ignore_changes に指定する必要があるためご注意を。
この記事がどなたかの参考になれば幸いです。 AWS 事業本部コンサルティング部のたかくに(@takakuni_)でした!