この記事から得られる知識
この記事を読むと、以下を "完全に理解" できます✌️
記事のざっくりした内容は、以下のスライドからキャッチアップできちゃいます!
- この記事から得られる知識
- 01. はじめに
- 02. なぜ tfstate ファイルを分割するのか
- 03. tfstate ファイルの分割
- 04. tfstate ファイルに基づくその他の設計
- 05. 状態の依存関係の定義方法
- 06. tfstate ファイルの分割パターン
- 07. 上層の分割 (推奨)
- 08. 下層の分割 (推奨)
- 09. 中間層の分割 (任意)
- 10. おわりに
- 謝辞
- 記事関連のおすすめ書籍
01. はじめに
どうも、Mitchell Hashimoto です。
さて最近の業務で、全プロダクトの技術基盤開発チームに携わっており、チームが使っているTerraform🧑🏻🚀のリポジトリをリプレイスする作業を担当しました。
このリポジトリでは単一のtfstate
ファイルが状態を持ち過ぎている課題を抱えていたため、課題に合った適切な分割パターンでリプレイスしました。
今回は、この時に整理した分割パターン (AWS向け) を記事で解説しました。
もちろん、GoogleCloudやAzureでも読み換えていただければ、同じように適用できます。
知る限りの分割パターンを記載したところ、情報量がエグいことになってしまったため、気になる分割パターンだけ拾って帰っていただけるとハッピーです🙏
それでは、もりもり布教していきます😗
飛ばしていただいても大丈夫ですが、読んでもらえるとより理解が深まるはずです👍
02. なぜ tfstate ファイルを分割するのか
%%{init: { 'theme': "default", 'themeVariables': { 'commitLabelFontSize': '13px' }}}%% gitGraph commit id: "8c8e6" commit id: "0e3c3" branch feature/foo checkout feature/foo commit id: "4e9e8" commit id: "da005" checkout main branch feature/bar commit id: "2d52f" checkout main commit id: "e74d6" branch feature/baz commit id: "f6881"
分割していない場合
そもそも、なぜtfstate
ファイルを分割する必要があるのでしょうか。
tfstate
ファイルを分割しなかったと仮定します。
様々なインフラコンポーネントを単一のtfstate
ファイルで状態を持つ場合、1回のterraform
コマンド全てのコンポーネントの状態を操作できて楽です。
ただし、複数の作業ブランチがある状況だと煩わしいことが起こります。
各作業ブランチでインフラコンポーネントの状態を変更しかけていると、他の作業ブランチから影響を受け、terraform
コマンドでtarget
オプションが必要になってしまいます。
他にも、terraform
コマンドの完了に時間がかかりすぎるといった問題も起こるかもしれません。
単一のtfstate
ファイルで管理するコンポーネントが多くなるほど、これらの問題は顕著になります。
分割している場合
その一方で、tfstate
ファイルをいい感じに分割したと仮定します。
各作業ブランチでは、まるで暗黙的にtarget
オプションがついたように、他の作業ブランチから影響を受けずにterraform
コマンドを実行できます。
よって、各tfstate
ファイルを操作できる管理者は互いに影響を受けずに、terraform
コマンドの結果を得られるようになります。
分割しなくていい場合
運用ルールや開発者人数が理由で作業が衝突せず、target
オプションが必要ない状況であれば、tfstate
ファイルは分割しなくてもよいでしょう。
tfstate
ファイルを分割するメリットが少ないです🙅🏻
03. tfstate ファイルの分割
分割の境界
それでは、tfstate
ファイルの分割の境界はどのようにして見つければよいのでしょうか。
これを見つけるコツは、できるだけ相互に依存しないインフラリソースの関係 に注目することだと考えています。
ここでいう依存とは、tfstate
ファイルが他のtfstate
ファイルの状態を使用することです。
状態をほとんど使用し合わないインフラリソース同士を、異なるtfstate
ファイルで管理します。
異なるtfstate
ファイルで管理できる分割パターンについては後述します。
ソフトウェアアーキテクチャの文脈では、他を使用することを『依存』と表現します。
そのため便宜上、
tfstate
ファイルでも同じ用語で表現することにしました。
@tmknom
さんが述べている通り、Terraformをよりよく設計するためには、『ソフトウェアの基礎知識』が必要です👍
状態の依存関係図
依存関係図とは
分割したtfstate
ファイル間の状態の依存関係を表現した図です。
プロバイダーのアカウントの状態をtfstate
ファイルで管理していることを想像してみてください。
%%{init:{'theme':'default'}}%% flowchart TB subgraph AWSアカウント foo["tfstateファイル"] end
似たものとしてterraform graph
コマンドによるグラフがありますが、これはインフラリソース間の依存関係図です。
tfstate
ファイル間で相互に依存関係があるからといって、個別のインフラリソース間で循環参照が起こってしまうというわけではないです。
続いて、依存関係がある場合と無い場合で、どのような依存関係図になるかを紹介していきます。
依存関係の表現
▼ 依存関係の表現記法
tfstate
ファイル間で状態の依存関係がある場合、これを図で表現すると分割の状況がわかりやすくなります。
『依存』は、--->
(波線矢印) で表現することとします。
設定値の参照数が少ないほどよいです。
依存関係がある場合については、後述します。
▼ 依存関係がない場合
例えば、AWSリソースからなるプロダクトをいくつかのtfstate
ファイル (foo-tfstate
、bar-tfstate
) に分割したと仮定します。
ここで仮定した状況では、
tfstate ファイル間に依存関係はない
とします。
そのため、想定される状態の依存関係図は以下の通りになります。
tfstate
ファイル間に依存関係がない状況がベストです。
--- title: tfstateファイル間に依存関係はない --- %%{init:{'theme':'default'}}%% flowchart TB subgraph AWSアカウント foo["foo-tfstate"] bar["bar-tfstate"] end
▼ 依存関係がある場合
同様に分割したと仮定します。
ここで仮定した状況では、
foo-tfstate ➡︎ bar-tfstate の方向に依存している
とします。
そのため、--->
(波線矢印) を使用して、想定される状態の依存関係図は以下の通りになります。
なお、依存方向は状況によって異なることをご容赦ください。
--- title: foo-tfstateファイルは、bar-tfstateファイルに依存 --- %%{init:{'theme':'default'}}%% flowchart TD subgraph AWSアカウント foo["foo-tfstate"] bar["bar-tfstate"] end foo -. 依存 .-> bar
04. tfstate ファイルに基づくその他の設計
リポジトリ 🐱 の設計
リポジトリ分割
ここまでで、tfstate
ファイル分割について簡単に紹介しました。
リポジトリの分割は、tfstate
ファイル分割に基づいて設計しましょう。
異なるリポジトリにtfstate
ファイルをおいた方がよい場合については、分割パターン で説明しています。
🐱 foo-repository/
├── backend.tf # fooコンポーネントの状態を持つ tfstate ファイルを指定する
...
🐱 bar-repository/
├── backend.tf # barコンポーネントの状態を持つ tfstate ファイルを指定する
...
ディレクトリ 📂 構成
リポジトリ内のディレクトリ構成も、tfstate
ファイル分割に基づいて設計しましょう。
率直に言うと、Terraformのディレクトリ構成のパターンは無数にあります。
そのため、基準なしにディレクトリ構成を考えると何でもあり になってしまいます。
その一方で、tfstate
ファイル分割に基づいて設計することにより、明確なディレクトリ構成パターン として抽出可能になります。
🐱 repository/ ├── 📂 foo/ │ ├── backend.tf # fooコンポーネントの状態を持つ tfstate ファイルを指定する │ ... │ └── 📂 bar/ ├── backend.tf # barコンポーネントの状態を持つ tfstate ファイルを指定する ...
Terraformには、そのリポジトリ内だけでブロック (例:
resource
、data
) のセットを使い回すことを目的とした、ローカルモジュールがあります。
今回、これのディレクトリ構成は設計に含めていません。
混同しやすいのですが、
tfstate
ファイル分割に基づくディレクトリ構成とローカルモジュール内のそれは、全く別のテーマとして切り離して考えることができます👍
リモートバックエンド 🪣 の設計
リモートバックエンド分割
本記事では、リモートバックエンドとしてAWS S3バケットを使用することを想定しています。
リモートバックエンドの分割は、tfstate
ファイル分割に基づいて設計しましょう。
異なるリモートバックエンドにtfstate
ファイルをおいた方がよい場合については、分割パターン で説明しています。
🪣 foo-bucket/
│
└── terraform.tfstate # fooコンポーネントの状態を持つ
🪣 bar-bucket/
│
└── terraform.tfstate # barコンポーネントの状態を持つ
ディレクトリ構成
もし、リモートバックエンドをtfstate
ファイル分割に基づいて分割しなかったとします。
その場合は、代わりにリモートバックエンド内のディレクトリ構成をtfstate
ファイル分割に基づいて設計しましょう。
🪣 bucket/ ├── 📂 foo/ │ └── terraform.tfstate # fooコンポーネントの状態を持つ │ └── 📂 bar/ └── terraform.tfstate # barコンポーネントの状態を持つ
05. 状態の依存関係の定義方法
terraform_remote_stateブロックの場合
terraform_remote_stateブロックによる依存
terraform_remote_state
ブロックには、以下のメリデメがあります。
特性 |
メリット ⭕️ | デメリット × |
---|---|---|
可読性 | - | terraform_remote_state ブロックに加えてoutput ブロックも実装が必要であり、output ブロックは依存先のAWSリソースが一見してわかりにくい。 |
拡張性 | 依存先のAWSリソースに関わらず、同じterraform_remote_state ブロックを使い回せる。 |
- |
保守性 | - | 依存先と依存元の間でTerraformのバージョンに差がありすぎると、tfstate ファイル間で互換性がなくなり、terraform_remote_state ブロックの処理が失敗する。 |
本記事では、 terraform_remote_state ブロックを使用して、状態の依存関係を定義 していきます。
tfstate
ファイルが他のtfstate
ファイルに依存する方法として、後述のAWSリソース別data
ブロックがあります。
状態の依存関係図
例えば、AWSリソースからなるプロダクトをいくつかのtfstate
ファイル (foo-tfstate
、bar-tfstate
) に分割したと仮定します。
ここで仮定した状況では、bar-tfstate
ファイルはVPCの状態を持っており、
foo-tfstate ファイルは bar-tfstate ファイルに依存している
とします。
そのため、想定される状態の依存関係図は以下の通りになります。
なお、依存方向は状況によって異なることをご容赦ください。
--- title: terraform_remote_stateブロックを使用した依存関係 --- %%{init:{'theme':'default'}}%% flowchart TD subgraph bucket foo["foo-tfstate"] bar["bar-tfstate"] end foo -. VPCの状態に依存 .-> bar
リポジトリのディレクトリ構成
tfstate
ファイル分割に基づいて、リポジトリのディレクトリ構成例は以下の通りになります。
🐱 repository/ ├── 📂 foo/ │ ├── backend.tf # fooコンポーネントの状態を持つ tfstate ファイルを指定する │ ├── remote_state.tf # terraform_remote_stateブロックを使用し、bar-tfstate ファイルに依存する │ ├── provider.tf │ ... │ └── 📂 bar/ ├── backend.tf # barコンポーネントの状態を持つ tfstate ファイルを指定する ├── output.tf # 他の tfstate ファイルから依存される ├── provider.tf ...
foo-tfstate
ファイルがbar-tfstate
ファイルに依存するために必要な実装は、以下の通りになります。
resource "example" "foo" { # fooリソースは、bar-tfstate ファイルのVPCに依存する vpc_id = data.terraform_remote_state.bar.outputs.bar_vpc_id ... } data "terraform_remote_state" "bar" { backend = "s3" config = { bucket = "tfstate" key = "bar/terraform.tfstate" region = "ap-northeast-1" } }
# VPCの状態は、bar-tfstate ファイルで持つ output "bar_vpc_id" { value = aws_vpc.bar.id } resource "aws_vpc" "bar" { ... }
リモートバックエンドのディレクトリ構成
tfstate
ファイル分割に基づいて、リモートバックエンド内のディレクトリ構成例は以下の通りになります。
🪣 bucket/ ├── 📂 foo │ └── terraform.tfstate # fooコンポーネントの状態を持つ │ └── 📂 bar └── terraform.tfstate # barコンポーネントの状態を持つ
AWSリソース別dataブロックの場合
AWSリソース別dataブロックによる依存
AWSリソース別data
ブロックには、以下のメリデメがあります。
特性 |
メリット ⭕️ | デメリット × |
---|---|---|
可読性 | 依存先のAWSリソースがわかりやすい。 | - |
拡張性 | - | 依存先のAWSリソース別data ブロックが必要である。 |
保守性 | 依存先と依存元の間でTerraformのバージョンに差があっても、tfstate ファイル間で直接的に依存するわけではないため、バージョン差の影響を受けない。 |
- |
今回は使用しませんが、依存関係の他の定義方法として、AWSリソース別data
ブロックがあります。
これは、tfstate
ファイルが自身以外 (例:コンソール画面、他のtfstate
ファイル) で作成されたAWSリソースの状態に依存するために使用できます。
terraform_remote_state
ブロックとは異なり、直接的にはtfstate
ファイルに依存しません。
AWSリソース別data
ブロックの場合は、実際のAWSリソースの状態に依存することにより、間接的にAWSリソースのtfstate
ファイルに依存することになります。
状態の依存関係図
例えば、AWSリソース別data
ブロックも同様にして、AWSリソースからなるプロダクトをいくつかのtfstate
ファイル (foo-tfstate
、bar-tfstate
) に分割したと仮定します。
ここで仮定した状況では、bar-tfstate
ファイルはVPCの状態を持っており、
foo-tfstate ファイルは bar-tfstate ファイルに依存している
とします。
想定される状態の依存関係図は以下の通りになります。
なお、依存方向は状況によって異なることをご容赦ください。
--- title: dataブロックを使用した依存関係 --- %%{init:{'theme':'default'}}%% flowchart TD subgraph bucket foo["foo-tfstate"] bar["bar-tfstate"] end foo -. VPCの状態に依存 .-> bar
リポジトリのディレクトリ構成
ディレクトリ構成は、tfstate
ファイル分割に基づいて、以下の通りになります。
🐱 repository/ ├── 📂 foo/ │ ├── backend.tf # fooコンポーネントの状態を持つ tfstate ファイルを指定する │ ├── data.tf # dataブロックを使用し、bar-tfstate ファイルに依存する │ ├── provider.tf │ ... │ └── 📂 bar/ ├── backend.tf # barコンポーネントの状態を持つ tfstate ファイルを指定する ├── provider.tf ...
foo-tfstate
ファイルがbar-tfstate
ファイルに依存するために必要な実装は、以下の通りになります。
# fooリソースの状態は、foo-tfstate ファイルで持つ resource "example" "foo" { # fooリソースは、bar-tfstate ファイルのVPCに依存する vpc_id = data.aws_vpc.bar.id } # VPCの状態は、bar-tfstate ファイルで持つ data "aws_vpc" "bar" { filter { name = "tag:Name" values = ["<bar-tfstateが持つVPCの名前>"] } }
リモートバックエンドのディレクトリ構成
tfstate
ファイル分割に基づいて、リモートバックエンド内のディレクトリ構成例は以下の通りになります。
🪣 bucket/ ├── 📂 foo │ └── terraform.tfstate # fooコンポーネントの状態を持つ │ └── 📂 bar └── terraform.tfstate # barコンポーネントの状態を持つ
06. tfstate ファイルの分割パターン
オススメな設計の一覧
前述の通り、tfstate
ファイルの分割の境界は、『他の状態にできるだけ依存しないリソースの関係』から見つけることができます。
分割しすぎると terraform_remote_stateブロック地獄 になるため、細かすぎず粗すぎない適切な境界を見つけていきましょう。
今回は、私が考える分割パターンをいくつか紹介します。
全てが実用的なパターンというわけでないため、オススメするものを ★ としています。
・ |
tfstate 大分類 |
tfstate 小分類 | 対応する |
対応する |
|
---|---|---|---|---|---|
推奨 | 上層 | プロバイダーのアカウント別 | ★★★ | リポジトリ自体 または上層ディレクトリ |
リモートバックエンド自体 または上層ディレクトリ |
下層 | 実行環境別 | ★★★ | 下層ディレクトリ | 下層ディレクトリ | |
任意 | 中間層 | 運用チーム責務範囲別 | ★★ | 中間層ディレクトリ | 中間層ディレクトリ |
プロダクトのサブコンポーネント別 | ★★ | ||||
運用チーム責務範囲別 × プロダクトのサブコンポーネント別 (組み合わせ) |
★ | ||||
同じテナント内のプロダクト別 | |||||
AWSリソースの種類グループ別 | |||||
AWSリソースの状態の変更頻度グループ別 |
大分類 (上層/下層/中間層) とディレクトリ構成の関係
リポジトリの場合
記事内のここ で、リポジトリ内のディレクトリ構成はtfstate
ファイル分割に基づいて設計するべき、という説明をしました。
tfstate
ファイルの分割パターンは、上層/下層/中間層 の層に大別できます。
これらの層は、以下の通りリポジトリ自体・ディレクトリ構成の設計方法に影響します。
# リポジトリ自体を分割する場合 🐱 上層/ ├── 📂 中間層/ │ ├── 📂 下層/ │ │ ├── backend.tfvars # 分割された tfstate ファイルを指定する │ │ ... │ │ ...
# リポジトリ内のディレクトリを分割する場合 🐱 リポジトリ/ ├── 📂 上層/ │ ├── 📂 中間層/ │ │ ├── 📂 下層/ │ │ │ ├── backend.tfvars # 分割された tfstate ファイルを指定する │ │ │ ... │ │ │ ...
リモートバックエンドの場合
記事内のここ で、リモートバックエンドのディレクトリ構成についても言及しました。
これらの層は、以下の通りリモートバックエンド自体・ディレクトリ構成の設計方法に影響します。
# リモートバックエンド自体を分割する場合 🪣 上層/ ├── 📂 中間層/ │ ├── 📂 下層/ │ │ └── terraform.tfstate # 分割された状態を持つ │ │ │ │ ...
# リモートバックエンド内のディレクトリを分割する場合 🪣 bucket/ ├── 📂 上層/ │ ├── 📂 中間層/ │ │ ├── 📂 下層/ │ │ │ └── terraform.tfstate # 分割された状態を持つ │ │ │ │ │ │ ...
07. 上層の分割 (推奨)
上層の分割について
上層の分割は 推奨 です。
Terraformに携わる管理者の数が少なくても採用した方がよいです。
tfstate
ファイルをパターンに応じて分割し、これに基づいてディレクトリ・リモートバックエンドも設計しましょう。
プロバイダーのアカウント別 - ★★★
この分割方法について
上層分割の中でも、基本的な方法の1つです。
プロバイダーのアカウント別にtfstate
ファイルを分割し、上層もこれに基づいて設計します。
この分割方法により、各プロバイダーの管理者が互いに影響を受けずに、terraform
コマンドの結果を得られるようになります。
一部のプロバイダーは、分割できず、他のプロバイダーと同じ
tfstate
ファイルで状態を管理せざるを得ない場合があります。
例えば、Kubernetesのプロバイダーは、EKSと同じ
tfstate
ファイルで管理した方がよいです👍
【プロバイダーアカウント別】状態の依存関係図
例えば、以下のプロバイダーを使用したい状況と仮定します。
- 主要プロバイダー (AWS)
- アプリ/インフラ監視プロバイダー (Datadog)
- ジョブ監視プロバイダー (Healthchecks)
- インシデント管理プロバイダー (PagerDuty)
ここで仮定した状況では、
各プロバイダーの tfstate ファイル間で状態が相互に依存している
とします。
AWSリソース間の相互依存ではないため、循環参照は起こりません。
そのため、想定される状態の依存関係図は以下の通りになります。
なお、依存方向は状況によって異なることをご容赦ください。
--- title: プロバイダーのアカウント別 --- %%{init:{'theme':'default'}}%% flowchart LR subgraph PagerDuty pagerDuty["tfstate"] end subgraph Healthchecks healthchecks["tfstate"] end subgraph Datadog datadog["tfstate"] end subgraph AWS aws["tfstate"] end aws -...-> datadog aws -...-> healthchecks aws -...-> pagerDuty datadog -...-> aws healthchecks -...-> aws pagerDuty -...-> aws
【プロバイダーアカウント別】リポジトリのディレクトリ構成
▼ 異なるリポジトリの場合
プロバイダーアカウント別に分割したtfstate
ファイルを、異なるリポジトリで管理します。
例えば、tfstate
ファイル分割に基づいて、リポジトリのディレクトリ構成例は以下の通りになります。
前述の依存関係図の状況と仮定します。
🐱 aws-repository/ ├── backend.tf # AWSの状態を持つ tfstate ファイルを指定する ├── output.tf # 他の tfstate ファイルから依存される ├── remote_state.tf # terraform_remote_state ブロックを使用する ├── provider.tf ...
🐱 datadog-repository/ ├── backend.tf # Datadogの状態を持つ tfstate ファイルを指定する ├── output.tf # 他の tfstate ファイルから依存される ├── remote_state.tf # terraform_remote_state ブロックを使用する ├── provider.tf ...
🐱 healthchecks-repository/ ├── backend.tf # Healthchecksの状態を持つ tfstate ファイルを指定する ├── output.tf # 他の tfstate ファイルから依存される ├── remote_state.tf # terraform_remote_state ブロックを使用する ├── provider.tf ...
🐱 pagerduty-repository/ ├── backend.tf # PagerDutyの状態を持つ tfstate ファイルを指定する ├── output.tf # 他の tfstate ファイルから依存される ├── remote_state.tf # terraform_remote_state ブロックを使用する ├── provider.tf ...
▼ 同じリポジトリの場合
プロバイダーアカウント別に分割したtfstate
ファイルを、同じリポジトリで管理します。
例えば、tfstate
ファイル分割に基づいて、リポジトリのディレクトリ構成例は以下の通りになります。
前述の依存関係図の状況と仮定します。
🐱 repository/ ├── 📂 aws/ │ ├── backend.tf # AWSの状態を持つ tfstate ファイルを指定する │ ├── output.tf # 他の tfstate ファイルから依存される │ ├── remote_state.tf # terraform_remote_state ブロックを使用する │ ├── provider.tf │ ... │ ├── 📂 datadog/ │ ├── backend.tf # Datadogの状態を持つ tfstate ファイルを指定する │ ├── output.tf # 他の tfstate ファイルから依存される │ ├── remote_state.tf # terraform_remote_state ブロックを使用する │ ├── provider.tf │ ... │ ├── 📂 healthchecks/ │ ├── backend.tf # Healthchecksの状態を持つ tfstate ファイルを指定する │ ├── output.tf # 他の tfstate ファイルから依存される │ ├── remote_state.tf # terraform_remote_state ブロックを使用する │ ├── provider.tf │ ... │ └── 📂 pagerduty/ ├── backend.tf # PagerDutyの状態を持つ tfstate ファイルを指定する ├── output.tf # 他の tfstate ファイルから依存される ├── remote_state.tf # terraform_remote_state ブロックを使用する ├── provider.tf ...
【プロバイダーアカウント別】リモートバックエンドのディレクトリ構成
▼ 異なるリモートバックエンドの場合
プロバイダーアカウント別に分割したtfstate
ファイルを、異なるリモートバックエンドで管理します。
例えば、tfstate
ファイル分割に基づいて、リモートバックエンド内のディレクトリ構成例は以下の通りになります。
前述の依存関係図の状況と仮定します。
🪣 aws-bucket/
│
└── terraform.tfstate # AWSの状態を持つ
🪣 datadog-bucket/
│
└── terraform.tfstate # Datadogの状態を持つ
🪣 healthchecks-bucket/
│
└── terraform.tfstate # Healthchecksの状態を持つ
🪣 pagerduty-bucket/
│
└── terraform.tfstate # PagerDutyの状態を持つ
▼ 同じリモートバックエンドの場合
プロバイダーアカウント別に分割したtfstate
ファイルを、同じリモートバックエンドで管理します。
例えば、tfstate
ファイル分割に基づいて、リモートバックエンド内のディレクトリ構成例は以下の通りになります。
前述の依存関係図の状況と仮定します。
🪣 bucket/ ├── 📂 aws │ └── terraform.tfstate # AWSの状態を持つ │ ├── 📂 datadog │ └── terraform.tfstate # Datadogの状態を持つ │ ├── 📂 healthchecks │ └── terraform.tfstate # Healthchecksの状態を持つ │ └── 📂 pagerduty └── terraform.tfstate # PagerDutyの状態を持つ
08. 下層の分割 (推奨)
下層の分割について
下層の分割は 推奨 です。
Terraformに携わる管理者の数が少なくても採用した方がよいです。
tfstate
ファイルをパターンに応じて分割し、これに基づいてディレクトリ・リモートバックエンドも設計しましょう。
実行環境別 - ★★★
この分割方法について
下層分割の中でも、基本的な方法の1つです。
実行環境別にtfstate
ファイルを分割し、下層もこれに基づいて設計します。
この分割方法により、各実行環境の管理者が互いに影響を受けずに、terraform
コマンドの結果を得られるようになります。
この分割方法は、様々なブログ・著名な書籍で紹介されています👀
私を含め、ほとんどの方に採用経験があるのではないでしょうか。
【実行環境別】状態の依存関係図
例えば、以下の実行環境を構築したい状況と仮定します。
- Tes環境 (検証環境)
- Stg環境 (ユーザー受け入れ環境)
- Prd環境 (本番環境)
かつ、以下のプロバイダーを使用したい状況と仮定します。
- 主要プロバイダー (AWS)
- アプリ/インフラ監視プロバイダー (Datadog)
- ジョブ監視プロバイダー (Healthchecks)
- インシデント管理プロバイダー (PagerDuty)
ここで仮定した状況では、
各実行環境の tfstate ファイルは他の実行環境には依存していない
とします。
そのため、想定される状態の依存関係図は以下の通りになります。
なお、依存方向は状況によって異なることをご容赦ください。
--- title: 実行環境別 --- %%{init:{'theme':'default'}}%% flowchart LR subgraph PagerDuty pagerDuty["tfstate"] end subgraph Healthchecks healthchecks["tfstate"] end subgraph Datadog datadog["tfstate"] end subgraph AWS subgraph tes-bucket tes["tfstate"] end subgraph stg-bucket stg["tfstate"] end subgraph prd-bucket prd["tfstate"] end end tes -...-> datadog tes -...-> healthchecks tes -...-> pagerDuty datadog -...-> tes healthchecks -...-> tes pagerDuty -...-> tes
【実行環境別】リポジトリのディレクトリ構成
▼ 異なるリポジトリの場合
プロバイダーアカウント別にtfstate
ファイルを分割することは推奨としているため、その上でディレクトリ構成を考えます。
例えば、tfstate
ファイル分割に基づいて、リポジトリのディレクトリ構成例は以下の通りになります。
前述の依存関係図の状況と仮定します。
🐱 aws-repository/ ├── output.tf # 他の tfstate ファイルから依存される ├── remote_state.tf # terraform_remote_state ブロックを使用する ├── provider.tf ├── 📂 tes/ # Tes環境 │ ├── backend.tfvars # Tes環境のAWSリソースの状態を持つ tfstate ファイルを指定する │ ... │ ├── 📂 stg/ # Stg環境 └── 📂 prd/ # Prd環境
🐱 datadog-repository/ ├── output.tf # 他の tfstate ファイルから依存される ├── remote_state.tf # terraform_remote_state ブロックを使用する ├── provider.tf ├── 📂 tes/ │ ├── backend.tfvars # Tes環境のDatadogの状態を持つ tfstate ファイルを指定する │ ... │ ├── 📂 stg/ └── 📂 prd/
🐱 healthchecks-repository/ ├── output.tf # 他の tfstate ファイルから依存される ├── remote_state.tf # terraform_remote_state ブロックを使用する ├── provider.tf ├── 📂 tes/ │ ├── backend.tfvars # HealthchecsのTes環境の状態を持つ tfstate ファイルを指定する │ ... │ ├── 📂 stg/ └── 📂 prd/
🐱 pagerduty-repository/ ├── output.tf # 他の tfstate ファイルから依存される ├── remote_state.tf # terraform_remote_state ブロックを使用する ├── provider.tf ├── 📂 tes/ │ ├── backend.tfvars # Tes環境のPagerDutyの状態を持つ tfstate ファイルを指定する │ ... │ ├── 📂 stg/ └── 📂 prd/
▼ 同じリポジトリの場合
プロバイダーアカウント別にtfstate
ファイルを分割することは推奨としているため、その上でディレクトリ構成を考えます。
例えば、tfstate
ファイル分割に基づいて、リポジトリのディレクトリ構成例は以下の通りになります。
前述の依存関係図の状況と仮定します。
🐱 repository/ ├── 📂 aws/ │ ├── output.tf # 他の tfstate ファイルから依存される │ ├── remote_state.tf # terraform_remote_state ブロックを使用する │ ├── provider.tf │ ├── 📂 tes/ # Tes環境 │ │ ├── backend.tfvars # Tes環境のAWSリソースの状態を持つ tfstate ファイルを指定する │ │ ... │ │ │ ├── 📂 stg/ # Stg環境 │ └── 📂 prd/ # Prd環境 │ ├── 📂 datadog/ │ ├── output.tf # 他の tfstate ファイルから依存される │ ├── remote_state.tf # terraform_remote_state ブロックを使用する │ ├── provider.tf │ ├── 📂 tes/ │ │ ├── backend.tfvars # Tes環境のDatadogの状態を持つ tfstate ファイルを指定する │ │ ... │ │ │ ├── 📂 stg/ │ └── 📂 prd/ │ ├── 📂 healthchecks/ │ ├── output.tf # 他の tfstate ファイルから依存される │ ├── remote_state.tf # terraform_remote_state ブロックを使用する │ ├── provider.tf │ ├── 📂 tes/ │ │ ├── backend.tfvars # Tes環境のHealthchecksの状態を持つ tfstate ファイルを指定する │ │ ... │ │ │ ├── 📂 stg/ │ └── 📂 prd/ │ └── 📂 pagerduty/ ├── output.tf # 他の tfstate ファイルから依存される ├── remote_state.tf # terraform_remote_state ブロックを使用する ├── provider.tf ├── 📂 tes/ │ ├── backend.tfvars # Tes環境のPagerDutyの状態を持つ tfstate ファイルを指定する │ ... │ ├── 📂 stg/ └── 📂 prd/
【実行環境別】リモートバックエンドのディレクトリ構成
▼ 異なるリモートバックエンドの場合
実行環境別に分割したtfstate
ファイルを、異なるリモートバックエンドで管理します。
tfstate
ファイル分割に基づいて、リモートバックエンド内のディレクトリ構成例は以下の通りになります。
例えば、前述の依存関係図の状況と仮定します。
🪣 tes-aws-bucket/
│
└── terraform.tfstate # Tes環境のAWSリソースの状態を持つ
🪣 tes-datadog-bucket/
│
└── terraform.tfstate # Tes環境のDatadogの状態を持つ
🪣 tes-healthchecks-bucket/
│
└── terraform.tfstate # Tes環境のHealthchecksの状態を持つ
🪣 tes-pagerduty-bucket/
│
└── terraform.tfstate # Tes環境のPagerDutyの状態を持つ
▼ 同じリモートバックエンド x AWSアカウント別に異なる実行環境 の場合
プロバイダーアカウント別に分割したtfstate
ファイルを、同じリモートバックエンドで管理します。
また、AWSアカウント別に異なる実行環境を作成していると仮定します。
例えば、tfstate
ファイル分割に基づいて、リモートバックエンド内のディレクトリ構成例は以下の通りになります。
前述の依存関係図の状況と仮定します。
# Tes環境の状態のみを管理するバケット 🪣 tes-bucket/ ├── 📂 aws/ │ └── terraform.tfstate # Tes環境のAWSリソースの状態を持つ │ ├── 📂 datadog/ │ └── terraform.tfstate # Tes環境のDatadogの状態を持つ │ ├── 📂 healthchecks/ │ └── terraform.tfstate # Tes環境のHealthchecksの状態を持つ │ └── 📂 pagerduty/ └── terraform.tfstate # Tes環境のPagerDutyの状態を持つ
# Stg環境の状態のみを管理するバケット
🪣 stg-bucket/
│
...
# Prd環境の状態のみを管理するバケット
🪣 prd-bucket/
│
...
▼ 同じリモートバックエンド x 単一のAWSアカウント内に全ての実行環境 の場合
プロバイダーアカウント別に分割したtfstate
ファイルを、同じリモートバックエンドで管理します。
また、単一のAWSアカウント内に全実行環境を作成しているとします。
例えば、tfstate
ファイル分割に基づいて、リモートバックエンド内のディレクトリ構成例は以下の通りになります。
前述の依存関係図の状況と仮定します。
🪣 bucket/ ├── 📂 aws/ │ ├── 📂 tes/ # Tes環境 │ │ └── terraform.tfstate # Tes環境のAWSリソースの状態を持つ │ │ │ ├── 📂 stg/ # Stg環境 │ └── 📂 prd/ # Prd環境 │ ├── 📂 datadog/ │ ├── 📂 tes/ │ │ └── terraform.tfstate # Tes環境のDatadogの状態を持つ │ │ │ ├── 📂 stg/ │ └── 📂 prd/ │ ├── 📂 healthchecks/ │ ├── 📂 tes/ │ │ └── terraform.tfstate # Tes環境のHealthchecksの状態を持つ │ │ │ ├── 📂 stg/ │ └── 📂 prd/ │ └── 📂 pagerduty/ ├── 📂 tes/ │ └── terraform.tfstate # Tes環境のPagerDutyの状態を持つ │ ├── 📂 stg/ └── 📂 prd/
09. 中間層の分割 (任意)
中間層の分割について
中間層の分割は 任意 です。
Terraformに携わる管理者が多くなるほど、効力を発揮します。
運用チーム責務範囲別 - ★★
この分割方法について
運用チーム (例:アプリチーム、インフラチーム) のAWSリソースの責務範囲別でtfstate
ファイルを分割し、中間層もこれに基づいて設計します。
この分割方法により、各運用チームが互いに影響を受けずに、terraform
コマンドの結果を得られるようになります。
この分割方法は、AWSドキュメント・著名な書籍で紹介されています👀
Terraformに携わるチームが複数ある非常に大規模なプロダクトほど効力を発揮します。
実際に私も現在進行形で採用しており、非常に実用的と考えています。
【チーム別】状態の依存関係図
例えば、以下の運用チームに分割した状況と仮定します。
- frontendチーム (アプリのフロントエンド領域担当)
- backendチーム (アプリのバックエンド領域担当)
- sreチーム (インフラ領域担当)
ここで仮定した状況では、
各チームが管理する tfstate ファイル間で状態が相互に依存している
とします。
AWSリソース間の相互依存ではないため、循環参照は起こりません。
そのため、想定される状態の依存関係図は以下の通りになります。
なお、依存方向は状況によって異なることをご容赦ください。
--- title: 運用チーム責務範囲別 --- %%{init:{'theme':'default'}}%% flowchart TB subgraph AWS subgraph tes-bucket frontend["frontend-team-tfstate<br>(CloudFront, S3, など)"] backend["backend-team-tfstate<br>(API Gateway, ElastiCache, RDS, SES, SNS, など)"] sre["sre-team-tfstate<br>(ALB, CloudWatch, EC2, ECS, EKS, IAM, VPC, など)"] frontend-..->sre backend-..->sre sre-..->frontend sre-..->backend end subgraph stg-bucket stg["tfstate"] end subgraph prd-bucket prd["tfstate"] end end
【チーム別】リポジトリのディレクトリ構成
▼ 異なるリポジトリの場合
この場合では、運用チーム責務範囲別に分割したtfstate
ファイルを、同じリポジトリで管理します。
例えば、tfstate
ファイル分割に基づいて、リポジトリのディレクトリ構成例は以下の通りになります。
この例では、状態の依存関係図と同じ状況を仮定しています。
🐱 aws-frontend-team-repository/ # frontendチーム ├── output.tf # 他の tfstate ファイルから依存される ├── provider.tf ├── remote_state.tf # terraform_remote_state ブロックを使用する ├── cloudfront.tf ├── s3.tf ├── 📂 tes/ # Tes環境 │ ├── backend.tfvars # frontendチームの状態を持つ tfstate ファイルを指定する │ ... │ ├── 📂 stg/ # Stg環境 │ ├── backend.tfvars # frontendチームの状態を持つ tfstate ファイルを指定する │ ... │ └── 📂 prd/ # Prd環境 ├── backend.tfvars # frontendチームの状態を持つ tfstate ファイルを指定する ...
🐱 aws-backend-team-repository/ # backendチーム ├── output.tf # 他の tfstate ファイルから依存される ├── provider.tf ├── remote_state.tf # terraform_remote_state ブロックを使用する ├── elasticache.tf ├── ses.tf ├── sns.tf ├── rds.tf ├── 📂 tes │ ├── backend.tfvars # backendチームの状態を持つ tfstate ファイルを指定する │ ... │ ├── 📂 stg │ ├── backend.tfvars # backendチームの状態を持つ tfstate ファイルを指定する │ ... │ └── 📂 prd ├── backend.tfvars # backendチームの状態を持つ tfstate ファイルを指定する ...
🐱 aws-sre-team-repository/ # sreチーム ├── output.tf # 他の tfstate ファイルから依存される ├── provider.tf ├── remote_state.tf # terraform_remote_state ブロックを使用する ├── alb.tf ├── cloudwatch.tf ├── ec2.tf ├── ecs.tf ├── eks.tf ├── iam.tf ├── vpc.tf ├── 📂 tes │ ├── backend.tfvars # sreチームの状態を持つ tfstate ファイルを指定する │ ... │ ├── 📂 stg │ ├── backend.tfvars # sreチームの状態を持つ tfstate ファイルを指定する │ ... │ └── 📂 prd ├── backend.tfvars # sreチームの状態を持つ tfstate ファイルを指定する ...
▼ 同じリポジトリの場合
この場合では、運用チーム責務範囲別に分割したtfstate
ファイルを、異なるリポジトリで管理します。
例えば、tfstate
ファイル分割に基づいて、リポジトリのディレクトリ構成例は以下の通りになります。
この例では、状態の依存関係図と同じ状況を仮定しています。
🐱 aws-repository/ ├── 📂 frontend-team # frontendチーム │ ├── provider.tf │ ├── output.tf # 他の tfstate ファイルから依存される │ ├── remote_state.tf # terraform_remote_state ブロックを使用する │ ├── cloudfront.tf │ ├── s3.tf │ ├── 📂 tes/ # Tes環境 │ │ ├── backend.tfvars # frontendチームの状態を持つ tfstate ファイルを指定する │ │ ... │ │ │ ├── 📂 stg/ # Stg環境 │ │ ├── backend.tfvars # frontendチームの状態を持つ tfstate ファイルを指定する │ │ ... │ │ │ └── 📂 prd/ # Prd環境 │ ├── backend.tfvars # frontendチームの状態を持つ tfstate ファイルを指定する │ ... │ ├── 📂 backend-team # backendチーム │ ├── provider.tf │ ├── output.tf # 他の tfstate ファイルから依存される │ ├── remote_state.tf # terraform_remote_state ブロックを使用する │ ├── elasticache.tf │ ├── ses.tf │ ├── sns.tf │ ├── rds.tf │ ├── 📂 tes │ │ ├── backend.tfvars # backendチームの状態を持つ tfstate ファイルを指定する │ │ ... │ │ │ ├── 📂 stg │ │ ├── backend.tfvars # backendチームの状態を持つ tfstate ファイルを指定する │ │ ... │ │ │ └── 📂 prd │ ├── backend.tfvars # backendチームの状態を持つ tfstate ファイルを指定する │ ... │ └── 📂 sre-team # sreチーム ├── provider.tf ├── output.tf # 他の tfstate ファイルから依存される ├── remote_state.tf # terraform_remote_state ブロックを使用する ├── alb.tf ├── cloudwatch.tf ├── ec2.tf ├── ecs.tf ├── eks.tf ├── iam.tf ├── vpc.tf ├── 📂 tes │ ├── backend.tfvars # sreチームの状態を持つ tfstate ファイルを指定する │ ... │ ├── 📂 stg │ ├── backend.tfvars # sreチームの状態を持つ tfstate ファイルを指定する │ ... │ └── 📂 prd ├── backend.tfvars # sreチームの状態を持つ tfstate ファイルを指定する ...
【チーム別】リモートバックエンドのディレクトリ構成
▼ 異なるリモートバックエンドの場合
運用チーム責務範囲別の場合、異なるリモートバックエンドで管理するとバックエンドが増え過ぎてしまいます。
そのため、これはお勧めしません。
▼ 同じリモートバックエンドの場合
この場合では、プロバイダーアカウント別に分割したtfstate
ファイルを、異なるリモートバックエンドで管理します。
例えば、tfstate
ファイル分割に基づいて、リモートバックエンド内のディレクトリ構成例は以下の通りになります。
この例では、状態の依存関係図と同じ状況を仮定しています。
# Tes環境の状態のみを管理するバケット 🪣 tes-bucket/ ├── 📂 frontend-team │ └── terraform.tfstate # frontendチームの状態を持つ │ ├── 📂 backend-team │ └── terraform.tfstate # backendチームの状態を持つ │ └── 📂 sre-team └── terraform.tfstate # sreチームの状態を持つ
# Stg環境の状態のみを管理するバケット
🪣 stg-bucket/
│
...
# Prd環境の状態のみを管理するバケット
🪣 prd-bucket/
│
...
プロダクトのサブコンポーネント別 - ★★
この分割方法について
プロダクトのサブコンポーネント (例:アプリ、ネットワーク、認証/認可、監視、など) 別でtfstate
ファイルを分割し、中間層もこれに基づいて設計します。
この分割方法により、サブコンポーネントの管理者が互いに影響を受けずに、terraform
コマンドの結果を得られるようになります。
サブコンポーネントは、分けようと思えばいくらでも細分化できてしまいます。
細分化した数だけ
terraform_remote_state
ブロック地獄になっていくため、適切な数 (3
〜5
個くらい) にしておくように注意が必要です。
この分割方法は、後述のAWSリソースの種類グループとごっちゃになってしまう場合があるため、プロダクトのサブコンポーネントとして意識的に分割させる必要があります👍
【サブコンポーネント別】状態の依存関係図
例えば、以下のサブコンポーネントに分割した状況と仮定します。
- application (Web3層系)
- auth (認証/認可系)
- monitor (監視系)
- network (ネットワーク系)
ここで仮定した状況では、
とします。
そのため、想定される状態の依存関係図は以下の通りになります。
なお、依存方向は状況によって異なることをご容赦ください。
--- title: プロダクトのサブコンポーネント別 --- %%{init:{'theme':'default'}}%% flowchart TB subgraph AWS subgraph tes-bucket application["application-tfstate<br>Web3層と周辺AWSリソース<br>(ALB, APIGateway, CloudFront, EC2, ECS, EKS, RDS, S3, SNS, など)"] auth["auth-tfstate<br>(IAMなど)"] monitor["monitor-tfstate<br>(CloudWatch, など)"] network["network-tfstate<br>(Route53, VPC, など)"] application-..->network application-..->auth monitor-..->application end subgraph stg-bucket stg["tfstate"] end subgraph prd-bucket prd["tfstate"] end end
【サブコンポーネント別】リポジトリのディレクトリ構成
▼ 異なるリポジトリの場合
プロダクトのサブコンポーネント別の分割パターンの場合、異なるリポジトリで管理するとリポジトリが増え過ぎてしまいます。
そのため、これはお勧めしません。
▼ 同じリポジトリの場合
この場合では、プロダクトのサブコンポーネント別に分割したtfstate
ファイルを、同じリポジトリで管理します。
例えば、tfstate
ファイル分割に基づいて、リポジトリのディレクトリ構成例は以下の通りになります。
この例では、状態の依存関係図と同じ状況を仮定しています。
🐱 aws-repository/ ├── 📂 application/ │ ├── output.tf # 他の tfstate ファイルから依存される │ ├── remote_state.tf # terraform_remote_state ブロックを使用する │ ├── provider.tf │ ├── alb.tf │ ├── cloudfront.tf │ ├── ec2.tf │ ├── ecs.tf │ ├── eks.tf │ ├── ses.tf │ ├── sns.tf │ ├── 📂 tes/ # Tes環境 │ │ ├── backend.tfvars # applicationコンポーネントの状態を持つ tfstate ファイルを指定する │ │ ... │ │ │ ├── 📂 stg/ # Stg環境 │ │ ├── backend.tfvars # applicationコンポーネントの状態を持つ tfstate ファイルを指定する │ │ ... │ │ │ └── 📂 prd/ # Prd環境 │ ├── backend.tfvars # applicationコンポーネントの状態を持つ tfstate ファイルを指定する │ ... │ ├── 📂 auth/ │ ├── provider.tf │ ├── output.tf # 他の tfstate ファイルから依存される │ ├── iam.tf │ ├── 📂 tes/ # Tes環境 │ │ ├── backend.tfvars # authコンポーネントの状態を持つ tfstate ファイルを指定する │ │ ... │ │ │ ├── 📂 stg/ # Stg環境 │ │ ├── backend.tfvars # authコンポーネントの状態を持つ tfstate ファイルを指定する │ │ ... │ │ │ └── 📂 prd/ # Prd環境 │ ├── backend.tfvars # authコンポーネントの状態を持つ tfstate ファイルを指定する │ ... │ ├── 📂 monitor/ │ ├── provider.tf │ ├── remote_state.tf # terraform_remote_state ブロックを使用する │ ├── cloudwatch.tf │ ├── 📂 tes/ # Tes環境 │ │ ├── backend.tfvars # monitorコンポーネントの状態を持つ tfstate ファイルを指定する │ │ ... │ │ │ ├── 📂 stg/ # Stg環境 │ │ ├── backend.tfvars # monitorコンポーネントの状態を持つ tfstate ファイルを指定する │ │ ... │ │ │ └── 📂 prd/ # Prd環境 │ ├── backend.tfvars # monitorコンポーネントの状態を持つ tfstate ファイルを指定する │ ... │ └── 📂 network ├── provider.tf ├── output.tf # 他の tfstate ファイルから依存される ├── route53.tf ├── vpc.tf ├── 📂 tes/ # Tes環境 │ ├── backend.tfvars # networkコンポーネントの状態を持つ tfstate ファイルを指定する │ ... │ ├── 📂 stg/ # Stg環境 │ ├── backend.tfvars # networkコンポーネントの状態を持つ tfstate ファイルを指定する │ ... │ └── 📂 prd/ # Prd環境 ├── backend.tfvars # networkコンポーネントの状態を持つ tfstate ファイルを指定する ...
【サブコンポーネント別】リモートバックエンドのディレクトリ構成
▼ 異なるリモートバックエンドの場合
プロダクトのサブコンポーネント別の分割パターンの場合、異なるリモートバックエンドで管理するとバックエンドが増え過ぎてしまいます。
そのため、これはお勧めしません。
▼ 同じリモートバックエンドの場合
この場合では、プロダクトのサブコンポーネント別に分割したtfstate
ファイルを、異なるリモートバックエンドで管理します。
例えば、tfstate
ファイル分割に基づいて、リモートバックエンド内のディレクトリ構成例は以下の通りになります。
この例では、状態の依存関係図と同じ状況を仮定しています。
# Tes環境の状態のみを管理するバケット 🪣 tes-bucket/ ├── 📂 application │ └── terraform.tfstate # applicationコンポーネントの状態を持つ │ ├── 📂 auth │ └── terraform.tfstate # authコンポーネントの状態を持つ │ ├── 📂 monitor │ └── terraform.tfstate # monitorコンポーネントの状態を持つ │ └── 📂 network └── terraform.tfstate # networkコンポーネントの状態を持つ
# Stg環境の状態のみを管理するバケット
🪣 stg-bucket/
│
...
# Prd環境の状態のみを管理するバケット
🪣 prd-bucket/
│
...
運用チーム責務範囲別 × プロダクトサブコンポーネント別 - ★
この分割方法について
運用チーム責務範囲別とプロダクトサブコンポーネント別を組み合わせてtfstate
ファイルを分割し、中間層もこれに基づいて設計します。
この分割方法により、各運用チーム内のサブコンポーネントの管理者が互いに影響を受けずに、terraform
コマンドの結果を得られるようになります。
この分割方法は、Terraformに携わるチームが複数あり、かつチームの人数も多い、非常に大規模なプロダクトほど効力を発揮します。
実際に私も現在進行形で採用しており、非常に実用的と考えています👍
【チーム別 × サブコンポーネント別】状態の依存関係図
以下の運用チームに分割した状況と仮定します。
また、各運用チームでTerraformを変更できる管理者が相当数するため、プロダクトのサブコンポーネント別にも分割したとします。
- frontendチーム
- application
- monitor
- backendチーム
- application
- monitor
- sreチーム
- application
- auth
- monitor
- network
ここで仮定した状況では、
- 各プロダクトのtfstateファイルの依存は一方向
- 最終的に、sreチームの管理する tfstate ファイルに依存している
とします。
そのため、想定される状態の依存関係図は以下の通りになります。
なお、依存方向は状況によって異なることをご容赦ください。
--- title: 運用チーム責務範囲別 × プロダクトサブコンポーネント別 --- %%{init:{'theme':'default'}}%% flowchart TB subgraph AWS subgraph tes-bucket subgraph frontend-team frontendApplication["application-tfstate<br>(CloudFront, S3, など)"] frontendMonitor["monitor-tfstate<br>(CloudWatch, など)"] end subgraph backend-team backendApplication["application-tfstate<br>(API Gateway, ElastiCache, RDS, SES, SNS, など)"] backendMonitor["monitor-tfstate<br>(CloudWatch, など)"] end subgraph sre-team sreApplication["application-tfstate<br>Web3層と周辺AWSリソース<br>(ALB, EC2, ECS, EKS, SNS, など)"] auth["auth-tfstate<br>(IAM, など)"] sreMonitor["monitor-tfstate<br>(CloudWatch, など)"] network["network-tfstate<br>(Route53, VPC, など)"] end frontendApplication-...->network sreApplication-...->auth sreApplication-...->network backendApplication-...->auth backendApplication-...->network frontendMonitor-...->frontendApplication sreMonitor-...->sreApplication backendMonitor-...->backendApplication end subgraph stg-bucket stg["tfstate"] end subgraph prd-bucket prd["tfstate"] end end
【チーム別 × サブコンポーネント別】リポジトリのディレクトリ構成
▼ 異なるリポジトリの場合
この場合では、運用チーム責務範囲別とプロダクトサブコンポーネント別を組み合わせて分割したtfstate
ファイルを、同じリポジトリで管理します。
例えば、tfstate
ファイル分割に基づいて、リポジトリのディレクトリ構成例は以下の通りになります。
この例では、状態の依存関係図と同じ状況を仮定しています。
🐱 aws-frontend-team-repository/ ├── 📂 application/ │ ├── provider.tf │ ├── output.tf # 他の tfstate ファイルから依存される │ ├── remote_state.tf # terraform_remote_state ブロックを使用する │ ├── cloudfront.tf │ ├── ses.tf │ ├── 📂 tes/ # Tes環境 │ │ ├── backend.tfvars # frontendチームが管理するapplicationコンポーネントの状態を持つ tfstate ファイルを指定する │ │ ... │ │ │ ├── 📂 stg/ # Stg環境 │ │ ├── backend.tfvars # frontendチームが管理するapplicationコンポーネントの状態を持つ tfstate ファイルを指定する │ │ ... │ │ │ └── 📂 prd/ # Prd環境 │ ├── backend.tfvars # frontendチームが管理するapplicationコンポーネントの状態を持つ tfstate ファイルを指定する │ ... │ └── 📂 monitor/ ├── provider.tf ├── remote_state.tf # terraform_remote_state ブロックを使用する ├── cloudwatch.tf ├── 📂 tes/ # Tes環境 │ ├── backend.tfvars # frontendチームが管理するmonitorコンポーネントの状態を持つ tfstate ファイルを指定する │ ... │ ├── 📂 stg/ # Stg環境 │ ├── backend.tfvars # frontendチームが管理するmonitorコンポーネントの状態を持つ tfstate ファイルを指定する │ ... │ └── 📂 prd/ # Prd環境 ├── backend.tfvars # frontendチームが管理するmonitorコンポーネントの状態を持つ tfstate ファイルを指定する ...
🐱 aws-backend-team-repository/ ├── 📂 application/ │ ├── provider.tf │ ├── output.tf # 他の tfstate ファイルから依存される │ ├── remote_state.tf # terraform_remote_state ブロックを使用する │ ├── api_gateway.tf │ ├── elasticache.tf │ ├── rds.tf │ ├── ses.tf │ ├── sns.tf │ ├── 📂 tes/ # Tes環境 │ │ ├── backend.tfvars # backendチームが管理するapplicationコンポーネントの状態を持つ tfstate ファイルを指定する │ │ ... │ │ │ ├── 📂 stg/ # Stg環境 │ │ ├── backend.tfvars # backendチームが管理するapplicationコンポーネントの状態を持つ tfstate ファイルを指定する │ │ ... │ │ │ └── 📂 prd/ # Prd環境 │ ├── backend.tfvars # backendチームが管理するapplicationコンポーネントの状態を持つ tfstate ファイルを指定する │ ... │ └── 📂 monitor/ ├── provider.tf ├── remote_state.tf # terraform_remote_state ブロックを使用する ├── cloudwatch.tf ├── 📂 tes/ # Tes環境 │ ├── backend.tfvars # backendチームが管理するmonitorコンポーネントの状態を持つ tfstate ファイルを指定する │ ... │ ├── 📂 stg/ # Stg環境 │ ├── backend.tfvars # backendチームが管理するmonitorコンポーネントの状態を持つ tfstate ファイルを指定する │ ... │ └── 📂 prd/ # Prd環境 ├── backend.tfvars # backendチームが管理するmonitorコンポーネントの状態を持つ tfstate ファイルを指定する ...
🐱 aws-sre-team-repository/ ├── 📂 application/ │ ├── provider.tf │ ├── output.tf # 他の tfstate ファイルから依存される │ ├── remote_state.tf # terraform_remote_state ブロックを使用する │ ├── alb.tf │ ├── ec2.tf │ ├── ecs.tf │ ├── eks.tf │ ├── 📂 tes/ # Tes環境 │ │ ├── backend.tfvars # sreチームが管理するapplicationコンポーネントの状態を持つ tfstate ファイルを指定する │ │ ... │ │ │ ├── 📂 stg/ # Stg環境 │ │ ├── backend.tfvars # sreチームが管理するapplicationコンポーネントの状態を持つ tfstate ファイルを指定する │ │ ... │ │ │ └── 📂 prd/ # Prd環境 │ ├── backend.tfvars # sreチームが管理するapplicationコンポーネントの状態を持つ tfstate ファイルを指定する │ ... │ ├── 📂 auth/ │ ├── provider.tf │ ├── output.tf # 他の tfstate ファイルから依存される │ ├── iam.tf │ ├── 📂 tes/ # Tes環境 │ │ ├── backend.tfvars # sreチームが管理するauthコンポーネントの状態を持つ tfstate ファイルを指定する │ │ ... │ │ │ ├── 📂 stg/ # Stg環境 │ │ ├── backend.tfvars # sreチームが管理するauthコンポーネントの状態を持つ tfstate ファイルを指定する │ │ ... │ │ │ └── 📂 prd/ # Prd環境 │ ├── backend.tfvars # sreチームが管理するauthコンポーネントの状態を持つ tfstate ファイルを指定する │ ... │ ├── 📂 monitor/ │ ├── provider.tf │ ├── remote_state.tf # terraform_remote_state ブロックを使用する │ ├── cloudwatch.tf │ ├── 📂 tes/ # Tes環境 │ │ ├── backend.tfvars # sreチームが管理するmonitorコンポーネントの状態を持つ tfstate ファイルを指定する │ │ ... │ │ │ ├── 📂 stg/ # Stg環境 │ │ ├── backend.tfvars # sreチームが管理するmonitorコンポーネントの状態を持つ tfstate ファイルを指定する │ │ ... │ │ │ └── 📂 prd/ # Prd環境 │ ├── backend.tfvars # sreチームが管理するmonitorコンポーネントの状態を持つ tfstate ファイルを指定する │ ... │ └── 📂 network ├── provider.tf ├── output.tf # 他の tfstate ファイルから依存される ├── route53.tf ├── vpc.tf ├── 📂 tes/ # Tes環境 │ ├── backend.tfvars # sreチームが管理するnetworkコンポーネントの状態を持つ tfstate ファイルを指定する │ ... │ ├── 📂 stg/ # Stg環境 │ ├── backend.tfvars # sreチームが管理するnetworkコンポーネントの状態を持つ tfstate ファイルを指定する │ ... │ └── 📂 prd/ # Prd環境 ├── backend.tfvars # sreチームが管理するnetworkコンポーネントの状態を持つ tfstate ファイルを指定する ...
▼ 同じリポジトリの場合
運用チーム責務範囲別とプロダクトサブコンポーネント別を組み合わせる分割パターンの場合、同じリポジトリで管理するとリポジトリが巨大になってしまいます。
そのため、これはお勧めしません。
【チーム別 × サブコンポーネント別】リモートバックエンドのディレクトリ構成
▼ 異なるリモートバックエンドの場合
運用チーム責務範囲別とプロダクトサブコンポーネント別を組み合わせる分割パターンの場合、異なるリモートバックエンドで管理するとバックエンドが増え過ぎてしまいます。
そのため、これはお勧めしません。
▼ 同じリモートバックエンドの場合
この場合では、運用チーム責務範囲別とプロダクトサブコンポーネント別を組み合わせて分割したtfstate
ファイルを、異なるリモートバックエンドで管理します。
例えば、tfstate
ファイル分割に基づいて、リモートバックエンド内のディレクトリ構成例は以下の通りになります。
この例では、状態の依存関係図と同じ状況を仮定しています。
# Tes環境の状態のみを管理するバケット 🪣 tes-bucket/ ├── 📂 frontend-team │ ├── 📂 application │ │ └── terraform.tfstate # frontendチームが管理するapplicationコンポーネントの状態を持つ │ │ │ └── 📂 monitor │ └── terraform.tfstate # frontendチームが管理するmonitorコンポーネントの状態を持つ │ ├── 📂 backend-team │ ├── 📂 application │ │ └── terraform.tfstate # backendチームが管理するapplicationコンポーネントの状態を持つ │ │ │ └── 📂 monitor │ └── terraform.tfstate # backendチームが管理するmonitorコンポーネントの状態を持つ │ └── 📂 sre-team ├── 📂 application │ └── terraform.tfstate # sreチームが管理するapplicationコンポーネントの状態を持つ │ ├── 📂 auth │ └── terraform.tfstate # sreチームが管理するauthコンポーネントの状態を持つ │ ├── 📂 monitor │ └── terraform.tfstate # sreチームが管理するmonitorコンポーネントの状態を持つ │ └── 📂 network └── terraform.tfstate # sreチームが管理するnetworkコンポーネントの状態を持つ
# Stg環境の状態のみを管理するバケット
🪣 stg-bucket/
│
...
# Prd環境の状態のみを管理するバケット
🪣 prd-bucket/
│
...
同じテナント内のプロダクト別
この分割方法について
同じテナント (例:同じAWSアカウントの同じVPC) 内に複数の小さなプロダクトがある場合、プロダクト別でtfstate
ファイルを分割し、中間層もこれに基づいて設計します。
ここでいうプロダクトは、アプリを動かすプラットフォーム (例:EKS、ECS、AppRunner、EC2) とそれを取り巻くAWSリソースを指しています。
この分割方法により、各プロダクトの管理者が互いに影響を受けずに、terraform
コマンドの結果を得られるようになります。
AWSの設計プラクティスとしてプロダクトごとにVPCを分けた方がよいため、この分割方法を採用することは少ないかもしれません。
ただ現実として、各プロダクトの使用するIPアドレス数が少なく、またプロダクト別にVPCを分割するのが煩雑という現場はあります😭
【同じテナント内のプロダクト】状態の依存関係図
例えば、以下のプロダクトに分割した状況と仮定します。
ここで仮定した状況では、
- 各プロダクトの tfstate ファイルの依存は一方向
- 最終的に、共有networkコンポーネントの tfstate ファイルに依存している
とします。
そのため、想定される状態の依存関係図は以下の通りになります。
なお、依存方向は状況によって異なることをご容赦ください。
--- title: 同じテナント内のプロダクト --- %%{init:{'theme':'default'}}%% flowchart TB subgraph AWS subgraph tes-bucket foo-product["foo-product-tfstate<br>(アプリを動かすプラットフォームのAWSリソース)"]-..->network bar-product["bar-product-tfstate<br>(アプリを動かすプラットフォームのAWSリソース)"]-..->network network["network-tfstate<br>(Route53, VPC)"] end subgraph stg-bucket stg["tfstate"] end subgraph prd-bucket prd["tfstate"] end end
【同じテナント内のプロダクト】リポジトリのディレクトリ構成
▼ 異なるリポジトリの場合
この場合では、同じテナント内のプロダクトに分割したtfstate
ファイルを、異なるリポジトリで管理します。
例えば、tfstate
ファイル分割に基づいて、リポジトリのディレクトリ構成例は以下の通りになります。
前述の依存関係図の状況と仮定します。
# fooプロダクトの tfstate ファイルのリポジトリ 🐱 aws-foo-product-repository/ ├── provider.tf ├── remote_state.tf # terraform_remote_state ブロックを使用する ├── 📂 tes/ # Tes環境 │ ├── backend.tfvars # fooプロダクトの状態を持つ tfstate ファイルを指定する │ ... │ ├── 📂 stg/ # Stg環境 │ ├── backend.tfvars # fooプロダクトの状態を持つ tfstate ファイルを指定する │ ... │ └── 📂 prd/ # Prd環境 ├── backend.tfvars # fooプロダクトの状態を持つ tfstate ファイルを指定する ...
# barプロダクトの tfstate ファイルのリポジトリ 🐱 aws-bar-product-repository/ ├── provider.tf ├── remote_state.tf # terraform_remote_state ブロックを使用する ├── 📂 tes/ # Tes環境 │ ├── backend.tfvars # barプロダクトの状態を持つ tfstate ファイルを指定する │ ... │ ├── 📂 stg/ # Stg環境 │ ├── backend.tfvars # barプロダクトの状態を持つ tfstate ファイルを指定する │ ... │ └── 📂 prd/ # Prd環境 ├── backend.tfvars # barプロダクトの状態を持つ tfstate ファイルを指定する ...
# 共有networkコンポーネントの tfstate ファイルのリポジトリ 🐱 aws-network-repository/ ├── output.tf # 他の tfstate ファイルから依存される ├── provider.tf ├── route53.tf ├── vpc.tf ├── 📂 tes/ # Tes環境 │ ├── backend.tfvars # networkコンポーネントの状態を持つ tfstate ファイルを指定する │ ... │ ├── 📂 stg/ # Stg環境 │ ├── backend.tfvars # networkコンポーネントの状態を持つ tfstate ファイルを指定する │ ... │ └── 📂 prd/ # Prd環境 ├── backend.tfvars # networkコンポーネントの状態を持つ tfstate ファイルを指定する ...
▼ 同じリポジトリの場合
この場合では、同じテナント内のプロダクトに分割したtfstate
ファイルを、同じリポジトリで管理します。
例えば、tfstate
ファイル分割に基づいて、リポジトリのディレクトリ構成例は以下の通りになります。
前述の依存関係図の状況と仮定します。
🐱 aws-repository/ ├── 📂 foo-product/ │ ├── provider.tf │ ├── remote_state.tf # terraform_remote_state ブロックを使用する │ ├── 📂 tes/ # Tes環境 │ │ ├── backend.tfvars # fooプロダクトの状態を持つ tfstate ファイルを指定する │ │ ... │ │ │ ├── 📂 stg/ # Stg環境 │ │ ├── backend.tfvars # fooプロダクトの状態を持つ tfstate ファイルを指定する │ │ ... │ │ │ └── 📂 prd/ # Prd環境 │ ├── backend.tfvars # fooプロダクトの状態を持つ tfstate ファイルを指定する │ ... │ ├── 📂 bar-product/ │ ├── provider.tf │ ├── remote_state.tf # terraform_remote_state ブロックを使用する │ ├── 📂 tes/ # Tes環境 │ │ ├── backend.tfvars # barプロダクトの状態を持つ tfstate ファイルを指定する │ │ ... │ │ │ ├── 📂 stg/ # Stg環境 │ │ ├── backend.tfvars # barプロダクトの状態を持つ tfstate ファイルを指定する │ │ ... │ │ │ └── 📂 prd/ # Prd環境 │ ├── backend.tfvars # barプロダクトの状態を持つ tfstate ファイルを指定する │ ... │ └── 📂 network ├── provider.tf ├── output.tf # 他の tfstate ファイルから依存される ├── route53.tf ├── vpc.tf ├── 📂 tes/ # Tes環境 │ ├── backend.tfvars # networkコンポーネントの状態を持つ tfstate ファイルを指定する │ ... │ ├── 📂 stg/ # Stg環境 │ ├── backend.tfvars # networkコンポーネントの状態を持つ tfstate ファイルを指定する │ ... │ └── 📂 prd/ # Prd環境 ├── backend.tfvars # networkコンポーネントの状態を持つ tfstate ファイルを指定する ...
【同じテナント内のプロダクト】リモートバックエンドのディレクトリ構成
▼ 異なるリモートバックエンドの場合
同じテナント内のプロダクトの場合、異なるリモートバックエンドで管理するとバックエンドが増え過ぎてしまいます。
そのため、これはお勧めしません。
▼ 同じリモートバックエンドの場合
この場合では、同じテナント内のプロダクトに分割したtfstate
ファイルを、異なるリモートバックエンドで管理します。
例えば、tfstate
ファイル分割に基づいて、リモートバックエンド内のディレクトリ構成例は以下の通りになります。
前述の依存関係図の状況と仮定します。
# Tes環境の状態のみを管理するバケット 🪣 tes-bucket/ ├── 📂 foo-product │ └── terraform.tfstate # fooプロダクトの状態を持つ │ ├── 📂 bar-product │ └── terraform.tfstate # barプロダクトの状態を持つ │ └── 📂 network └── terraform.tfstate # networkコンポーネントの状態を持つ
# Stg環境の状態のみを管理するバケット
🪣 stg-bucket/
│
...
# Prd環境の状態のみを管理するバケット
🪣 prd-bucket/
│
...
AWSリソースの種類グループ別
この分割方法について
AWSリソースの種類グループ別でtfstate
ファイルを分割し、中間層もこれに基づいて設計します。
この分割方法により、各AWSリソースの種類グループも管理者が互いに影響を受けずに、terraform
コマンドの結果を得られるようになります。
AWSリソースの種類グループは、分けようと思えばいくらでも細分化できてしまいます。
細分化した数だけ
terraform_remote_state
ブロック地獄になっていくため、適切な数 (3
〜5
個くらい) にしておくように注意が必要です。
特にこの分割方法は、グループ数がどんどん増えていく可能性があります😇
【種類グループ別】状態の依存関係図
例えば、以下の種類グループに分割した状況と仮定します。
- application (Webサーバー、Appサーバー系)
- auth (認証/認可系)
- datastore (DBサーバー系)
- cicd (CI/CD系)
- monitor (監視系)
- network (ネットワーク系)
ここで仮定した状況では、
- 各プロダクトのtfstateファイルの依存は一方向
- 最終的に、networkグループやauthグループの tfstate ファイルに依存している
とします。
そのため、想定される状態の依存関係図は以下の通りになります。
なお、依存方向は状況によって異なることをご容赦ください。
--- title: AWSリソースの種類グループ別 --- %%{init:{'theme':'default'}}%% flowchart TB subgraph AWS subgraph tes-bucket application["application-tfstate<br>例: ALB, API Gateway, CloudFront, EC2, ECS, EKS, SNS, など"] auth["auth-tfstate<br>例: IAM, など"] cicd["cicd-tfstate<br>例: Code3兄弟, など"] monitor["monitor-tfstate<br>例: CloudWatch, など"] network["network-tfstate<br>例: Route53, VPC, など"] datastore["datastore-tfstate<br>例: ElastiCache, RDS, S3, など"] application-....->auth application-..->datastore application-...->network cicd-..->application datastore-..->network monitor-..->application monitor-..->datastore end subgraph stg-bucket stg["tfstate"] end subgraph prd-bucket prd["tfstate"] end end
【種類グループ別】リポジトリのディレクトリ構成
▼ 異なるリポジトリの場合
AWSリソースの種類グループ別の分割パターンの場合、異なるリポジトリで管理するとリポジトリが増え過ぎてしまいます。
そのため、これはお勧めしません。
▼ 同じリポジトリの場合
この場合では、AWSリソースの種類グループ別に分割したtfstate
ファイルを、同じリポジトリで管理します。
例えば、tfstate
ファイル分割に基づいて、リポジトリのディレクトリ構成例は以下の通りになります。
この例では、状態の依存関係図と同じ状況を仮定しています。
🐱 aws-repository/ ├── 📂 application/ │ ├── provider.tf │ ├── remote_state.tf # terraform_remote_state ブロックを使用する │ ├── output.tf # 他の tfstate ファイルから依存される │ ├── alb.tf │ ├── api_gateway.tf │ ├── cloudfront.tf │ ├── ec2.tf │ ├── ecs.tf │ ├── eks.tf │ ├── ses.tf │ ├── sns.tf │ ├── 📂 tes/ # Tes環境 │ │ ├── backend.tfvars # applicationコンポーネントの状態を持つ tfstate ファイルを指定する │ │ ... │ │ │ ├── 📂 stg/ # Stg環境 │ │ ├── backend.tfvars # applicationコンポーネントの状態を持つ tfstate ファイルを指定する │ │ ... │ │ │ └── 📂 prd/ # Prd環境 │ ├── backend.tfvars # applicationコンポーネントの状態を持つ tfstate ファイルを指定する │ ... │ ├── 📂 auth/ │ ├── provider.tf │ ├── output.tf # 他の tfstate ファイルから依存される │ ├── iam.tf │ ├── 📂 tes/ # Tes環境 │ │ ├── backend.tfvars # authコンポーネントの状態を持つ tfstate ファイルを指定する │ │ ... │ │ │ ├── 📂 stg/ # Stg環境 │ │ ├── backend.tfvars # authコンポーネントの状態を持つ tfstate ファイルを指定する │ │ ... │ │ │ └── 📂 prd/ # Prd環境 │ ├── backend.tfvars # authコンポーネントの状態を持つ tfstate ファイルを指定する │ ... │ ├── 📂 cicd/ │ ├── provider.tf │ ├── remote_state.tf # terraform_remote_state ブロックを使用する │ ├── codebuild.tf │ ├── codecommit.tf │ ├── codedeploy.tf │ ├── 📂 tes/ # Tes環境 │ │ ├── backend.tfvars # cicdコンポーネントの状態を持つ tfstate ファイルを指定する │ │ ... │ │ │ ├── 📂 stg/ # Stg環境 │ │ ├── backend.tfvars # cicdコンポーネントの状態を持つ tfstate ファイルを指定する │ │ ... │ │ │ └── 📂 prd/ # Prd環境 │ ├── backend.tfvars # cicdコンポーネントの状態を持つ tfstate ファイルを指定する │ ... │ ├── 📂 datastore/ │ ├── provider.tf │ ├── output.tf # 他の tfstate ファイルから依存される │ ├── remote_state.tf # terraform_remote_state ブロックを使用する │ ├── elasticache.tf │ ├── rds.tf │ ├── s3.tf │ ├── 📂 tes/ # Tes環境 │ │ ├── backend.tfvars # datastoreコンポーネントの状態を持つ tfstate ファイルを指定する │ │ ... │ │ │ ├── 📂 stg/ # Stg環境 │ │ ├── backend.tfvars # datastoreコンポーネントの状態を持つ tfstate ファイルを指定する │ │ ... │ │ │ └── 📂 prd/ # Prd環境 │ ├── backend.tfvars # datastoreコンポーネントの状態を持つ tfstate ファイルを指定する │ ... │ ├── 📂 monitor/ │ ├── provider.tf │ ├── remote_state.tf # terraform_remote_state ブロックを使用する │ ├── cloudwatch.tf │ ├── 📂 tes/ # Tes環境 │ │ ├── backend.tfvars # monitorコンポーネントの状態を持つ tfstate ファイルを指定する │ │ ... │ │ │ ├── 📂 stg/ # Stg環境 │ │ ├── backend.tfvars # monitorコンポーネントの状態を持つ tfstate ファイルを指定する │ │ ... │ │ │ └── 📂 prd/ # Prd環境 │ ├── backend.tfvars # monitorコンポーネントの状態を持つ tfstate ファイルを指定する │ ... │ └── 📂 network ├── provider.tf ├── output.tf # 他の tfstate ファイルから参照できるように、outputブロックを定義する ├── route53.tf ├── vpc.tf ├── 📂 tes/ # Tes環境 │ ├── backend.tfvars # networkコンポーネントの状態を持つ tfstate ファイルを指定する │ ... │ ├── 📂 stg/ # Stg環境 │ ├── backend.tfvars # networkコンポーネントの状態を持つ tfstate ファイルを指定する │ ... │ └── 📂 prd/ # Prd環境 ├── backend.tfvars # networkコンポーネントの状態を持つ tfstate ファイルを指定する ...
【種類グループ別】リモートバックエンドのディレクトリ構成
▼ 異なるリモートバックエンドの場合
AWSリソースの種類グループ別の分割パターンの場合、異なるリモートバックエンドで管理するとバックエンドが増え過ぎてしまいます。
そのため、これはお勧めしません。
▼ 同じリモートバックエンドの場合
この場合では、AWSリソースの種類グループ別に分割したtfstate
ファイルを、異なるリモートバックエンドで管理します。
例えば、tfstate
ファイル分割に基づいて、リモートバックエンド内のディレクトリ構成例は以下の通りになります。
この例では、状態の依存関係図と同じ状況を仮定しています。
# Tes環境の状態のみを管理するバケット 🪣 tes-bucket/ ├── 📂 application │ └── terraform.tfstate # applicationコンポーネントの状態を持つ │ ├── 📂 auth │ └── terraform.tfstate # authコンポーネントの状態を持つ │ ├── 📂 cicd │ └── terraform.tfstate # cicdコンポーネントの状態を持つ │ ├── 📂 datastore │ └── terraform.tfstate # datastoreコンポーネントの状態を持つ │ ├── 📂 monitor │ └── terraform.tfstate # monitorコンポーネントの状態を持つ │ └── 📂 network └── terraform.tfstate # networkコンポーネントの状態を持つ
# Stg環境の状態のみを管理するバケット
🪣 stg-bucket/
│
...
# Prd環境の状態のみを管理するバケット
🪣 prd-bucket/
│
...
AWSリソースの状態の変更頻度グループ別
この分割方法について
AWSリソースの状態の変更頻度グループ別でtfstate
ファイルを分割し、中間層もこれに基づいて設計します。
この分割方法により、各変更頻度グループの管理者が互いに影響を受けずに、terraform
コマンドの結果を得られるようになります。
変更頻度の境界が曖昧なため、この分割方法は個人的にお勧めしません。
【変更頻度グループ別】状態の依存関係図
例えば、以下の変更頻度グループに分割した状況と仮定します。
- 変更高頻度グループ
- 変更中頻度グループ
- 変更低頻度グループ
ここで仮定した状況では、
- 各プロダクトのtfstateファイルの依存は一方向
- 最終的に、変更低頻度グループの tfstate ファイルに依存している
とします。
そのため、想定される状態の依存関係図は以下の通りになります。
なお、依存方向は状況によって異なることをご容赦ください。
--- title: AWSリソースの状態の変更頻度グループ別 --- %%{init:{'theme':'default'}}%% flowchart TB subgraph AWS subgraph tes-bucket high["high-freq-tfstate<br>例: API Gateway, CloudFront, CloudWatch, IAM"] middle["middle-freq-tfstate<br>例: ALB, EC2, ECS, EKS, ElastiCache, RDS, S3, SES, SNS"] low["low-freq-tfstate<br>例: Route53, VPC"] high-...->low middle-..->low end subgraph stg-bucket stg["tfstate"] end subgraph prd-bucket prd["tfstate"] end end
【変更頻度グループ別】リポジトリのディレクトリ構成
▼ 異なるリポジトリの場合
AWSリソースの変更頻度グループ別の分割パターンの場合、異なるリポジトリで管理するとリポジトリが増え過ぎてしまいます。
そのため、これはお勧めしません。
▼ 同じリポジトリの場合
この場合では、AWSリソースの変更頻度グループ別に分割したtfstate
ファイルを、同じリポジトリで管理します。
例えば、tfstate
ファイル分割に基づいて、リポジトリのディレクトリ構成例は以下の通りになります。
この例では、状態の依存関係図と同じ状況を仮定しています。
🐱 aws-repository/ ├── 📂 high-freq # 高頻度変更グループ │ ├── provider.tf │ ├── remote_state.tf # terraform_remote_state ブロックを使用する │ ├── api_gateway.tf │ ├── cloudfront.tf │ ├── cloudwatch.tf │ ├── ec2.tf │ ├── ecs.tf │ ├── eks.tf │ ├── iam.tf │ ├── 📂 tes/ # Tes環境 │ │ ├── backend.tfvars # high-freqコンポーネントの状態を持つ tfstate ファイルを指定する │ │ ... │ │ │ ├── 📂 stg/ # Stg環境 │ │ ├── backend.tfvars # high-freqコンポーネントの状態を持つ tfstate ファイルを指定する │ │ ... │ │ │ └── 📂 prd/ # Prd環境 │ ├── backend.tfvars # high-freqコンポーネントの状態を持つ tfstate ファイルを指定する │ ... │ ├── 📂 low-freq # 低頻度変更グループ │ ├── provider.tf │ ├── output.tf # 他の tfstate ファイルから依存される │ ├── route53.tf │ ├── vpc.tf │ ├── 📂 tes │ │ ├── backend.tfvars # low-freqコンポーネントの状態を持つ tfstate ファイルを指定する │ │ ... │ │ │ ├── 📂 stg │ │ ├── backend.tfvars # low-freqコンポーネントの状態を持つ tfstate ファイルを指定する │ │ ... │ │ │ └── 📂 prd │ ├── backend.tfvars # low-freqコンポーネントの状態を持つ tfstate ファイルを指定する │ ... │ └── 📂 middle-freq # 中頻度変更グループ (高頻度とも低頻度とも言えないリソース) ├── provider.tf ├── remote_state.tf # terraform_remote_state ブロックを使用する ├── elasticache.tf ├── rds.tf ├── s3.tf ├── ses.tf ├── 📂 tes │ ├── backend.tfvars # middle-freqコンポーネントの状態を持つ tfstate ファイルを指定する │ ... │ ├── 📂 stg │ ├── backend.tfvars # middle-freqコンポーネントの状態を持つ tfstate ファイルを指定する │ ... │ └── 📂 prd ├── backend.tfvars # middle-freqコンポーネントの状態を持つ tfstate ファイルを指定する ...
【変更頻度グループ別】リモートバックエンドのディレクトリ構成
▼ 異なるリモートバックエンドの場合
AWSリソースの変更頻度グループ別の分割パターンの場合、異なるリモートバックエンドで管理するとバックエンドが増え過ぎてしまいます。
そのため、これはお勧めしません。
▼ 同じリモートバックエンドの場合
この場合では、AWSリソースの変更頻度グループ別に分割したtfstate
ファイルを、異なるリモートバックエンドで管理します。
例えば、tfstate
ファイル分割に基づいて、リモートバックエンド内のディレクトリ構成例は以下の通りになります。
この例では、状態の依存関係図と同じ状況を仮定しています。
# Tes環境の状態のみを管理するバケット 🪣 tes-bucket/ ├── 📂 high-freq │ └── terraform.tfstate # high-freqコンポーネントの状態を持つ │ ├── 📂 middle-freq │ └── terraform.tfstate # middle-freqコンポーネントの状態を持つ │ └── 📂 low-freq └── terraform.tfstate # low-freqコンポーネントの状態を持つ
# Stg環境の状態のみを管理するバケット
🪣 stg-bucket/
│
...
# Prd環境の状態のみを管理するバケット
🪣 prd-bucket/
│
...
10. おわりに
Terraformのtfstate
ファイルの分割パターンをもりもり布教しました。
ぜひ採用してみたい分割パターンはあったでしょうか。
Terraformの開発現場の具体的な要件は千差万別であり、特にtfstate
ファイル間の状態の依存関係は様々です。
もし、この記事を参考に設計してくださる方は、分割パターンを現場に落とし込んで解釈いただけると幸いです🙇🏻
「自分を信じても…信頼に足る仲間を信じても…誰にもわからない…」
(お友達の
@nwiizo
, 2023, Terraform Modules で再利用できるので最高ではないでしょうか?)
謝辞
今回、Terraformの分割パターンの収集にあたり、以下の方々からの意見・実装方法も参考にさせていただきました。
@kiyo_12_07
さん@masasuzu
さん@tozastation
さん
(アルファベット順)
この場で感謝申し上げます🙇🏻