【AWSハンズオン】第13回 本番運用とスケーリング

【AWSハンズオン】第13回 本番運用とスケーリング

Terraformのモジュール化と環境別管理により、開発・ステージング・本番環境を効率的に管理する方法、Auto Scalingによる自動スケーリングとRDS Performance Insightsを活用した監視体制も構築します。

AWS #AWS#API#ハンズオン

【AWSハンズオン】第13回 本番運用とスケーリング

サムネイル

Terraformのモジュール化と環境別管理により、開発・ステージング・本番環境を効率的に管理する方法、Auto Scalingによる自動スケーリングとRDS Performance Insightsを活用した監視体制も構築します。

更新日: 8/13/2025

今回作業対象のブランチ

前提条件

  • 第1回から第12回までの構築が完了していること
  • 複数環境(dev/staging/prod)の管理を想定していること
  • こちらからDockerコンテナでの実行環境が準備できていること

Terraform実行環境の起動

前回と同様に、Dockerコンテナ内で作業を進めます。

# コンテナを起動してbashに入る
docker-compose run --rm terraform

これ以降のコマンドは、すべてこのコンテナ内で実行します。

基礎知識

環境別管理

本番環境では、開発環境と異なる設定が必要になります。

環境 特徴 設定例
開発(dev) コスト優先、頻繁な変更 t3.micro、Single-AZ、最小タスク数
ステージング(stg) 本番に近い構成 t3.small、Multi-AZ、中規模
本番(prod) 可用性・性能優先 m5.large、Multi-AZ、Auto Scaling

Auto Scalingの仕組み

Auto Scalingは、負荷に応じてリソースを自動的に増減させる機能です。

WEBアプリ開発者の視点から見ると

ローカル開発では1つのサーバープロセスで処理しますが、本番環境では負荷に応じて複数のプロセスが必要になります。Auto Scalingは、CPU使用率やメモリ使用率を監視し、閾値を超えたら自動的にサーバーを追加、負荷が下がったら削減します。

スケーリングタイプ 説明 適用例
Target Tracking 指定メトリクスを目標値に維持 CPU使用率を50%に維持
Step Scaling 段階的にスケール 70%で+1、90%で+2タスク
Scheduled Scaling 時間帯でスケール 営業時間中は多め、夜間は少なめ

今回作成するリソース

リソース 説明 数量
Terraformモジュール VPC、ECS、RDS等の再利用可能モジュール 複数
環境別設定ファイル dev/stg/prod用tfvars 3個
ECS Auto Scaling CPU/メモリベースの自動スケーリング 1個
Application Auto Scaling Target スケーリング対象の定義 1個
Target Tracking Scaling Policy スケーリングポリシー 2個
RDS Performance Insights データベース性能監視 1個
CloudWatch Dashboards 環境別ダッシュボード 環境数分

プロジェクト構成

以下のような構成でTerraformの環境別&モジュール化を実装します。

terraform/
├── environments/              # 環境別設定
│   ├── dev/
│   │   ├── main.tf            # モジュール呼び出し
│   │   ├── variables.tf       # 環境固有変数定義
│   │   ├── terraform.tfvars   # 環境固有値
│   │   └── backend.hcl        # S3バックエンド設定
│   ├── stg/
│   └── prod/
└── modules/                   # 再利用可能モジュール
    ├── network/               # VPC・サブネット
    ├── security/              # セキュリティグループ
    ├── compute/               # ECS・Auto Scaling
    ├── database/              # RDS・Performance Insights
    ├── storage/               # S3
    ├── loadbalancer/          # ALB
    ├── cdn/                   # CloudFront
    ├── monitoring/            # CloudWatch
    ├── cicd/                  # GitHub Actions
    └── vpc_endpoints/         # VPCエンドポイント

モジュールの基本構造

各モジュールは以下の3つのファイルで構成されます。

modules/[モジュール名]/
├── main.tf           # リソース定義
├── variables.tf      # 入力変数
└── outputs.tf        # 出力値

実装の考え方

原則 説明 実装例
単一責任 1モジュール1責務 networkモジュールはVPC関連のみ
疎結合 依存を最小化 出力値を通じて連携
環境非依存 環境固有値は外部から注入 変数で環境差分を吸収
条件分岐 機能の有効/無効を制御 count/for_eachで条件付きリソース作成

環境別設定の実装

開発環境

# environments/dev/terraform.tfvars

# プロジェクト設定
# project_name = "" 環境変数(TF_VAR_project_name)で設定
# environment = "dev" 

# 環境別機能有効化設定
enable_rds         = true   # RDSデータベースを有効化(学習目的)
enable_nat_gateway = false  # NAT Gatewayを無効化(コスト削減)

# セキュリティ・監視
enable_waf                  = false # WAFを無効化(開発環境)
enable_vpc_endpoints        = true  # VPCエンドポイントを有効化(NAT Gateway無効のため必須)
enable_vpc_flow_logs        = false # VPC Flow Logsを無効化(コスト削減)
enable_alb_access_logs      = false # ALBアクセスログを無効化(コスト削減)
enable_xray_tracing         = false # X-Rayトレーシングを無効化(コスト削減)
enable_cloudfront_logging   = false # CloudFrontログを無効化(コスト削減)

# アラーム・監視
enable_cpu_alarm            = false # CPUアラームを無効化(開発環境)
enable_memory_alarm         = false # メモリアラームを無効化(開発環境)
enable_unhealthy_host_alarm = false # 異常ホストアラームを無効化(開発環境)

# その他の環境固有設定
allowed_origins = [] # CORS許可オリジン(実際の値を設定)

# SSL証明書(オプション)
alb_certificate_arn      = ""
frontend_certificate_arn = ""
frontend_domain_name     = ""

環境別 main.tf

environments/dev/main.tfを例に、モジュールを呼び出し環境固有の設定を適用します。

terraform {
  required_version = ">= 1.5"
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.0"
    }
  }

  backend "s3" {
    # backend.hclで設定
  }
}

provider "aws" {
  region = var.aws_region

  default_tags {
    tags = {
      Environment = var.environment
      Project     = var.project_name
      ManagedBy   = "terraform"
    }
  }
}

# 共通タグの定義
locals {
  common_tags = {
    Project     = var.project_name
    Environment = var.environment
    ManagedBy   = "terraform"
  }
}

# Network Module
module "network" {
  source = "../../modules/network"

  project_name       = var.project_name
  environment        = var.environment
  vpc_cidr           = var.vpc_cidr
  enable_nat_gateway = var.enable_nat_gateway
}

# Security Module
module "security" {
  source = "../../modules/security"

  project_name         = var.project_name
  environment          = var.environment
  vpc_id               = module.network.vpc_id
  vpc_cidr_block       = var.vpc_cidr
  enable_vpc_endpoints = false
}

# Database Module(開発環境では条件付き)
module "database" {
  count  = var.enable_rds ? 1 : 0 # RDS有効化変数で制御
  source = "../../modules/database"

  project_name          = var.project_name
  environment           = var.environment
  private_subnet_ids    = module.network.private_subnet_ids
  rds_security_group_id = module.security.database_security_group_id

  db_engine_version           = "15.8"
  db_instance_class           = "db.t3.micro"
  db_allocated_storage        = 20
  db_username                 = "postgres"
  multi_az                    = false
  backup_retention_period     = 1
  enable_performance_insights = false
  enable_rds_proxy            = false
}

# Compute Module
module "compute" {
  source = "../../modules/compute"

  project_name = var.project_name
  environment  = var.environment
  aws_region   = var.aws_region

  task_cpu       = "256"
  task_memory    = "512"
  desired_count  = 1
  container_port = 3000

  min_capacity = 1
  max_capacity = 2

  private_subnet_ids    = module.network.private_subnet_ids
  ecs_security_group_id = module.security.ecs_security_group_id
  target_group_arn      = module.loadbalancer.target_group_arn
  db_secret_arn         = var.enable_rds && length(module.database) > 0 ? module.database[0].db_secret_arn : ""

  allowed_origins         = var.allowed_origins
  enable_security_headers = var.enable_security_headers
  enable_xray_tracing     = var.enable_xray_tracing
}

Auto Scalingの実装

Auto Scaling(computeモジュール)

# modules/compute/main.tf

# ECSサービス(既存のリソース)
resource "aws_ecs_service" "app" {
  name            = "${var.project_name}-${var.environment}-app-service"
  cluster         = aws_ecs_cluster.main.id
  task_definition = aws_ecs_task_definition.app.arn
  desired_count   = var.desired_count
  launch_type     = "FARGATE"
  
  # 以下、既存の設定...
}

# Auto Scaling Target
resource "aws_appautoscaling_target" "ecs" {
  max_capacity       = var.max_capacity
  min_capacity       = var.min_capacity
  resource_id        = "service/${aws_ecs_cluster.main.name}/${aws_ecs_service.app.name}"
  scalable_dimension = "ecs:service:DesiredCount"
  service_namespace  = "ecs"

  depends_on = [aws_ecs_service.app]
}

# CPU使用率によるスケーリングポリシー
resource "aws_appautoscaling_policy" "ecs_cpu" {
  name               = "${var.project_name}-${var.environment}-cpu-scaling"
  policy_type        = "TargetTrackingScaling"
  resource_id        = aws_appautoscaling_target.ecs.resource_id
  scalable_dimension = aws_appautoscaling_target.ecs.scalable_dimension
  service_namespace  = aws_appautoscaling_target.ecs.service_namespace

  target_tracking_scaling_policy_configuration {
    predefined_metric_specification {
      predefined_metric_type = "ECSServiceAverageCPUUtilization"
    }
    
    target_value       = var.cpu_target_value
    scale_in_cooldown  = var.scale_in_cooldown
    scale_out_cooldown = var.scale_out_cooldown
  }
}

# メモリ使用率によるスケーリングポリシー
resource "aws_appautoscaling_policy" "ecs_memory" {
  name               = "${var.project_name}-${var.environment}-memory-scaling"
  policy_type        = "TargetTrackingScaling"
  resource_id        = aws_appautoscaling_target.ecs.resource_id
  scalable_dimension = aws_appautoscaling_target.ecs.scalable_dimension
  service_namespace  = aws_appautoscaling_target.ecs.service_namespace

  target_tracking_scaling_policy_configuration {
    predefined_metric_specification {
      predefined_metric_type = "ECSServiceAverageMemoryUtilization"
    }
    
    target_value       = var.memory_target_value
    scale_in_cooldown  = var.scale_in_cooldown
    scale_out_cooldown = var.scale_out_cooldown
  }
}

スケジュールベースのスケーリング

# modules/compute/scheduled_scaling.tf

# 営業時間中のスケールアップ
resource "aws_appautoscaling_scheduled_action" "scale_up_morning" {
  count = var.enable_scheduled_scaling ? 1 : 0

  name               = "${var.project_name}-${var.environment}-scale-up-morning"
  service_namespace  = aws_appautoscaling_target.ecs.service_namespace
  resource_id        = aws_appautoscaling_target.ecs.resource_id
  scalable_dimension = aws_appautoscaling_target.ecs.scalable_dimension
  schedule           = "cron(0 0 * * MON-FRI *)"  # 平日9時(JST)
  timezone          = "Asia/Tokyo"

  scalable_target_action {
    min_capacity = var.business_hours_min_capacity
    max_capacity = var.business_hours_max_capacity
  }
}

# 営業時間外のスケールダウン
resource "aws_appautoscaling_scheduled_action" "scale_down_evening" {
  count = var.enable_scheduled_scaling ? 1 : 0

  name               = "${var.project_name}-${var.environment}-scale-down-evening"
  service_namespace  = aws_appautoscaling_target.ecs.service_namespace
  resource_id        = aws_appautoscaling_target.ecs.resource_id
  scalable_dimension = aws_appautoscaling_target.ecs.scalable_dimension
  schedule           = "cron(0 10 * * MON-FRI *)"  # 平日19時(JST)
  timezone          = "Asia/Tokyo"

  scalable_target_action {
    min_capacity = var.off_hours_min_capacity
    max_capacity = var.off_hours_max_capacity
  }
}

Auto Scalingパラメータ

パラメータ 説明 設定内容 環境別推奨値
min_capacity number 最小タスク数 常時稼働させる最小数 dev:1、stg:2、prod:3
max_capacity number 最大タスク数 スケールアウトの上限 dev:2、stg:4、prod:10
cpu_target_value number CPU使用率の目標値(%) 平均CPU使用率をこの値に維持 50
memory_target_value number メモリ使用率の目標値(%) 平均メモリ使用率をこの値に維持 70
scale_in_cooldown number スケールイン後の待機時間(秒) 頻繁なスケール変更を防止 300
scale_out_cooldown number スケールアウト後の待機時間(秒) 急激な負荷増加への対応 60

RDS Performance Insightsの実装

Performance Insights(databaseモジュール)

# modules/database/main.tf

# Performance Insights用KMSキー
resource "aws_kms_key" "rds" {
  count = var.enable_performance_insights ? 1 : 0

  description             = "${var.project_name}-${var.environment}-rds-pi-key"
  deletion_window_in_days = 10

  tags = {
    Name = "${var.project_name}-${var.environment}-rds-pi-key"
  }
}

# Enhanced Monitoring用IAMロール
resource "aws_iam_role" "rds_monitoring" {
  count = var.enable_performance_insights ? 1 : 0

  name = "${var.project_name}-${var.environment}-rds-monitoring"

  assume_role_policy = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        Action = "sts:AssumeRole"
        Effect = "Allow"
        Principal = {
          Service = "monitoring.rds.amazonaws.com"
        }
      }
    ]
  })
}

resource "aws_iam_role_policy_attachment" "rds_monitoring" {
  count = var.enable_performance_insights ? 1 : 0

  role       = aws_iam_role.rds_monitoring[0].name
  policy_arn = "arn:aws:iam::aws:policy/service-role/AmazonRDSEnhancedMonitoringRole"
}

# RDSインスタンス(Performance Insights設定含む)
resource "aws_db_instance" "main" {
  identifier = "${var.project_name}-${var.environment}-db"
  
  # 既存の設定...
  
  # Performance Insights設定
  enabled_cloudwatch_logs_exports       = ["postgresql"]
  performance_insights_enabled          = var.enable_performance_insights
  performance_insights_retention_period = var.enable_performance_insights ? var.performance_insights_retention : null
  performance_insights_kms_key_id      = var.enable_performance_insights ? aws_kms_key.rds[0].arn : null

  # Enhanced Monitoring
  monitoring_interval = var.enable_performance_insights ? 60 : 0
  monitoring_role_arn = var.enable_performance_insights ? aws_iam_role.rds_monitoring[0].arn : null
  
  # その他の設定
  skip_final_snapshot = var.skip_final_snapshot
  deletion_protection = var.enable_deletion_protection
}

# DB Parameter Group(Performance Insights用設定含む)
resource "aws_db_parameter_group" "main" {
  name   = "${var.project_name}-${var.environment}-pg15"
  family = "postgres15"

  parameter {
    name  = "shared_preload_libraries"
    value = "pg_stat_statements"
  }

  parameter {
    name  = "log_statement"
    value = var.enable_performance_insights ? "all" : "none"
  }

  parameter {
    name  = "log_min_duration_statement"
    value = var.enable_performance_insights ? "1000" : "-1"  # 1秒以上のクエリをログ出力
  }
}

Performance Insightsパラメータ

パラメータ 説明 設定内容 環境別推奨値
performance_insights_enabled bool Performance Insights有効化 SQLの実行時間とDB負荷を可視化 dev:false、stg/prod:true
performance_insights_retention_period number データ保持期間(日) 分析データの保存期間 7(無料)または731
monitoring_interval number メトリクス収集間隔(秒) Enhanced Monitoring頻度 0(無効)または60
log_statement string SQLログ記録レベル 実行されるSQL文のログ記録 dev:“all”、prod:“none”
log_min_duration_statement number スロークエリ閾値(ミリ秒) この時間以上のクエリをログ記録 1000(1秒)

リソースの作成

環境別にTerraformを実行します。

# 開発環境
cd terraform/environments/dev
terraform init -backend-config=backend.hcl
terraform plan
terraform apply

# ステージング環境
cd terraform/environments/stg
terraform init -backend-config=backend.hcl
terraform plan
terraform apply

# 本番環境(承認フロー推奨)
cd terraform/environments/prod
terraform init -backend-config=backend.hcl
terraform plan -out=prod.tfplan
# レビュー後
terraform apply prod.tfplan

動作確認

Auto Scalingの動作確認

# スケーリングターゲットの確認
aws application-autoscaling describe-scalable-targets \
  --service-namespace ecs \
  --query 'ScalableTargets[*].[ResourceId,MinCapacity,MaxCapacity]' \
  --output table

# スケーリングポリシーの確認
aws application-autoscaling describe-scaling-policies \
  --service-namespace ecs \
  --query 'ScalingPolicies[*].[PolicyName,TargetTrackingScalingPolicyConfiguration.TargetValue]' \
  --output table

# 負荷テストでAuto Scalingを確認
for i in {1..1000}; do
  curl -s http://$ALB_DNS/api/heavy-endpoint &
done
wait

# タスク数の変化を監視
watch -n 5 'aws ecs describe-services \
  --cluster $CLUSTER_NAME \
  --services $SERVICE_NAME \
  --query "services[0].[desiredCount,runningCount]" \
  --output text'

Performance Insightsの確認

AWS Management Consoleで以下を確認します。

RDS > Performance Insights

確認項目 説明
Top SQL 実行時間の長いクエリが表示される
Database Load 時系列でDB負荷が可視化される
Top Waits 待機イベントの内訳が表示される
SQL Statistics クエリごとの統計情報が確認できる

コスト管理

環境別のコスト最適化設定です。

リソース 開発環境 ステージング環境 本番環境
NAT Gateway 無効($0) 有効(月額$65) 有効(月額$130、Multi-AZ)
ECS Fargate 最小構成(月額$10) 中規模(月額$50) Auto Scaling(月額$200〜)
RDS t3.micro(月額$13) t3.small(月額$26) m5.large(月額$130)
Performance Insights 無効($0) 7日保持(無料) 30日保持(月額$20)

トラブルシューティング

モジュール参照エラー

エラー 原因 対処方法
Module not found パスの指定ミス source = "…/…/modules/xxx"の相対パスを確認
Variable not defined 変数の未定義 モジュールのvariables.tfに変数を追加
Output not found 出力の未定義 モジュールのoutputs.tfに出力を追加

Auto Scalingが動作しない

# CloudWatch Alarmsの状態確認
aws cloudwatch describe-alarms \
  --alarm-name-prefix $PROJECT_NAME \
  --query 'MetricAlarms[?StateValue!=`OK`].[AlarmName,StateValue,StateReason]' \
  --output table

# ECSサービスのイベント確認
aws ecs describe-services \
  --cluster $CLUSTER_NAME \
  --services $SERVICE_NAME \
  --query 'services[0].events[0:5]' \
  --output table

ベストプラクティス

terraform.tfvarsの管理

# 推奨: enable系フラグのみを管理
enable_nat_gateway = false
enable_monitoring  = false

# 非推奨: すべての値を記述
vpc_cidr = "10.0.0.0/16"
availability_zones = ["ap-northeast-1a", "ap-northeast-1c"]

環境別設定の使い分け

設定項目 開発 ステージング 本番 理由
enable_nat_gateway false true true コスト vs セキュリティ
enable_performance_insights false true true 監視レベルの違い
enable_deletion_protection false false true 誤削除防止
enable_scheduled_scaling false false true 本番環境のみ時間帯別スケーリング
secrets_recovery_window_days 0 7 30 環境別復旧期間設定
skip_final_snapshot true false false 開発環境ではスナップショット不要
multi_az false true true 可用性要件

モジュール設計の原則

原則 実装方法
単一責任 1モジュール1責務(networkはVPC関連のみ)
疎結合 出力値を通じた連携
再利用性 環境固有値は変数で外部化
条件分岐 count/for_eachで条件付きリソース作成

次のステップ

次回は、セキュリティベストプラクティスと総まとめを行います。

  • WAF(Web Application Firewall)の詳細設定
  • AWS Config、GuardDutyによるセキュリティ監視
  • IAMロールとポリシーの最適化
  • セキュリティグループの最小権限化
  • パフォーマンステストと負荷試験
  • 12回分の総まとめとベストプラクティス集

検索

検索条件に一致する記事が見つかりませんでした