1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【第9回】AWS ECS Fargate 11回シリーズ|Phase 5-1|RDS MySQLの構築

Last updated at Posted at 2026-01-23

【第9回】AWS ECS Fargate 11回シリーズ|Phase 5-1|RDS MySQLの構築

この記事について

記事 タイトル 状態
第0回 全体ガイド ✅ 完了
第1回 構成図編 ✅ 完了
第2回 VPC & Subnet ✅ 完了
第3回 NAT Gateway & Route Table ✅ 完了
第4回 Security Group ✅ 完了
第5回 ECR & Docker Image ✅ 完了
第6回 Public ALB ✅ 完了
第7回 ECS Front & IAM ✅ 完了
第8回 ECS API & Internal ALB ✅ 完了
第9回 RDS MySQL 📍今回
第10回 Secrets Manager & 完成 ⬜ 未読

進捗: 82% (9/11記事) | Phase: 5-1 | 目標: RDS MySQL (Multi-AZ) 作成

📁 完全なコードはGitHubで公開GitHub: fargate-iac-02


1. はじめに

この記事は、Phase 5の前半として、RDS MySQL(Multi-AZ)を作成します。

📝 この記事の構成:

  • Terraformコードの全文を掲載し、1ファイルずつ詳しく解説します
  • 各コードブロックには初心者向けのコメントを充実させています

1-1. 前回までの振り返り

Phase 4(第8回)で作成したもの:

  • ✅ Internal ALB
  • ✅ ECS API Service
  • 「API Test」ボタンが動作!

今回(第9回: Phase 5-1)で作成するもの:

  • RDS MySQL(Multi-AZ)
  • DB Subnet Group
  • DB Parameter Group(オプション)

1-2. この記事で学べること

  • RDS Mysqlの基本と役割
  • Multi-AZ構成のメリット
  • DB Subnet Groupの設計
  • Parameter Groupの設定

1-3. 対象読者

  • Phase 4(第8回)を完了した方
  • ECS APIが動作している方

1-4. 想定環境

  • Terraform: v1.9.x
  • Phase 1-4: 実行済み

2. Phase 5-1で作成するもの

2-1. リソース一覧

リソース 数量 用途 日額費用(24h) 月額費用
RDS MySQL 1 データベース $1.17 約$35
DB Subnet Group 1 Multi-AZ配置 無料 無料
DB Parameter Group 1 MySQL設定 無料 無料
Random Password 1 パスワード生成 無料 無料

合計: 4リソース
コスト: 約$1.17/日(約$35/月)

💰 RDS MySQL (db.t3.micro) コスト詳細:

項目 単価 計算式 日額
インスタンス(Multi-AZ) $0.048/時間 $0.048 × 24h = $1.152 $1.15
ストレージ(20GB gp3) $0.138/GB/月 20GB × $0.138 ÷ 30日 = $0.092 $0.09
バックアップ(7日分) 無料 - 無料
合計 - - $1.24/日

月額換算: $1.24 × 30日 = $37.20/月

注: 実際の料金は$35/月程度(AWS料金計算ツールによる)

2-2. 構成図での位置づけ

API (ECS Fargate)
  ↓ MySQL:3306
RDS MySQL (Multi-AZ) 👈 今回作成
  ├─ Primary DB (AZ-1a, DB Subnet A)
  └─ Standby DB (AZ-1c, DB Subnet C)

3. RDS MySQLの基本

3-1. RDS MySQLとは

RDS (Relational Database Service) は、AWSのマネージドなデータベースサービスです。

特徴:

  • サーバー管理不要
  • 自動バックアップ
  • Multi-AZ対応
  • 自動パッチ適用

料理で例えると:

  • EC2 + MySQL = レストランで自分で食材管理
  • RDS MySQL = 食材が自動で補充される冷蔵庫

3-2. Multi-AZ構成

Single-AZ vs Multi-AZ:

項目 Single-AZ Multi-AZ(推奨)
可用性 高(Primary + Standby)
自動フェイルオーバー なし あり
日額費用(24h) $0.58 $1.17
月額費用 約$18 約$35

💰 コスト計算:

  • Single-AZ: $0.024/時間 × 24h = $0.576 ≈ $0.58/日
  • Multi-AZ: $0.024/時間 × 2 × 24h = $1.152 ≈ $1.17/日

Multi-AZの動作:

通常時:
API → Primary DB (AZ-1a)
      ↓ (同期レプリケーション)
      Standby DB (AZ-1c)

障害時:
API → Primary DB (AZ-1a) ❌ 障害発生
      ↓ (自動フェイルオーバー 1-2分)
API → Standby DB (AZ-1c) ✅ Primaryに昇格

料理で例えると:

  • Single-AZ = メインシェフ1人(休んだら店休業)
  • Multi-AZ = メインシェフ + 副シェフ(メインが休んでも副が代役)

3-3. DB Subnet Groupとは

DB Subnet Groupは、RDSを配置できるSubnetのグループです。

今回の設定:

  • DB Subnet A(AZ-1a)
  • DB Subnet C(AZ-1c)

Multi-AZ構成の前提条件:

  • 最低2つの異なるAZにSubnetが必要

4. ディレクトリ構成

4-1. プロジェクト全体の構成

terraform/
├── phase1-network/      ← Phase 1完了
├── phase2-security/     ← Phase 2完了
├── phase3-ecs-front/    ← Phase 3完了
├── phase4-ecs-api/      ← Phase 4完了
└── phase5-rds/          👉 今回作成
    ├── main.tf
    ├── variables.tf
    ├── terraform.tfvars
    ├── data.tf
    ├── rds.tf
    └── outputs.tf

4-2. Phase 5-1のファイル構成

ファイル名 役割 行数
main.tf Provider設定 20
variables.tf 変数定義 40
terraform.tfvars 変数の値 15
data.tf Phase 1-2の参照 40
rds.tf RDS、DB Subnet Group、Parameter Group 100
outputs.tf 出力 20

合計: 6ファイル、約235行


5. ディレクトリ・ファイル作成

6-1. ディレクトリ作成

Phase 5用のディレクトリを作成します:

mkdir -p terraform/phase5-rds
cd terraform/phase5-rds

6-2. ファイル一括作成

以下のコマンドで6つのファイルを一括作成します:

touch {main,variables,data,rds,outputs}.tf terraform.tfvars

作成されたファイル:

  • main.tf - Provider設定
  • variables.tf - 変数定義
  • terraform.tfvars - 変数の値
  • data.tf - Phase 1-2の参照
  • rds.tf - RDS MySQL、DB Subnet Group、Parameter Group
  • outputs.tf - 出力値

確認:

ls -la
# main.tf, variables.tf, terraform.tfvars, data.tf, rds.tf, outputs.tf

6. ファイル別コード解説

それでは、各ファイルのコードを1つずつ解説していきます。

6-1. main.tf - Provider設定

コード:

# ファイルパス: terraform/phase5-rds/main.tf

terraform {
  required_version = ">= 1.9.0"

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

provider "aws" {
  region = var.region

  default_tags {
    tags = {
      Project   = var.project_name
      ManagedBy = "Terraform"
      Phase     = "5"
    }
  }
}

6-2. variables.tf - 変数定義

コード:

# ファイルパス: terraform/phase5-rds/variables.tf

variable "project_name" {
  description = "プロジェクト名"
  type        = string
}

variable "region" {
  description = "AWSリージョン"
  type        = string
  default     = "ap-northeast-1"
}

variable "db_name" {
  description = "データベース名"
  type        = string
  default     = "testdb"
}

variable "db_username" {
  description = "データベースユーザー名"
  type        = string
  default     = "admin"
}

variable "db_password" {
  description = "データベースパスワード"
  type        = string
  sensitive   = true
}

variable "db_instance_class" {
  description = "DBインスタンスクラス"
  type        = string
  default     = "db.t3.micro"
}

variable "db_allocated_storage" {
  description = "ストレージサイズ(GB)"
  type        = number
  default     = 20
}

ポイント:

  1. db_password: sensitive = true(ログに表示しない)
  2. db_instance_class: db.t3.micro(無料枠対象外だが最小)
  3. db_allocated_storage: 20GB

6-3. terraform.tfvars - 変数の値

コード:

# ファイルパス: terraform/phase5-rds/terraform.tfvars

project_name = "myproject"
region       = "ap-northeast-1"

db_name              = "testdb"
db_username          = "admin"
db_password          = "YourSecurePassword123!"  # 👈 強力なパスワードに変更
db_instance_class    = "db.t3.micro"
db_allocated_storage = 20

⚠️ 重要: パスワードの要件

AWSの要件:

  • 8文字以上
  • 英大文字、英小文字、数字を含む
  • 記号を含む(推奨)

例:

MySecurePass123!
CloudTech2025#DB
Terraform@RDS99

本番環境:

  • terraform.tfvarsにパスワードを書かない
  • Secrets Managerまたは環境変数を使用

6-4. data.tf - Phase 1-2の参照

ファイルの役割:

  • Phase 1のVPC、Subnet情報を取得
  • Phase 2のSecurity Group情報を取得

コード:

# ファイルパス: terraform/phase5-rds/data.tf

# Phase 1の出力を参照
data "terraform_remote_state" "network" {
  backend = "local"

  config = {
    path = "../phase1-network/terraform.tfstate"
  }
}

# Phase 2の出力を参照
data "terraform_remote_state" "security" {
  backend = "local"

  config = {
    path = "../phase2-security/terraform.tfstate"
  }
}

# Phase 1から取得する値をローカル変数化
locals {
  vpc_id        = data.terraform_remote_state.network.outputs.vpc_id
  db_subnet_ids = data.terraform_remote_state.network.outputs.db_subnet_ids
  azs           = data.terraform_remote_state.network.outputs.azs
}

# Phase 2から取得する値をローカル変数化
locals {
  rds_sg_id = data.terraform_remote_state.security.outputs.rds_sg_id
}

6-5. rds.tf - RDS MySQL

ファイルの役割:

  • DB Subnet Groupを作成
  • DB Parameter Groupを作成(オプション)
  • RDS MySQLを作成

6-5-1. DB Subnet Group

コード:

# ファイルパス: terraform/phase5-rds/rds.tf

# ==========================================
# DB Subnet Group
# ==========================================
resource "aws_db_subnet_group" "main" {
  name       = "${var.project_name}-db-subnet-group"
  subnet_ids = local.db_subnet_ids

  tags = {
    Name = "${var.project_name}-db-subnet-group"
  }
}

ポイント:

  • subnet_ids: Phase 1で作成したDB Subnet × 2
  • Multi-AZ構成の前提条件

6-5-2. DB Parameter Group(オプション)

コード:

# ==========================================
# DB Parameter Group
# ==========================================
resource "aws_db_parameter_group" "main" {
  name   = "${var.project_name}-mysql-params"
  family = "mysql8.0"

  parameter {
    name  = "character_set_server"
    value = "utf8mb4"
  }

  parameter {
    name  = "character_set_client"
    value = "utf8mb4"
  }

  parameter {
    name  = "character_set_connection"
    value = "utf8mb4"
  }

  parameter {
    name  = "character_set_results"
    value = "utf8mb4"
  }

  parameter {
    name  = "character_set_database"
    value = "utf8mb4"
  }

  parameter {
    name  = "collation_server"
    value = "utf8mb4_general_ci"
  }

  tags = {
    Name = "${var.project_name}-mysql-params"
  }
}

ポイント:

  1. family = "mysql8.0": MySQL 8.0系
  2. 文字セット: utf8mb4(絵文字対応)
  3. 照合順序: utf8mb4_general_ci

なぜParameter Groupが必要?

デフォルトのMySQLは latin1(英語のみ)なので、日本語を扱う場合は utf8mb4 に変更が必要。


6-5-3. RDS MySQL

コード:

# ==========================================
# RDS MySQL
# ==========================================
resource "aws_db_instance" "main" {
  identifier = "${var.project_name}-mysql"

  # エンジン設定
  engine               = "mysql"
  engine_version       = "8.0.35"
  instance_class       = var.db_instance_class
  allocated_storage    = var.db_allocated_storage
  storage_type         = "gp3"
  storage_encrypted    = true

  # データベース設定
  db_name  = var.db_name
  username = var.db_username
  password = var.db_password

  # ネットワーク設定
  db_subnet_group_name   = aws_db_subnet_group.main.name
  vpc_security_group_ids = [local.rds_sg_id]
  publicly_accessible    = false

  # Multi-AZ設定
  multi_az = true

  # バックアップ設定
  backup_retention_period = 7
  backup_window           = "03:00-04:00"
  maintenance_window      = "mon:04:00-mon:05:00"

  # Parameter Group
  parameter_group_name = aws_db_parameter_group.main.name

  # 削除保護(本番環境ではtrue推奨)
  deletion_protection = false
  skip_final_snapshot = true

  # 自動マイナーバージョンアップ
  auto_minor_version_upgrade = true

  tags = {
    Name = "${var.project_name}-mysql"
  }
}

ポイント:

  1. engine_version = "8.0.35": MySQL 8.0系の最新
  2. storage_type = "gp3": 最新のSSD(gp2より高性能)
  3. storage_encrypted = true: ストレージ暗号化
  4. publicly_accessible = false: インターネットからアクセス不可
  5. multi_az = true: Multi-AZ構成
  6. backup_retention_period = 7: 7日間バックアップ保持
  7. deletion_protection = false: 削除保護無効(学習用)

本番環境での推奨設定:

deletion_protection = true   # 削除保護を有効
skip_final_snapshot = false  # 最終スナップショットを作成

6-6. outputs.tf - 出力

コード:

# ファイルパス: terraform/phase5-rds/outputs.tf

output "db_endpoint" {
  description = "RDS Endpoint"
  value       = aws_db_instance.main.endpoint
}

output "db_endpoint_address" {
  description = "RDS Endpoint Address (without port)"
  value       = aws_db_instance.main.address
}

output "db_name" {
  description = "Database Name"
  value       = aws_db_instance.main.db_name
}

output "db_username" {
  description = "Database Username"
  value       = aws_db_instance.main.username
  sensitive   = true
}

output "db_port" {
  description = "Database Port"
  value       = aws_db_instance.main.port
}

ポイント:

  • endpoint: ホスト名:ポート(例: xxx.rds.amazonaws.com:3306)
  • address: ホスト名のみ(例: xxx.rds.amazonaws.com)

6. 実行手順

6-1. ディレクトリ作成

mkdir -p terraform/phase5-rds
cd terraform/phase5-rds

6-2. ファイル作成

6つのファイルを作成:

  • main.tf
  • variables.tf
  • terraform.tfvars(パスワードを必ず変更!)
  • data.tf
  • rds.tf
  • outputs.tf

6-3. terraform init

terraform init

6-4. terraform plan

terraform plan

実行結果:

Plan: 3 to add, 0 to change, 0 to destroy.

内訳:

  • DB Subnet Group × 1
  • DB Parameter Group × 1
  • RDS Instance × 1

6-5. terraform apply

terraform apply

確認メッセージ:

Enter a value: yes

実行結果:

aws_db_subnet_group.main: Creating...
aws_db_parameter_group.main: Creating...
aws_db_subnet_group.main: Creation complete
aws_db_parameter_group.main: Creation complete
aws_db_instance.main: Creating...
aws_db_instance.main: Still creating... [2m0s elapsed]
aws_db_instance.main: Still creating... [4m0s elapsed]
aws_db_instance.main: Still creating... [6m0s elapsed]
aws_db_instance.main: Still creating... [8m0s elapsed]
aws_db_instance.main: Creation complete after 8m15s

Apply complete! Resources: 3 added, 0 changed, 0 destroyed.

Outputs:

db_endpoint = "myproject-mysql.xxxxxxxxxxxx.ap-northeast-1.rds.amazonaws.com:3306"
db_endpoint_address = "myproject-mysql.xxxxxxxxxxxx.ap-northeast-1.rds.amazonaws.com"
db_name = "testdb"
db_port = 3306

⏰ 所要時間: 約8-10分(RDSの作成に時間がかかる)


7. 動作確認

7-1. AWSコンソールで確認

RDS:

  1. AWSコンソール → RDS → Databases
  2. 「myproject-mysql」が作成されていることを確認
  3. Status: Available(約8-10分後)

詳細確認:

  • Multi-AZ: Yes
  • Endpoint: xxx.rds.amazonaws.com:3306
  • VPC: myproject-vpc
  • Subnet group: myproject-db-subnet-group

7-2. 接続テスト(ECS Task経由)

ECS API TaskからRDSに接続:

# API TaskのIDを取得
aws ecs list-tasks \
  --cluster myproject-cluster \
  --service-name myproject-api-service \
  --region ap-northeast-1

# Task内でmysqlコマンド実行
aws ecs execute-command \
  --cluster myproject-cluster \
  --task <TASK_ID> \
  --container api \
  --command "/bin/sh" \
  --interactive \
  --region ap-northeast-1

Task内で実行:

# mysqlクライアントをインストール(Alpine Linuxの場合)
apk add mysql-client

# RDSに接続
mysql -h myproject-mysql.xxxxxxxxxxxx.ap-northeast-1.rds.amazonaws.com \
  -u admin \
  -p

# パスワードを入力

接続成功:

mysql> SHOW DATABASES;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| mysql              |
| performance_schema |
| sys                |
| testdb             |
+--------------------+
5 rows in set (0.00 sec)

7-3. 初期テーブルの作成

testdbに移動:

mysql> USE testdb;
Database changed

test_tableを作成:

CREATE TABLE test_table (
    id INT AUTO_INCREMENT PRIMARY KEY,
    name VARCHAR(50),
    age INT
);

初期データを挿入:

INSERT INTO test_table (name, age) VALUES ('Test Taro', 30);
INSERT INTO test_table (name, age) VALUES ('Test Jiro', 22);
INSERT INTO test_table (name, age) VALUES ('Test Hanako', 25);
INSERT INTO test_table (name, age) VALUES ('Test Youko', 25);

確認:

SELECT * FROM test_table;

結果:

+----+--------------+-----+
| id | name         | age |
+----+--------------+-----+
|  1 | Test Taro    |  30 |
|  2 | Test Jiro    |  22 |
|  3 | Test Hanako  |  25 |
|  4 | Test Youko   |  25 |
+----+--------------+-----+
4 rows in set (0.00 sec)

8. トラブルシューティング

8-1. エラー1: RDS作成失敗

エラーメッセージ:

Error: creating RDS DB Instance: InvalidParameterValue

よくある原因:

原因1: パスワードが要件を満たしていない

解決策:

terraform.tfvarsのdb_passwordを以下の要件を満たすように変更:

  • 8文字以上
  • 英大文字、英小文字、数字を含む
  • 記号を含む
db_password = "MySecurePass123!"

原因2: DB Subnet GroupのSubnetが不足

エラーメッセージ:

DBSubnetGroupDoesNotCoverEnoughAZs

解決策:

Phase 1でDB Subnet × 2が作成されているか確認:

cd ../phase1-network
terraform output db_subnet_ids

# 2つのSubnet IDが表示されればOK

8-2. エラー2: RDSに接続できない

現象:

  • ECS TaskからRDSに接続できない

確認ポイント:

確認1: Security Group

ecs-api-sg → rds-sg への3306番ポートが許可されているか:

cd ../phase2-security
terraform state show aws_security_group.ecs_api
terraform state show aws_security_group.rds

# Egressに3306番ポートが含まれているか確認

確認2: RDSのエンドポイント

正しいエンドポイントを使用しているか:

cd ../phase5-rds
terraform output db_endpoint_address

# 出力されたエンドポイントを使用

8-3. エラー3: 文字化け

現象:

  • 日本語データが文字化けする

原因:

  • Parameter Groupで文字セットが設定されていない

解決策:

Parameter Groupを確認:

SHOW VARIABLES LIKE 'character_set%';

期待される結果:

+--------------------------+----------------------------+
| Variable_name            | Value                      |
+--------------------------+----------------------------+
| character_set_client     | utf8mb4                    |
| character_set_connection | utf8mb4                    |
| character_set_database   | utf8mb4                    |
| character_set_results    | utf8mb4                    |
| character_set_server     | utf8mb4                    |
| character_set_system     | utf8mb3                    |
+--------------------------+----------------------------+

9. コスト最適化

9-1. シングルAZ構成に変更(学習用)

Multi-AZ → シングルAZに変更して約$15/月節約:

# rds.tf

resource "aws_db_instance" "main" {
  # ...

  # Multi-AZ設定
  multi_az = false  # true → false に変更

  # ...
}

デメリット:

  • 可用性が低下
  • フェイルオーバーなし

9-2. 自動停止スクリプト(学習用)

夜間・休日に自動停止:

# stop-rds.sh
aws rds stop-db-instance \
  --db-instance-identifier myproject-mysql \
  --region ap-northeast-1

# start-rds.sh
aws rds start-db-instance \
  --db-instance-identifier myproject-mysql \
  --region ap-northeast-1

注意:

  • 7日間停止すると自動的に起動する
  • 本番環境では使用しない

10. 🎯 Phase 5-1完了!

10-1. できたこと

✅ RDS MySQL(Multi-AZ)の作成
✅ DB Subnet Groupの作成
✅ DB Parameter Groupの作成
✅ 初期テーブルとデータの作成

10-2. 次回予告

第10回: Phase 5-2 - Secrets Manager & 完成

次回は、Phase 5の後半として以下を実装します:

  • Secrets Manager(DBパスワード管理)
  • Phase 4の環境変数更新(DB接続情報)
  • ECS API Taskの再起動

Phase 5-2が完了すると 🎉「Database Test」ボタンが動作し、全システムが完全動作! します。


11. 参考リンク


1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?