Terraformの構成管理として、モノレポ型とコンポーネント分割型を比較します。
はじめに
Terraformを利用したプロジェクト(今回はAWS)を運用している最中にコード量が増えてくると、ディレクトリをどう切るか、状態ファイル(tfstate)をどう分けるか、がチーム運用のボトルネックになります。
本稿では現場でよく採用される次の 2つの構成パターンを取り上げ、メリット・デメリットを整理します。
私自身、両方のディレクトリ構成の運用を経験しているため、主観も含まれます。
⚠️本記事が絶対的な解ではないため、参考程度にお読みください。
| 呼称 |
簡単に説明 |
| パターンA:モノレポ型 |
1つのリポジトリで各環境(dev/stg/prodなど)でそれぞれ1つのtfstateにすべてを集約 |
| パターンB:コンポーネント分割型 |
コンポーネント(VPC, S3, RDS …)ごとにディレクトリと tfstateを分割 |
どちらが良いか?ではなく、どんな条件ならどちらがプロジェクトに適用するかを考えます。
パターンA: モノレポ型(単一 tfstate)
terraform/
├── modules/ # 再利用可能なモジュール
│ ├── vpc/
│ ├── security_groups/
│ ├── alb/
│ ├── ecs/
│ ├── rds/
│ ├── s3/
│ ├── waf/
│ ├── ecr/
│ └── codepipeline/
├── bootstrap/ # Remote State 用(初回だけ実行)
└── environments/ # 環境ごと (workspace 切替でも OK)
├── dev/
├── stg/
└── prod/
仕組み
- 環境1つに対してtfstateを1つずつ管理。
例:environments/prod 以下を apply すると prod.tfstate が更新される
- 依存関係の可視性を重視
すべてのリソースが1つのtfstateで状態保持されるため、terraform planのdiffも一括で確認できる。
- 変更範囲を絞りたいときは-targetオプションで適用範囲を制御
メリット
| 項目 |
詳細 |
| コード全体の一貫性を担保 |
VPC→ALB→ECS などの依存がterraform上で自動解決される |
| state運用が簡単 |
AWS上のS3などを使用してtfstateファイルを管理するため、依存関係なども解消 |
| レビューのしやすさ |
PRで今回のインフラ変更を俯瞰して確認できる。 |
デメリット
| 項目 |
詳細 |
| PR競合 |
複数人が同時に異なるリソースを変更すると競合が起きる。 |
| ピンポイントの更新が面倒 |
-targetでリソースをピンポイントにapplyできるが、その際に依存がある時もあり都度指定が煩雑 |
| 巨大なterraform plan |
大規模環境だとplan/diffが数千行になり可読性が低下(レビューが追いつかない) |
パターンB:コンポーネント分割型(複数tfstate)
terraform/
├── components/
│ ├── vpc/
│ │ ├── main.tf
│ │ └── backend.hcl # vpc.tfstate
│ ├── s3/
│ │ ├── main.tf
│ │ └── backend.hcl # s3.tfstate
│ ├── rds/
│ │ └── ...
│ └── ...
└── modules/ # 各ディレクトリで呼び出す共通モジュール
仕組み
- components内のリソースを示すディレクトリ1つに対して1つのtfstateを管理
例:components/vpcをapplyするとvpc.tfstateのみ変更
- コンポーネント間連携は output, data, terraform_remote_stateで行う
VPC のoutput.vpc_idをS3側で参照する、など
メリット
| 項目 |
詳細 |
| 並列開発しやすい |
それぞれ独立したtfstateなので競合が起きづらい |
| terraform planが軽量 |
対象コンポーネントだけのdiffを確認できる |
| 段階的デプロイが可能 |
VPC→S3→ECSの順にapplyなど、リリース計画が立てやすい。 |
デメリット
| 項目 |
詳細 |
| 依存管理を手動で更新 |
VPCを更新→他ディレクトリでterraform applyし直し…と運用コストが増える。 |
| 初回のoutputが空 |
参照元のapplyが終わるまでremote_stateが解決できずエラーになりがち(段階的なPRを作成する必要がある。) |
| stateファイル乱立 |
バックエンド設定・IAM ポリシー・Lockを各ディレクトリごとに用意 |
比較のサマリ
| 観点 |
モノレポ型 |
コンポーネント分割型 |
| tfstate管理 |
1環境1ファイル |
コンポーネントごと |
| CI/CDの並列実行 |
競合しやすい |
しやすい |
| 変更の見通し |
⭕️全リソースがdiffに出る |
🔺依存変更が追いづらい |
| 初学者体験 |
⭕️公式ドキュメントに近い |
🔺remote_stateの理解が必須(もしくは経験者のフォロー) |
| 運用コスト |
低 |
高 |
| オススメ規模感 |
小〜中規模/少人数チーム |
大規模/マイクロサービス的分割 |
個人的な結論
- チームサイズが小さいorPoC フェーズ
→ パターンAシンプルで学習コストも低い。特に個人でCursorなどのエージェントを使用して作成する際にはパターンAが親和性が高い。小さい基盤かつ数人のプロジェクトであれば、PRの粒度も小さいので運用負荷が低い。
- チームで日次デプロイを回す大規模環境
→ パターンBでコンポーネントをサービス単位に切り出し、各チームが独立デプロイ&GitHub Actionsの並列化を狙う。
ただしVPC更新後に依存関係を手動でapplyが運用負債になりやすいので、CI/CDで依存グラフを解決する仕組みの導入が必要である。
運用におけるプラクティス
- Remote State は必須
- S3+DynamoDB Lock構成を用いて、ローカルでtfstateの管理は避ける。
まとめ
まずは パターンAで始め、チームやシステムがスケールして痛みが出てきた段階で パターンBへ移行するのが良い運用なのかもしれません。この記事がディレクトリ設計のヒントになれば幸いです。
Discussion