はじめに
こんにちは。某SaaS企業でPlatform Teamの一員として、主にTerraformモジュールの構築を担当している、もいもい(@moimoiblog)と申します。
突然ですが、皆さんの組織では、**「守りのインフラ統制」と「攻めの開発スピード」**のバランスをどう取っていますか?
私たちPlatform Teamの日常は、この二つの要求の板挟みです。
- 事業サイド: 「とにかく早く機能を出して市場で勝ちたい!すぐにこの構成が欲しい!」
- インフラ/セキュリティ: 「待ってくれ!その設定は脆弱だ。全社標準のこの構成を守ってくれ!」
モジュールビルダーとしての私の元には、日々このような相反するリクエストが舞い込んできます。片方を立てれば、もう片方から不満が噴出する。このジレンマこそ、Platform Engineeringにおける永遠の課題ではないでしょうか。
- 統制を強めれば… 開発者から「Platformはブロッカーだ」「遅すぎる!」という不満が生まれ、ルールを無視した"シャドーIT"が横行する。
- 自由を与えれば… プロダクトごとに設定がバラバラな"秘伝のタレ"が生まれ、セキュリティホールや障害の原因が潜む無法地帯(Wild West)と化す。
この永遠とも思える課題に対し、試行錯誤の末に私たちがたどり着いたのが、**「ガードレールのある高速道路」**という思想です。つまり、「安全な範囲からはみ出すことはできないが、その道の上では誰でも猛スピードで走れる」状態を目指しました。
そして、その思想をTerraformで実現したのが、今回ご紹介する3層レイヤーモデルです。
この記事では、インフラのガバナンスと開発チームの自律性を両立させ、安全性を担保しながらも開発スピードを最大化するための具体的な設計思想と、コードレベルでの実装方法について共有します。
この記事は、以下のような方に特に読んでいただきたいです。
- Platform Engineerとして、開発者体験とガバナンスの両立に悩んでいる方
- Tech LeadやEngineering Managerとして、チームの生産性を上げたい方
- Terraformで大規模なインフラをどう管理すべきか、ベストプラクティスを探している方
「インフラの門番(ゲートキーパー)」から「開発の加速装置(アクセラレーター)」へと役割を進化させたい、全てのエンジニアに捧げます。
課題:サイロ化が生んだ「部分最適」の壁 😩
かつての「プロダクトごとの専属チーム体制」は、担当領域に集中できるメリットはあったものの、組織全体で見ると深刻な非効率性とガバナンスの問題を抱えていました。
1. プロダクトごとに行われる「車輪の再発明」
最も大きな課題は、プロダクトの数だけ「車輪の再発明」が行われていたことです。
プロダクトAのインフラチームがECSのCI/CDパイプラインを苦労して構築しても、その知見がプロダクトBに活かされることはありません。プロダクトBのチームもまた、同じようなパイプラインをゼロから構築していました。全社で見れば、同じ課題を解決するためにN倍のコストを支払っている状態でした。
2. 「村ごと」に違う、セキュリティ基準の解釈
全社的なセキュリティガイドラインは存在しました。しかし、それをどう解釈し、どう実装するかは各プロダクトのインフラチームに委ねられていました。
その結果、「村ごとにルールが違う」状態が生まれます。
- Aチームは、ALBの最新セキュリティポリシーを厳格に適用。
- Bチームは、DBの暗号化を徹底しているが、ポリシーの追従は少し遅れ気味。
- Cチームは、S3の暗号化は完璧だが…。
このように、善意で構築していても、プロダクトごとにセキュリティレベルに「意図しない濃淡」が生まれてしまっていました。
3. 全体最適を阻む「部分最適」のジレンマ
各チームは、自分たちのプロダクトにとっては最適な(=部分最適な)インフラを構築します。しかし、それが組織全体にとって最適(=全体最適)とは限りませんでした。
後から全社横断で「全てのS3バケットの暗号化を有効にしよう」という号令がかかっても、各プロダクトで実装方法が異なるため、修正コストは膨大になります。チームごとの独立性が、逆に組織全体の変化を妨げるブレーキとなっていました。
プロダクトの独立性は高いものの、組織全体としてスケールできない。
そんなジレンマに、私たちは陥っていました。
解決策:Terraform 3層レイヤーモデルという設計思想
この課題を解決するため、私たちはTerraformの構成を以下の3つのレイヤーに分けて管理するモデルを導入しました。
⚠️ おことわり
これから紹介するディレクトリ構造やコードは、あくまで私たちの思想を伝えるための**サンプル(スターターキット)**です。
細かいリソースのパラメータや命名規則などは、ぜひ皆さんの組織のルールに合わせて最適化してくださいね。
第1層: プラットフォーム基盤 (Platform Foundation)
-
責任者:
Platform Team -
リポジトリ:
platform-terraform-modules - 役割: 全社的なガバナンス、セキュリティ、標準化を担保するレイヤーです。私たちが「安全な高速道路」を用意します。
- 思想: Terraform Registryなどで公開されている汎用的なモジュールを直接は使わせず、私たちの統制を組み込んだ薄いラッパーモジュールを提供します。
第1層のディレクトリ構造
```
platform-terraform-modules/
└── modules/
├── alb/
├── ecs_service/
├── iam_role/
├── rds_aurora/
├── s3/
└── ... (その他、全社で利用する標準モジュール)
```
【第1層の具体例】S3モジュールにおける統制
例えば、私たちのs3モジュールは、コミュニティのterraform-aws-modules/s3-bucketをラップしています。これにより、プロダクトチームに柔軟性を提供しつつ、必須のセキュリティ設定を強制できます。
```terraform:modules/s3/main.tf
module "s3_bucket" {
source = "terraform-aws-modules/s3-bucket/aws"
version = "~> 5.4"
# ----------------------------------------------------------------
# services層から渡される、プロジェクト固有の「定義」 (柔軟性)
# ----------------------------------------------------------------
bucket = var.bucket_name
tags = var.tags
versioning = {
enabled = var.versioning_enabled
}
lifecycle_rule = var.lifecycle_rules
# ----------------------------------------------------------------
# このモジュールが強制する、全社共通の「標準化」 (統制)
# ----------------------------------------------------------------
# パブリックアクセスを完全にブロック
block_public_acls = true
block_public_policy = true
ignore_public_acls = true
restrict_public_buckets = true
# サーバーサイド暗号化 (AES256) を強制
server_side_encryption_configuration = {
rule = {
apply_server_side_encryption_by_default = {
sse_algorithm = "AES256"
}
}
}
}
```
このように、プロダクトチームは意識しなくても自動的に安全な設定が適用されます。
第2層・第3層: プロダクト固有構成
-
責任者:
Product Team - リポジトリ: 各プロダクトのアプリケーションリポジトリ
- 役割: 第1層の部品を使い、ビジネスロジックの実装とデリバリースピードに責任を持ちます。
第2層・第3層のディレクトリ構造
各プロダクトのリポジトリは、以下のような標準的なディレクトリ構造を持ちます。
```
product-a-repo/
├── envs/ # <-- 第3層: 環境ごとの構成
│ └── temp/
│ ├── main.tf
│ └── ...
└── services/ # <-- 第2層: サービス・テンプレート
├── api/
│ ├── compute.tf
│ └── ...
├── batch/
│ ├── compute.tf
│ └── ...
└── common/
├── rds.tf
└── ...
```
呼び出し関係のイメージ
-
第3層 (
envs/temp/main.tf)- 第2層のサービスモジュールを呼び出し、環境固有の値(インスタンス数など)を渡します。
# envs/temp/main.tf module "api" { # このリポジトリ内の第2層モジュールを指定 source = "../../services/api" # 環境固有の値を渡す ecs_desired_count = 3 } -
第2層 (
services/api/compute.tf)- 第1層のプラットフォームモジュールを呼び出し、サービス固有のロジックを記述します。
# services/api/compute.tf module "ecs_service" { # 第1層の共有モジュールをGit経由で指定 source = "git::[github.com/your-org/platform-terraform-modules.git//modules/ecs_service?ref=v1.0.0](https://github.com/your-org/platform-terraform-modules.git//modules/ecs_service?ref=v1.0.0)" # サービス固有の値を設定 service_name = "api" container_port = 8080 health_check_path = "/health" desired_count = var.ecs_desired_count # 第3層から来た値をパススルー }
設計の勘所:なぜ私たちは「管理された冗長性」を許容するのか
ここからは、私たちの設計思想の最も重要な核心部分についてお話しします。
プロダクトごとに第2層・第3層のコードがコピーされ、冗長性が生まれる。私たちはこれを単なるデメリットではなく、**「管理された冗長性(Managed Redundancy)」**として、意図的に選択しています。
雛形は「フォーク」してもらうためのもの
私たちが提供するテンプレートリポジトリは、各チームに「フォーク」してもらうためのものです。一度コピーされたコードの所有権は、完全にプロダクトチームに移ります。
これにより、各チームは自分たちのプロダクトにとって最適な形に、第2層のサービスモジュールを自由に進化させることができます。
- あるプロダクトでは、
apiモジュールに特殊な監視設定を追加する。 - 別のプロダクトでは、不要なリソースを削ってモジュールを軽量化する。
こうしたプロダクトごとの最適化を、私たちは歓迎します。なぜなら、ビジネスの最前線にいる彼らこそが、そのプロダクトに必要なものを最もよく知っているからです。
では、どうやって統制を維持するのか? → 第1層が「契約」だからです
「各チームが自由にコードを変えられるなら、結局また統制が取れなくなるのでは?」
その懸念が生まれるのは当然です。しかし、問題ありません。なぜなら、第2層がどんなに独自の進化を遂げても、コアなリソースを作成する際には、必ず私たちが管理する第1層のプラットフォームモジュールを呼び出す、という「契約」があるからです。
-
Product Team(第2, 3層)の自由:
- 第1層の部品(
s3,iam_role,ecs_service...)をどのように組み合わせるかは自由です。
- 第1層の部品(
-
Platform Team(第1層)の統制:
- その部品一つ一つには、必須のセキュリティ設定や標準化が組み込まれています。
例えば、プロダクトチームがどんなに複雑なアプリケーションを作ろうとも、S3バケットを作る際には私たちのs3モジュールを使わざるを得ません。そしてそのモジュールを使えば、暗号化やパブリックアクセス禁止といったガードレールから逸脱することはできないのです。
まとめ:私たちは「門番」ではなく、「レゴブロックの製造者」でありたい
この3層レイヤーモデルを導入したことで、私たちはガバナンスと自律性というトレードオフを解消することができました。
統制 (Governance) + 裁量 (Autonomy) = 開発速度 (Velocity)
私たちの役割は、開発チームの前に立ちはだかり「あれはダメ、これもダメ」と制限する「門番(ゲートキーパー)」ではありません。彼らが思い描く素晴らしいプロダクトを自由に、そして安全に組み立てられるよう、最高品質の「レゴブロック(=第1層モジュール)」を供給する製造者でありたいと考えています。
- Platform Teamの約束: 私たちは、安全で、組み合わせやすく、信頼性の高いレゴブロック(第1層モジュール)を常に最新の状態で豊富に用意し、供給し続けます。
- Product Teamへの信頼: 皆さんには、供給されたブロックを最大限に活用し、素晴らしいお城や宇宙船(=プロダクト)を自由に創造してもらうことを期待しています。
この高品質な部品(統制)と、創造的な自由(自律性)の上に成り立つ信頼関係こそが、強い開発組織を作ると信じています。
この記事が、同じような課題を抱える方々の参考になれば幸いです。
