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/

運用設計#

  • 環境ごとのアクセス権限分離
  • モジュールのバージョニング
  • コスト管理とタグ付け戦略
  • 定期的な監査とセキュリティレビュー