1141 Words
6 Minutes
Terraformでマルチ環境戦略を実現する
1: 基本構成の解説
Terraformでインフラを構築する際、環境分離とコードの再利用性は重要な課題です。今回は、環境ごとに独立したディレクトリを持ち、共通のモジュールを利用する構成を紹介します。
リポジトリ構造
├── .github
│ ├── dependabot.yml
│ └── workflows
│ └── validate-terraform.yml
├── .gitignore
├── Makefile
├── README.md
├── envs
│ ├── production
│ │ ├── .terraform-version
│ │ ├── backend.tf
│ │ ├── locals.tf
│ │ ├── main.tf
│ │ ├── outputs.tf
│ │ ├── providers.tf
│ │ └── variables.tf
│ └── staging
│ ├── .terraform-version
│ ├── backend.tf
│ ├── locals.tf
│ ├── main.tf
│ ├── outputs.tf
│ ├── providers.tf
│ └── variables.tf
└── modules
├── .gitkeep
└── aws
├── cloudwatch
│ ├── main.tf
│ ├── outputs.tf
│ └── variables.tf
├── erc
│ ├── main.tf
│ ├── outputs.tf
│ └── variables.tf
├── iam
│ ├── main.tf
│ ├── outputs.tf
│ └── variables.tf
└── lambda
├── main.tf
├── outputs.tf
└── variables.tfなぜこの構成なのか
1. 環境の完全分離
各環境(staging/production)が独立したディレクトリを持つことで:
- 環境固有の設定を明確に管理
- 誤って本番環境に影響を与えるリスクを低減
.tfstateファイルの分離が容易
2. モジュール化のメリット
modules/配下に共通コンポーネントを配置:
- コードの再利用性向上
- 設定の一貫性担保
- バグ修正や機能追加の効率化
3. terraform workspaceとの比較
workspaceを使用しない理由:
- 条件分岐による複雑化を回避
- 環境ごとの差分が明確
- 権限管理が容易
実装例
# envs/production/main.tf
module "cloudwatch" {
source = "../../modules/aws/cloudwatch"
environment = "production"
alert_threshold = 90
}
# envs/staging/main.tf
module "cloudwatch" {
source = "../../modules/aws/cloudwatch"
environment = "staging"
alert_threshold = 70
}ディレクトリの役割
envs/
- 環境固有の設定を管理
- backend.tfで状態管理を設定
- variables.tfで環境変数を定義
- locals.tfで派生値を計算
modules/
- 再利用可能なコンポーネントを定義
- インターフェースを明確に定義
- 柔軟な設定オプションを提供
2: モジュール設計のベストプラクティス
モジュールの設計原則
単一責任の原則
- 各モジュールは明確な1つの役割を持つ
- 例:
cloudwatchモジュールはメトリクスとアラートのみを担当
入出力の設計
# modules/aws/cloudwatch/variables.tf
variable "environment" {
type = string
description = "Environment name (e.g. staging, production)"
}
variable "alert_thresholds" {
type = object({
cpu = number
memory = number
})
description = "Alert thresholds for resources"
}
# modules/aws/cloudwatch/outputs.tf
output "alarm_arns" {
value = aws_cloudwatch_metric_alarm.*.arn
description = "ARNs of created CloudWatch alarms"
}実装例:CloudWatchモジュール
# modules/aws/cloudwatch/main.tf
resource "aws_cloudwatch_metric_alarm" "cpu_alarm" {
alarm_name = "${var.environment}-cpu-alarm"
comparison_operator = "GreaterThanThreshold"
threshold = var.alert_thresholds.cpu
metric_name = "CPUUtilization"
namespace = "AWS/EC2"
dimensions = var.dimensions
tags = {
Environment = var.environment
}
}モジュールの粒度
インフラコンポーネント別
- cloudwatch: メトリクス・アラート管理
- iam: 権限管理
- lambda: サーバーレス関数
- erc: コンテナ実行環境
共通設定の共有
# modules/aws/common/variables.tf
locals {
common_tags = {
Environment = var.environment
ManagedBy = "terraform"
}
naming_prefix = "${var.project}-${var.environment}"
}バージョン管理戦略
- Git tagによるバージョニング
- 後方互換性の維持
- CHANGELOGの管理
3: 環境別設定の実装
envs配下のファイル構成
envs/
├── production/
│ ├── backend.tf # S3バックエンド設定
│ ├── locals.tf # 環境固有の計算値
│ ├── main.tf # モジュール呼び出し
│ ├── outputs.tf # 出力値定義
│ ├── providers.tf# プロバイダー設定
│ └── variables.tf# 入力変数
└── staging/
└── ...backend.tfの環境分離
# envs/production/backend.tf
terraform {
backend "s3" {
bucket = "my-terraform-production-state"
key = "terraform.tfstate"
region = "ap-northeast-1"
}
}環境変数の管理
# envs/production/variables.tf
variable "aws_region" {
default = "ap-northeast-1"
}
variable "instance_types" {
default = {
app = "t3.medium"
db = "t3.large"
}
}
# envs/production/locals.tf
locals {
resource_prefix = "prod"
capacity = {
min = 2
max = 10
}
}モジュール呼び出しの差分
# envs/production/main.tf
module "lambda" {
source = "../../modules/aws/lambda"
memory_size = 1024
timeout = 30
}
# envs/staging/main.tf
module "lambda" {
source = "../../modules/aws/lambda"
memory_size = 512
timeout = 10
}4: CI/CDパイプラインの構築
GitHub Actionsの設定
# .github/workflows/validate-terraform-fmt.yml
name: "Validate Terraform Format"
on:
pull_request:
paths:
- 'envs/**/*.tf'
- 'modules/**/*.tf'
- '.github/workflows/validate-terraform-fmt.yml'
push:
branches:
- main
paths:
- 'envs/**/*.tf'
- 'modules/**/*.tf'
jobs:
validate-terraform-fmt:
name: "Validate Terraform Format"
runs-on: ubuntu-latest
defaults:
run:
shell: bash
strategy:
matrix:
environment: [staging, production]
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Terraform
uses: hashicorp/setup-terraform@v3
with:
terraform_version: 1.10.5
- name: Terraform Format
id: fmt
run: |
cd envs/${{ matrix.environment }}
terraform fmt -check
continue-on-error: true
Makefileの活用
.PHONY: init plan apply
init:
cd envs/$(ENV) && terraform init
plan:
cd envs/$(ENV) && terraform plan
apply:
cd envs/$(ENV) && terraform apply
fmt:
terraform fmt -recursive依存関係管理
# .github/dependabot.yml
version: 2
updates:
- package-ecosystem: "terraform"
directory: "/modules"
schedule:
interval: "weekly"5: 実践的なTips
環境の追加手順
# 新環境(dev)の追加例
cp -r envs/staging envs/dev
cd envs/dev
# backend.tf, variables.tf, locals.tfの環境固有値を更新バージョン管理
# .terraform-version
1.5.7
# providers.tf
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
}
required_version = "~> 1.5.0"
}セキュリティプラクティス
# sensitive情報の管理
variable "database_password" {
type = string
sensitive = true
}
# IAMの最小権限設定
module "iam" {
source = "../../modules/aws/iam"
permissions = ["s3:GetObject", "s3:PutObject"]
resources = [aws_s3_bucket.app.arn]
}ケーススタディ
大規模プロジェクトでの実例
project/
├── envs/
│ ├── production/
│ ├── staging/
│ └── dev/
└── modules/
├── aws/
│ ├── network/
│ ├── compute/
│ └── database/
└── gcp/
├── network/
└── compute/運用設計
- 環境ごとのアクセス権限分離
- モジュールのバージョニング
- コスト管理とタグ付け戦略
- 定期的な監査とセキュリティレビュー