🌟

TerraformとAWS CodeDeployの相性が良くなかった話

2023/12/10に公開

はじめに

こんにちは、最近はAWS化業務をやっています、itohisです。
今回はAWSのリソース運用管理について困ったことについてご紹介します。

伝えたいこと

この記事では、AWSのオートスケーリンググループ(以下、ASG)をTerraformで静的に管理する一方で、デプロイにはAWS CodeDeployを用いたBlue/Greenデプロイを採用した際に生じる相性の問題について解説します。
ですから、当記事を読むに当たってAWS、AWS CodeDeploy、Terraformの前提知識が少し必要になります。

困ったこと

背景

今回は社内のAWS化業務を進めていて、小規模なAPIをオンプレミス環境からAWS環境へ移行することになりました。
全社的にシステムのクラウド化を進めていることもあり、ベースの方針としてAWSリソースはTerraformというIaCツールを用いて管理することが決まっています。

構成

コンピューティングサービスはEC2を採用し、ASGでスケールする形を取りました。
デプロイフローについてはGitHubActionsからCodeDeployをトリガーすることとし、デプロイの自動化を図ります。
CodeDeployのデプロイ方式はAPIのリリースのため、ダウンタイムなしで実行を実現できるBlue/Greenデプロイを採用しました。

発生した問題

CodeDeployによるBlue/Greenデプロイ実施後、terraform applyによるAWSリソースの更新を行うと、今作成されているASGとは別に新規ASGを作成してしまうという事象が発生しました。
Terraformはリソース定義を静的に管理し、期待される状態を保とうとします。
つまり、「既にASGが作成されているはずなのに、なぜ追加で新規作成されてしまうのか?」
という部分が今回のポイントになります。
Terraformはもちろん他のAWSリソースも管理するので、修正内容に関わらず更新タイミングで毎回ASGが余計に作成されてしまっては困ります。

なぜこうなるのか

順番に図を見ながら解説します。
結論として、CodeDeployのBlue/Greenデプロイにより、CodeDeployが「Terraformが初回作成したASG」を削除し、新たなASGを追加します。この入れ替えにより、作成されているASGがTerraformの管理から外れます。
Terraformはリソースの状態を、定義された「期待される状態」に戻そうするため、削除されたASGを毎回新規作成してしまいます。

1.初回のTerraformによるASG新規作成
新規作成です。


2.CodeDeployによるTerraformASGの削除、ASG新規作成
CodeDeployのBlue/Greenデプロイにより、Green環境ASGが新規作成され、Terraformが作ったBlue環境は削除されてしまいます。


3.CodeDeployによる入れ替え
Blue環境からGreen環境へ入れ替えてデプロイ完了です。


4.Terraform更新時に管理するASGが存在しないことを検知し、改めて新規作成
Terraformは自分が作ったASGを消されてしまったので、作り直そうとします。


といった流れです。

根本的な原因は何なのか

管理者 リソース管理方法 実際の動作
Terraform 静的 ASGを定義通りに更新、なければ新規作成
CodeDeploy 動的 新しいASGを新規作成、古いASGを削除し入れ替える

表にまとめた通り、両者のAWSリソースの管理方法が根本的に異なることが原因です。
言い換えると、「相性が悪い」ということになります。
この問題が発生するのは、基礎的な理解が浅いまま組み合わせてしまったことによる開発者側の原因と言えそうです。

解決方法①

暫定的ですが現実的な解決方法です。
管理手法の衝突は免れないので、どちらかの方針に寄せます。
今回はTerraformが差分を検知しないようにすることを目指します。
Terraformにはignoreを定義することで差分検知回避が可能ですが、Terraform上のCodeDeployの定義内容にASGの内容を含むため、単純なコメントアウトやignoreでは構文エラーとなり、対応することができませんでした。
そのため、下記の方法で多少強引に解決します。

  • 初回terraform apply後、Terraformコードでcount変数を利用し0に設定する
resource "aws_autoscaling_group" "autoscaling" {
  count = local.executed_codedeploy ? 0 : 1
}

count変数は定義されたAWSリソースをいくつ作成するか指定することができます。
CodeDeployによるデプロイ実行後に上記設定とローカル変数を設定することで、Terraform目線でASGが0個の状態が正であると指定することができます。
これによりCodeDeployでASGが動的に変更されてもTerraformは差分を検知しなくなります。

解決方法②

  • 別の方法で実現する。

今回はある程度リソース定義を作ってしまった後にこの問題が発覚したことや、開発スケジュールの兼合いもあり、後戻りできないため①の方法で解決しました。
しかし「APIをダウンタイムなしでデプロイして自動化もする」という目的のためにこの手法を採用しただけなので、時間やコストが許す限り別の方法を検討しても良いはずです。
最終的な振り返りとしてEC2ではなくECSであればこの問題は回避することができていました。

おわりに

Terraform・CodeDeployのBlue/Greenデプロイ、両者の特徴を理解していたら当然とも思えるような内容だったかもしれません。しかし、AWSには豊富な選択肢があり、現場では利用サービスの選定や組み合わせからこのような問題で苦労している場合もあるかもしれません(いや絶対にある...)。
私も作って動かしてみるまでこの問題を把握することができませんでした。
チーム内でも「APIの規模が極小なのでEC2の方が見合っているのでは?」という直感的な判断を元にEC2を採用したこともあり、事前調査では洗い出しきれなかった部分でした。

また、

  • 「Terraform利用でEC2をダウンタイムなしでデプロイする」

という方法に関しては未だに綺麗に解決していません。
本記事では暫定策になりましたが、より良い解決策があれば是非ご教示ください。

AWSはわからないことだらけで私も日々情報を集めながら開発をしています。
この記事も、もし同じ課題に当たった人の助けになればと思います🙏
長文になってしまいましたが、ありがとうございました!

よいクリスマスを👋🎅

株式会社アイスタイル

Discussion