はじめに
私は博報堂テクノロジーズのマネジメントセンター、サービスリライアビリティ部に所属するSRE(Service Reliability Engineering)の矢野です。
開発チームと密接に連携しながら、広告運用の効率化を目指したSaaS型ウェブアプリケーション『iPalette』の信頼性向上と運用の最適化に取り組んでいます。iPaletteは、広告主と広告代理店が広告キャンペーンのKPIや予算進捗をリアルタイムで確認し、業務を効率化するためのツールです。
iPaletteのインフラはスケーラビリティと柔軟性が求められるため、Terraformを活用してインフラのコード管理を行い、これらを確保しています。しかし、Terraformを運用する中で、「AWSサービス単位のモジュール」と「機能単位のモジュール」のどちらが最適かという課題に直面しました。
本記事では、私たちが直面した具体的な課題や決断の背景を通して、モジュール構成選定のプロセスと学びを共有します。
目次
- 第1章:Terraform導入初期 — 私たちが『AWSサービス単位』にこだわった理由と、小さな成功体験の積み重ね
- 第2章:AWSサービス単位の限界 — 横断的サービス(IAM・S3)の壁に直面し、チームで本気で悩んだこと
- 第3章:機能単位へのリファクタリング — チームの議論を経て決断したモジュール構成の変更と、その実際の道のり
- 第4章:リファクタリング後日談 — 数ヶ月運用してチームが実感した、機能単位モジュールによるリアルな改善
- 第5章:絶対的な正解はない — Terraformモジュール構成の本質的なトレードオフと、私たちが悩み抜いて辿り着いた結論
- おわりに:あなたのチームでは、Terraformのモジュール構成をどうしていますか?
第1章:Terraform導入初期 — 私たちが『AWSサービス単位』にこだわった理由と、小さな成功体験の積み重ね
私たちのチームがTerraformを導入した初期、AWSサービス単位でモジュールを作成することを決めました。この選択は、シンプルさと再利用性を最優先にしたものでした。
最初の段階では、Terraformを使い始めたばかりのメンバーが多く、インフラのコード管理に対して不安がありました。手動でAWSマネジメントコンソールを使用してリソースを作成していた経験から、IaC(Infrastructure as Code)への移行は大きな変革でした。
サービス単位でモジュールを分けたことで、S3やCloudFrontなどのリソースをシンプルに管理できました。これにより、インフラの設定が直感的になり、どのリソースがどこで管理されているのかが明確になりました。特に、Terraformに不慣れなメンバーも設定変更を迅速に行うことができ、作業の効率化が図れました。
初期導入の背景と選んだ理由
手動でリソースを管理していた当初、インフラの変更に時間がかかり、エラーが頻発していました。これを解決するために、Terraformを導入することを決定し、最初に選んだのがサービス単位でのモジュール分割でした。AWSのリソース(S3、CloudFront、IAMなど)をそれぞれ独立したモジュールとして管理することで、設定をシンプルに保ちながらも、必要な部分での再利用が可能になると考えたからです。
特に、Terraformを導入した当時(数年前)は、私たちのチームにもTerraformに関するノウハウが少なく、初めて扱うインフラ管理ツールに対して慎重にアプローチする必要がありました。リソース単位でモジュールを分けることで、最初の学習曲線を低く保ち、チーム全体がスムーズに運用を開始できると判断しました。
成功体験とその具体例
サービス単位でモジュールを分けたことで、S3やCloudFrontなどのリソースをシンプルに管理できました。例えば、以下のように各サービスを独立したモジュールとして管理したことで、インフラの設定は直感的でわかりやすく、変更が非常にスムーズに行えるようになりました。
modules/
├─ iam
│ ├─ main.tf
│ ├─ outputs.tf
│ └─ variables.tf
├─ s3
│ ├─ main.tf
│ ├─ outputs.tf
│ └─ variables.tf
├─ cloudfront
│ ├─ main.tf
│ ├─ outputs.tf
│ └─ variables.tf
└─ ...
この構成では、S3の設定は modules/s3 にあるということが明確で、どのリソースがどこで管理されているのか、チームメンバー全員が直感的に理解できました。特にTerraformに不慣れなメンバーも、どこで設定を行うべきかがすぐに分かるため、作業の混乱が少なく、スムーズに進みました。
また、最初はTerraformの経験が浅いメンバーも多かったため、よりシンプルな設計が運用を楽にし、インフラ管理に対する心理的なハードルを下げる効果もありました。モジュールごとに明確に整理されていたため、「IAMの設定は modules/iam」「CloudFrontの設定は modules/cloudfront」といった認識が簡単にできました。これにより、設定変更の際のミスも減少し、スムーズな運用が可能になりました。
AWSサービス単位のディレクトリ構成とその利点
最初に採用したディレクトリ構成は、サービス単位でモジュールを分ける非常にシンプルで直感的なものでした。各サービスが独立しており、どの設定をどこで行うかが非常に明確で、チームメンバー全員がすぐに理解できました。
この構成の大きな利点は、直感的でわかりやすいことでした。リソースごとにモジュールを分けることで、どの設定をどこで行うかが非常に明確になり、チームメンバー全員が素早く理解できるようになりました。また、初期の段階ではリソース間の依存関係が少なく、サービス単位で分けることで変更範囲が明確になり、作業がしやすかったのです。
さらに、最初にシンプルな設計を選んだことが学習コストを低減させ、Terraformの運用に慣れるためのスムーズなスタートを切ることができました。最初に直面するのが単純で理解しやすい設計だったため、チーム全体が早い段階で運用に取り組むことができました。
まとめ
Terraform導入初期において、AWSサービス単位でモジュールを作成したことは、シンプルで直感的な管理を可能にし、チーム全体の学習曲線を低く保つうえで非常に効果的でした。しかし、次第に複数機能にまたがるリソース管理に直面し、課題も出てきました。この課題にどのように対処し、モジュール設計を進化させたのかを次章で詳しく解説します。
第2章:AWSサービス単位の限界 — 横断的サービス(IAM・S3)の壁に直面し、チームで本気で悩んだこと
初期のTerraform導入時にAWSサービス単位でモジュールを分けたことが、シンプルで理解しやすく、チーム全体の学習曲線を低く保つためにどれだけ効果的であったかを紹介しました。しかし、開発が進むにつれて、複数のリソースが相互に依存するようになり、サービス単位でのモジュール分割の限界が見えてきました。特に、IAMやS3のように、複数のサービスにまたがるリソースを設定する場合、変更箇所が広がりすぎて、管理が複雑になっていったのです。
本章では、私たちが実際に直面した具体的な課題と、それに対する議論を通じて、AWSサービス単位のモジュール設計における限界を掘り下げていきます。
AWSサービス単位モジュールの限界
初期の設計では、各AWSサービス(S3、CloudFront、IAMなど)を独立したモジュールとして管理していました。このアプローチは、リソース間の依存関係が少ない段階では非常に効果的でした。しかし、次第にプロジェクトが成長し、機能が増えるにつれて、リソース間の依存関係が増加し、単一のモジュールに対して変更を加えるたびに、関連するモジュール全てを手動で修正しなければならないという問題が発生しました。
例えば、ある日、開発メンバーから以下のような依頼がありました:
💬『バックエンドAPIの特定のLambda関数に、新しいS3バケットへのアクセス権限を追加したいんですが、Terraformのどのファイルを修正すれば良いですか?変数とか追加する必要がありますか?』
変更箇所の広がりとその影響
この依頼に対して、私たちは以下の手順を踏まなければなりませんでした:
- IAMモジュール: 新しいIAMポリシーを追加し、Lambda関数に新しい権限を付与する必要がありました。
- modules/iam/variables.tf に新しいポリシー用の変数を追加。
- modules/iam/role_backend_api.tf にIAMポリシーを追加。
- S3モジュール: 新しいS3バケットを作成する必要がありました。
- modules/s3/variables.tf に新しいバケット名を追加。
- modules/s3/backend_api_bucket.tf でバケットの設定を追加。
- environments: 各環境(開発・ステージング・本番)の main.tf を修正。
- environments/prd/main.tf や environments/stg/main.tf で新しい変数を渡す必要があり、変更箇所が広がりました。
environments/
├─ prd
│ ├─ main.tf # 新たなIAMポリシーとS3バケットに関連する変数の追加
│ ├─ variables.tf
│ └─ terraform.tfvars
├─ stg
│ ├─ main.tf # 同様に変数追加が必要
│ ├─ variables.tf
│ └─ terraform.tfvars
└─ dev
├─ main.tf # 同様に変数追加が必要
├─ variables.tf
└─ terraform.tfvars
modules/
├─ iam
│ ├─ variables.tf # 新規変数の追加(新ポリシー用)
│ ├─ role_backend_api.tf # 新しいIAMポリシーを追加
│ └─ …
├─ s3
│ ├─ variables.tf # 新規変数の追加(新バケット名用)
│ ├─ backend_api_bucket.tf. # 新しいバケット設定
│ └─ …
└─ …
課題:変更箇所が散在している
上記のように、1つの変更に対して数箇所にわたるファイルの変更が必要でした。IAMモジュールとS3モジュールは、それぞれ独立して管理されていたため、変数の追加や値の受け渡しが複数のファイルに分散しました。
これにより、作業する際にどこを変更すべきかを確認するのが非常に手間であり、設定ミスや変更漏れが発生するリスクが高まりました。
特に、environmentsディレクトリ内の main.tf を修正する際、異なる環境ごとに同じ変数を追加する必要があり、手間が増しました。具体的には、 variables.tf を変更し、 main.tf でそれを呼び出して適用するプロセスが複雑化し、環境ごとの設定漏れや変更漏れが発生するリスクがありました。
チーム内の議論:どうすればより管理しやすくなるか?
この問題に対して、チーム内では次のような議論がありました:
- 複数のモジュールを一元管理する方法はないか: IAMやS3のように、複数のサービスで共有されるリソースが増えてきたため、これらを横断的に管理する方法が必要だと感じました。
- 変数の受け渡しが複雑すぎる: 各サービス単位でモジュールを作ることによって、変数の受け渡しが頻繁に必要になり、それに伴う変更作業が非常に煩雑でした。これにより、変数管理の一貫性を保つ方法を考える必要性が浮かび上がりました。
- 変更範囲が広すぎる: 1つの変更に対して、リソースごとに異なるモジュールを修正する必要があり、作業範囲の広さに対して効率的に対応できる方法が求められました。
これらの課題に対して、私たちは次のステップとしてモジュールの再構築を決定しました。
まとめ
AWSサービス単位でのモジュール設計は、シンプルで直感的な管理を可能にしましたが、リソース間の依存関係が複雑化するにつれて、変更作業が煩雑でミスが起きやすくなるという課題に直面しました。特に、IAMやS3のように複数のサービスにまたがるリソースを管理する際、変更箇所が多岐にわたり、どこを変更すべきかを正確に把握するのが難しくなりました。
次章では、これらの課題にどう対応したのか、そして機能単位のモジュール構成にリファクタリングした具体的な手順と成果を紹介します。
第3章:機能単位へのリファクタリング — チームの議論を経て決断したモジュール構成の変更と、その実際の道のり
前章で述べたAWSサービス単位のモジュール構成の限界に対処するために、私たちはモジュール構成のリファクタリングを決断し、機能単位でのモジュール管理に移行することにしました。この章では、私たちがどのようにしてモジュール構成を改善したか、そしてその結果、どのようなメリットを得ることができたのかを具体的な実例を交えて説明します。
課題に対する解決策の提案
最初に、私たちが直面した課題を再確認し、それに対する解決策をどのように提案したかを整理します。
課題1: モジュール間での変数受け渡しの煩雑さ
最も大きな課題は、モジュール間での変数の受け渡しが非常に煩雑であったことです。特に、IAMやS3など、複数のサービスにまたがるリソースが増えると、各リソースに対する変数をモジュール間で頻繁に渡さなければならず、管理の一貫性を保つのが難しくなっていました。
これに対する解決策として、私たちはモジュールを機能単位に整理することを提案しました。これにより、各機能に必要なリソースを一つのモジュール内に集約し、変数の受け渡しが単一モジュール内で完結するようにすることを目指しました。
課題2: 変更範囲が広すぎる
次に、1つの変更がリソースごとに異なるモジュールを修正する必要があったことです。例えば、バックエンド用のLambda関数に対して新しいポリシーを追加したり、S3バケットへのアクセス権を変更したりする場合、変更対象が複数のモジュールにわたるため、変更作業が非常に煩雑でした。
これに対して、私たちはモジュールを機能単位に分けることで、変更範囲を明確化し、変更を1つのモジュール内で完結させるようにしました。これにより、作業範囲の縮小とともに、レビュー負荷の軽減にも繋がりました。
課題3: state分割に対する懸念
さらに、リソースの増加に伴い、stateの分割も一時的に検討しました。しかし、リポジトリ単位でtfstateを分けることには、当初から複雑さを避けるべきという意見が強くありました。最初に取り組むべきは、モジュール単位での整理であると判断し、stateの分割は後回しにしました。
実施したリファクタリングの具体例
ディレクトリ構成の変更
最も大きな変更は、AWSサービス単位から機能単位へのモジュール分割です。これにより、各機能に必要なリソースをその機能に関連するモジュール内で管理することができ、リソース間の依存関係が明確になりました。
Before(AWSサービス単位モジュール管理):
modules/
├─ iam
├─ s3
├─ cloudfront
├─ elb
├─ ecs
├─ lambda
├─ rds
└─ ...
After(機能単位モジュール管理):
modules/
├─ components/
│ ├─ backend/
│ ├─ frontend/
│ └─ job/
├─ shared/
│ ├─ network/
│ └─ database/
│ └─ ...
└─ ...
この変更により、例えば、バックエンド機能に関連するIAMロールやLambdaの設定を modules/components/backend/ 内で管理できるようになり、変更範囲がその機能に絞られることで、作業が効率的に行えるようになりました。
変数受け渡しの簡素化
また、モジュールを機能単位に整理することで、各モジュール内での変数受け渡しが簡素化されました。特に、IAMロールやS3バケットのように、複数のリソースが相互に依存する場合でも、同じ機能内で変数を管理することで、他のモジュールとの依存関係が減少し、管理が非常にシンプルになりました。
Before:
module "iam" {
source = "../../modules/iam"
env = var.env
s3_bucket_arn = module.s3.backend_bucket_arn
... # 実際には十数個程度の変数受け渡しが発生(変更が煩雑化)
}
module "s3" {
source = "../../modules/s3"
env = var.env
... # 他モジュールとの多数の依存関係(メンテナンス性の低下)
}
module "ecs" { ... }
...
After:
# components(機能単位で整理し、モジュール間の受け渡しを最小限化)
module "backend" {
source = "../../modules/components/backend"
env = var.env # 実行環境を指定し、モジュール内のlocalsで環境差分を管理
region = var.region # リージョンは環境ごとに一定で、直接渡す
... # sharedモジュールなど、最低限の受け渡しのみ。その他の変数はモジュール内で完結
}
module "frontend" { ... } # 同様に受け渡しは最小限
module "job" { ... } # 同様に受け渡しは最小限
# shared(共通リソースのみを管理)
module "network" { ... }
module "database" { ... }
...
このように、機能単位でモジュールを整理することで、変数受け渡しの複雑さが解消され、リソース間で共有すべき変数の数が激減しました。
環境差分の管理方法の変化
また、環境ごとの差分を管理する方法も改善されました。locals.tf を活用して、環境ごとの設定をモジュール内で完結させることで、各環境における設定漏れが減少しました。
たとえば、以下は shared/database モジュールでの環境差分管理を示すサンプル例です。
locals.tfの例(shared/databaseモジュールの場合):
locals {
# shared/databaseモジュールにおける環境差分を定義
env_diff = {
# 開発環境:低コスト・柔軟な設定で運用
dev = {
instance_class = "<INSTANCE_CLASS_SMALL>"
backup_retention_period = <RETENTION_SHORT>
skip_final_snapshot = true
performance_insights = false
},
# 検証環境:本番に近い性能・設定を検証
stg = {
instance_class = "<INSTANCE_CLASS_MEDIUM>"
backup_retention_period = <RETENTION_MEDIUM>
skip_final_snapshot = false
performance_insights = true
},
# 本番環境:高性能・高可用性・厳格なセキュリティ
prd = {
instance_class = "<INSTANCE_CLASS_LARGE>"
backup_retention_period = <RETENTION_LONG>
skip_final_snapshot = false
performance_insights = true
}
}
# 現在の環境に基づいた設定を参照
current_env = local.env_diff[var.env]
}
上記のように、環境ごとのインスタンスクラスやバックアップ期間などの差分を一元管理しました。
これにより、設定がシンプルになり、環境ごとの差分によるミスや漏れが大幅に減少しました。
チーム内での反響と利点
リファクタリング後、チーム内では変更作業の効率化とレビュー負荷の軽減が実感され、ポジティブな反響がありました。特に、複数のリソースを横断して設定する必要がなくなり、作業の変更範囲が限定されたことで、レビューも迅速に行えるようになりました。
また、新規メンバーや初心者にとっても、モジュールの構造が明確になり、どこで何を変更すればよいかが直感的に理解できるようになりました。これにより、Terraformの学習コストが低減し、メンバー間での情報共有もスムーズになりました。
まとめ
私たちのチームは、AWSサービス単位でのモジュール管理から、機能単位でのモジュール管理に移行することで、以下のメリットを得ることができました:
- 変数の受け渡しが簡素化され、管理の一貫性が向上。
- 変更範囲が限定され、レビュー負荷が減少。
- 環境差分の管理が一元化され、設定漏れが減少。
このリファクタリングによって、インフラ管理がより効率的でスムーズになり、チーム全体の生産性が向上しました。次章では、これらの改善が実際の運用にどのように貢献したかについて、さらに掘り下げていきます。
第4章:リファクタリング後日談 — 数ヶ月運用してチームが実感した、機能単位モジュールによるリアルな改善
機能単位のモジュール構成へのリファクタリングを終えて、チーム全体に漠然とした期待と不安が入り混じっていました。特に「機能単位での整理がかえって煩雑になるのでは?」という懸念を完全に拭えないまま運用がスタートしました。しかし、実際に数ヶ月運用を続けた今、主観的に明らかな変化や改善が見えてきました。
変数管理が直感的になった
最も大きな改善は、変数の管理がシンプルで直感的になったことです。以前は、IAMやS3といった横断的に利用するサービスを変更する際、複数のモジュールを跨いで変数の追加や受け渡しが必要で、どこを修正すべきかの特定が難しいことが多くありました。
しかし、機能単位のモジュール構成に変更して以降、各機能内での変更がそのモジュールに閉じるようになり、変数の受け渡しが劇的に減少しました。例えば、バックエンドのLambda関数に新しいIAMポリシーを追加する場合も、関連する設定が modules/components/backend 内で完結するため、「どこに変数を追加するか」というストレスをほとんど感じなくなりました。
PRレビューの負担が減少した
また、プルリクエスト(PR)のレビュー作業が以前より格段に軽くなりました。AWSサービス単位でモジュールを管理していた頃は、1つの変更が複数モジュールにまたがってしまい、レビュー担当者も「どこまで影響範囲を追えば良いか」と慎重にならざるを得ませんでした。
しかし現在は、変更範囲が明確に特定の機能モジュール内に収まるため、レビュー担当者の心理的負荷が減少しました。そのため、チーム全体としてレビューのスピード感が向上し、変更やデプロイが以前よりも迅速に行えるようになりました。
環境差分管理が明確になり、安心感が生まれた
locals.tfを活用して環境差分をモジュール内で管理する方法を採用したことも、主観的な安心感につながっています。以前は環境ごとの設定差分を各環境のmain.tfに個別に記述する必要があり、設定漏れやミスに対して常に不安がつきまとっていました。
現在は、環境ごとの設定差分がlocals.tfで一元管理されているため、設定漏れが起きにくくなったと実感しています。「この設定はどこにあるか」という質問もチーム内で減り、自然と「設定は正しく反映されている」という安心感がチームに広がりました。
新規メンバーがTerraformに取り組みやすくなった
さらに、新しくチームに加わったメンバーがTerraformの運用にスムーズに入れるようになりました。以前の構成では、初学者や新規メンバーが設定変更をする際に戸惑うことが多く、ベテランメンバーへの質問が頻発していました。
しかし現在の機能単位のモジュール構成では、「どの機能のどのリソースを修正したいのか」が明確であり、新規メンバーでもすぐに作業を進められるようになりました。Terraformの学習コストが下がり、メンバー間での情報共有や引き継ぎも容易になったと感じています。
チーム文化へのポジティブな影響
今回のリファクタリングを通して、私が関わっているiPaletteの開発チームやSREチームでは、技術的な課題や運用上の負担を放置せず、常にチーム全体で改善策を議論し、積極的に取り組む姿勢が根付いていることを再認識しました。こうした取り組みは単にインフラ運用の負担を軽減するだけでなく、チームのメンバーが心理的に安心して働ける環境づくりにも貢献していると感じています。
特にSREとして日頃から開発チームと密接に連携し、『iPalette』の信頼性向上やインフラの最適化に取り組んできた中で、Terraformのモジュール構成というテーマを通じて、技術的な課題を柔軟に受け止めて改善していく文化が改めて浮き彫りになったように思います。こうした協力的な姿勢やオープンな議論はチーム全体の心理的安全性を高め、新しくチームに加わるメンバーに対しても、私たちがどのように課題に向き合い、協力して改善を進めるのかという具体的なイメージを伝えることに繋がると感じています。
次章では、こうした経験を通じて私たちが学んだ「Terraformのモジュール構成には絶対的な正解がない」という本質的なトレードオフについて、さらに掘り下げていきます。
第5章:絶対的な正解はない — Terraformモジュール構成の本質的なトレードオフと、私たちが悩み抜いて辿り着いた結論
これまで4章を通じて、私たちが直面した課題や実際の解決方法をリアルに共有してきました。Terraform導入初期にサービス単位モジュールを選んだ背景や成功体験、その限界から機能単位へのリファクタリング、そして実際の運用を通じて得た具体的なメリットについて詳しく振り返りました。
では、最終的に「サービス単位」と「機能単位」のどちらが良いのでしょうか?私たちのチームが実際に経験した結果として感じているのは、Terraformモジュール構成には『絶対的な正解』はなく、状況やチームのフェーズによって最適解は異なってくる、ということです。大切なのは、それぞれの選択肢にあるトレードオフを正しく理解し、自分たちの置かれた状況に照らして、最適だと思える構成を意識的に選んでいく姿勢ではないでしょうか。
Terraformモジュール構成の本質的なトレードオフ(整理)
ここで改めて、両方の構成方法の特徴を比較し、トレードオフを表に整理してみました。
| トレードオフの軸 | サービス単位 | 機能単位 |
|---|---|---|
| 再利用性 | ✅ 高い | ⚠️ 限定的 |
| 変更の影響範囲 | ⚠️ 広がりやすい | ✅ 限定的 |
| 可読性(文脈の理解) | ⚠️ 散逸しやすい | ✅ 直感的・高い |
| 管理コスト(変数の煩雑さ) | ❌ 煩雑化しやすい | ✅ 簡素化しやすい |
| 運用時の安定性 | ✅ 安定的 | ⚠️ 状況次第で肥大化 |
私たちのチームが初期にサービス単位を選んだのは、再利用性やシンプルさを追求した結果でした。実際、初期にはTerraformの学習曲線を低く抑えることができ、メンバー全員が迅速に運用に慣れました。しかし、徐々にリソース間の依存関係が複雑になるにつれ、管理が煩雑になり、機能単位への移行を決断しました。その結果、変数管理のシンプル化や変更範囲の明確化が進みました。
現実的なハイブリッド構成のすすめ
実際の運用現場では、どちらか一方に固執するのではなく、双方を組み合わせた『ハイブリッド構成』が現実的な解決策となります。
サービス単位モジュールで定義が適した例:
- ネットワーク基盤(VPC、Subnet、Route Table)
- データベース基盤(Auroraクラスタ、バックアップ設定)
- 証明書管理(ACM)
- 認証基盤(AWS SSO、Identity Provider)
- 通知基盤(SNSトピック、CloudWatchアラーム)
機能単位モジュールで定義が適した例:
- 各機能専用のLambda関数やECSクラスター
- 機能ごとのS3バケットやCloudFront
- 各機能固有のIAMロール、セキュリティグループ
- 機能専用のキューやイベントリソース(SQS、EventBridgeルールなど)
このハイブリッド構成により、変数の受け渡しを最小化し、影響範囲を明確化できます。実際、私たちのチームでは特にIAMロールやセキュリティグループを機能単位モジュール内にまとめることで、設定漏れや変更ミスが激減しました。
チーム規模・プロジェクトの成熟度別おすすめ構成
チームの規模やプロジェクトのフェーズによって最適な構成が変わります。具体的に整理すると、以下のようになります。
小規模チーム・初期プロジェクト
- サービス単位でシンプルにスタート。
- 機能単位モジュール化は限定的に導入。
- 成長に応じて柔軟に構成を見直す。
中規模以上・成熟期のプロジェクト(私たちの現状)
- 基盤的なサービスをサービス単位で、ビジネスロジックに近い部分を機能単位で整理。
- 共通モジュールはバージョニングを用いて管理。
- 環境差分はモジュール内のlocals.tfで一元管理。
大規模プロジェクト・さらなるスケールへの対応(未来の検討事項)
- 肥大化したstateの戦略的分割を実施(論理的境界に基づくリポジトリ構成とworkspace設計)
- モジュールの依存関係管理を体系化し、バージョニング規約の厳格化とドキュメント整備を推進
- インフラパイプラインの高度化(自動検証、セキュリティスキャン、コスト予測の統合)
- 組織全体のガバナンスフレームワーク構築(ポリシー遵守の自動検証メカニズム導入)
- 内部モジュールマーケットプレイスの確立(標準モジュールの一元管理と品質保証)
- さらなる抽象化や運用効率化の可能性を探索中…(モジュール設計の再整理、Terraform Cloudの活用、本当に必要な運用ポリシーの見直し、あるいはまだ私たちが気づいていない全く新しい手法など)
私たちが実践するTerraformモジュール構成ベストプラクティス
以下のポイントは私たちの実践から導いた『役に立つ』ベストプラクティスです。
- ✅ モジュールの責務を明確にする:一言で役割が説明できる単位で設計。
- ✅ 過度な汎用化を避ける:8割のケースをシンプルに実現するモジュールに。
- ✅ 環境差分の一元管理:locals.tfを活用して設定漏れを防止。
- ✅ モジュールのバージョニング管理:変更影響範囲をコントロール。
- ✅ ハイブリッド構成を採用する:基盤とビジネスロジックで役割を明確に分離。
- ✅ ドキュメント化を徹底する:新メンバーへのオンボーディングに役立てる。
まとめ
結局のところ、『Terraformモジュール構成には絶対的な正解はない』というのが私たちの辿り着いた結論です。重要なのは、自分たちが置かれた状況やチーム規模、プロジェクトの成熟度に応じて最適な構成を意識的に選ぶことです。
次章では、あなたのチームがモジュール構成を具体的にどう選択すれば良いか、実際に考えていただくための問いかけを用意しています。ぜひ引き続きお読みください。
おわりに:あなたのチームでは、Terraformのモジュール構成をどうしていますか?
ここまで私たちが直面した課題やその解決のために行った選択、そしてその背後にあるトレードオフについて詳しくお話ししてきました。私たちのチームがたどり着いた結論は、「Terraformのモジュール構成には絶対的な正解はない」というものです。
Terraformのモジュール構成を考えるということは、技術的なベストプラクティスだけではなく、チームの状況、プロジェクトの成熟度、そして将来的なスケールに対する考慮など、多くの要素を含んだ非常に奥深いテーマだと実感しています。
私たちは、シンプルさと再利用性を優先したサービス単位でのモジュールからスタートし、管理の複雑さや変更影響範囲の課題に直面し、機能単位へのリファクタリングを決断しました。その過程で得た最も大きな学びは、「自分たちがなぜその構成を選んだのか」という意識的な意思決定が非常に重要だということでした。
さて、あなたのチームでは、Terraformのモジュール構成をどのようにしていますか?
- これからTerraformを導入する段階でしょうか?それとも既に運用を始めていますか?
- サービス単位や機能単位、あるいは私たちが紹介したハイブリッド構成を選んでいますか?また、その選択をした背景や理由は何でしょうか?
- 現在の構成で課題や改善したいポイントはありますか?
このブログを通じて、あなたのチームがモジュール構成について深く考えるきっかけになれば嬉しいです。もしよろしければ、ぜひこの記事をあなたのチーム内で共有してみてください。Terraformのモジュール構成を議論することは、単なる技術的な課題解決を超え、チーム全体のコミュニケーションや協力関係を深めるきっかけにもなります。
Terraformのモジュール構成に「絶対的な正解」がないからこそ、私たち一人ひとりの現場の経験や知見が重要になります。
ぜひ、あなたのチームがどのような選択をし、どんなことを考えているのかを教えていただけると嬉しいです。
最後までお読みいただき、本当にありがとうございました。