![[ アップデート ] aws_ecs_task_definition に CI/CD との競合を防ぐ track_latest 引数がリリースされました](https://devio2023-media.developers.io/wp-content/uploads/2019/05/terraform-eyecatch.png)
[ アップデート ] 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_)でした!