【第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
}
ポイント:
- db_password: sensitive = true(ログに表示しない)
- db_instance_class: db.t3.micro(無料枠対象外だが最小)
- 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"
}
}
ポイント:
- family = "mysql8.0": MySQL 8.0系
- 文字セット: utf8mb4(絵文字対応)
- 照合順序: 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"
}
}
ポイント:
- engine_version = "8.0.35": MySQL 8.0系の最新
- storage_type = "gp3": 最新のSSD(gp2より高性能)
- storage_encrypted = true: ストレージ暗号化
- publicly_accessible = false: インターネットからアクセス不可
- multi_az = true: Multi-AZ構成
- backup_retention_period = 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:
- AWSコンソール → RDS → Databases
- 「myproject-mysql」が作成されていることを確認
- 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」ボタンが動作し、全システムが完全動作! します。