🫠

Terraformのディレクトリ構成の設計

に公開

Terraformのディレクトリ構成をどう設計する?

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