Terraformの環境切り替えとフォルダ構成について
はじめに
Terraformを使用してクラウドインフラを構築する際、dev/stg/prdといった環境の切り替えや、それに伴うファイルレイアウト(ディレクトリ構成)にはさまざまな方法があります。明確なベストプラクティスは存在しませんが、それぞれにメリットとデメリットがあります。
本記事では、一般的なパターンをまとめ、プロジェクトの要件や条件を踏まえて適切な方法を選択する際の参考になればと思います。一部、個人的な見解も含まれますのでご了承ください。
ファイルレイアウト (+module) による方法
Terraform の module 機能を使用することで、dev/stg/prdといった複数の環境に対して大部分が同様[1]のインフラを構築することができます。
module機能を活用することで、再利用の粒度や管理範囲をファイルレイアウトによって柔軟に調整できます。ここでは、よくあるパターンをいくつか紹介します。プロジェクトの条件に応じてメリット・デメリットを検討し、最適なものを選択してください。
ファイルレイアウト1
最も一般的と思われるパターンで、トップレベルで環境別に分けるパターンです。ディレクトリ構成の一例を以下に示します。
.
├── environments
│ ├── dev
│ │ ├── backend.tf
│ │ └── main.tf
│ ├── prd
│ │ ├── backend.tf
│ │ └── main.tf
│ └── stg
│ ├── backend.tf
│ └── main.tf
└── modules
├── database
│ ├── main.tf
│ ├── outputs.tf
│ └── variables.tf
├── networking
│ ├── main.tf
│ ├── outputs.tf
│ └── variables.tf
└── web_server
├── main.tf
├── outputs.tf
└── variables.tf
大部分のリソースは module の一部として作成し、各環境からこれを取捨選択し、必要に応じてパラメータを調節して使用します。特定の環境固有のリソースがある場合は、その環境のディレクトリ内(例: environments/dev/main.tf
)に定義します。
参考: Google Cloud ドキュメント 「Terraform on Google Cloud」
ファイルレイアウト2
トップレベルをサービスなどのまとまりで分け、その下に環境別のディレクトリを作成するパターンです。以下が一例です。
.
├── service1
│ ├── environments
│ │ ├── dev
│ │ │ ├── backend.tf
│ │ │ └── main.tf
│ │ ├── prd
│ │ │ ├── backend.tf
│ │ │ └── main.tf
│ │ └── stg
│ │ ├── backend.tf
│ │ └── main.tf
│ └── modules
│ ├── database
│ │ ├── main.tf
│ │ ├── outputs.tf
│ │ └── variables.tf
│ ├── networking
│ │ ├── main.tf
│ │ ├── outputs.tf
│ │ └── variables.tf
│ └── web_server
│ ├── main.tf
│ ├── outputs.tf
│ └── variables.tf
├── service2
│ ├── environments
│ │ ├── dev
│ │ ├── prd
│ │ └── stg
│ └── modules
│ ├── datalake
│ ├── ml_modules
│ └── networking
└── service3
├── environments
│ ├── dev
│ ├── prd
│ └── stg
└── modules
├── data_pipeline
└── database
ファイルレイアウト1の構成をサービスごとに管理リポジトリを分割するなどすると、このファイルレイアウト2と同様の形になります。
参考: Zenn記事「ココナラ的ベストTerraformディレクトリ構造を考える」
ファイルレイアウト1との比較によるメリットデメリット
メリット
-
.tfstate
ファイルが小さくなるため、-
terraform apply
の実行時間が短くなる - あるリソースが
.tfstate
でどのように管理されているか特定しやすい
-
-
terraform apply
などにより、環境が壊れた際の影響範囲が小さい
デメリット
- 管理する
.tfstate
ファイルの数が多くなるため、必要なterraform apply
実行回数が増える - サービス間で共通のモジュールをどこに配置するか等の設計上の労力が増える
ファイルレイアウト3
module を使わない、または限定的に使用する方法で、トップレベルでリソースのまとまりごとにディレクトリを分け、その下に各環境のリソースを定義する方法です。以下の構成が一例です。
.
├── database
│ ├── dev
│ │ └── main.tf
│ ├── prd
│ │ └── main.tf
│ └── stg
│ └── main.tf
├── networking
│ ├── dev
│ │ └── main.tf
│ ├── prd
│ │ └── main.tf
│ └── stg
│ └── main.tf
└── web_server
├── dev
│ └── main.tf
├── prd
│ └── main.tf
└── stg
└── main.tf
参考: スライド「ベストな Terraform ディレクトリ構成を考察してみた」
ファイルレイアウト1と比較してのメリットデメリット
メリット
-
.tfstate
ファイルが小さくなるため、terraform apply
の実行時間が短くなる -
terraform apply
などにより、環境が壊れた際の影響範囲が小さい
デメリット
- 管理する
.tfstate
ファイルの数が多くなるため、必要なterraform apply
実行回数が増える - コピペのようにして同様のリソースを異なる環境に作成していくため、コードの重複部分が大きくなる(DRYに反する)
.tfstate
の管理対象リソースが小さいことによって、ファイルレイアウト2と同様のメリットがありますが、コードの重複などデメリットも大きいため、敢えてこの方法を選択べきケースは多くないと思われます。
workspace による方法 (基本的には非推奨)
Terraform には、複数の独立した作業スペースを作成するための機能として、workspace があります。
しかし、公式ドキュメント
にそれとなくdev/stg/prdのような環境の切り替えに使用することは避けるべきことが記載されています。また、書籍「詳解Terraform 第3版
」にも、環境の切り替えにworkspaceの使用を避けるべきという趣旨の記載があります。
各環境での構成差分が無い小規模なプロダクトを高速に作りたいなど、特殊な事情がない限り、workspaceを環境を分けるために使用しないのが懸命でしょう。
workspace のユースケース
workspace のユースケースは公式ドキュメント
に記載されている通り、以下のようなものがあります。
- 本番同等の環境でテストを行う
- ディスク領域や帯域幅の節約
同一開発環境で複数の開発者間で干渉しない環境を作るユースケースも考えられます。例えば、開発者ごとにワークスペース名を割り当て、リソースの命名規則にワークスペース名(${terraform.workspace}
)を接頭辞に指定するなどでこれが可能です。
変数による方法
こちらの記事で紹介されているものです。
利点は記事内でも述べられています。特にmodule設計の労力から解放されるという点は大きなメリットです。
一方で、moduleはコードの整理という側面もあり、そのメリットを享受できない点には注意が必要です。例えば、サービスが大規模になると、適切に切り分けないと各ファイルが肥大化し、保守や新規メンバーのキャッチアップなどが難しくなる可能性があります。その点、適切に module が分割されている場合、必要なコードへのアクセスが容易になります(そのためのmodule設計自体が難しいという話もありますが)。
また、module は Terraform の標準機能であるのに対し、独自のラッパースクリプトを使用する方法は、新規メンバーが戸惑う可能性もあります。
結論
dev/stg/prdといった環境を分ける方法として、基本的には以下のいずれかから選択するのが良いでしょう。
- ファイルレイアウト1による方法
- ファイルレイアウト2による方法
- 変数による方法
管理対象のインフラの規模や、チームメンバーの出入りの頻度、プロダクトのスケール予測、適用速度などの要件を考慮し、それぞれのメリット・デメリットを比較して最適な方法を選定するのが良いでしょう。
-
開発環境では本番環境よりコストを抑えた設定値にする、データ保護の観点で特定リソースを開発環境に構築しない等によって、一部の値を変更する ↩︎
Discussion