この記事について
| 記事 | タイトル | 状態 |
|---|---|---|
| 第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 & 完成 | ⬜ 未読 |
進捗: 27% (3/11記事) | Phase: 1-2 | 🎯 マイルストーン1達成: Phase 1完了!
📁 完全なコードはGitHubで公開:👉 GitHub: fargate-iac-02
1. はじめに
この記事は、Phase 1の後半として、NAT GatewayとRoute Tableを作成します。
1-1. 前回までの振り返り
第2回(Phase 1-1)で作成したもの:
- ✅ VPC
- ✅ Subnet × 6(Public/Private/DB)
- ✅ Internet Gateway
今回(第3回: Phase 1-2)で作成するもの:
- NAT Gateway × 2
- Elastic IP × 2
- Route Table × 2(Public用、Private用)
- Route Table Association × 6
1-2. この記事で学べること
- NAT Gatewayの役割と仕組み
- Route Tableの設計(Public/Private)
- 通信経路の理解(IGW経由 vs NAT Gateway経由)
- Multi-AZ構成のNAT Gateway
1-3. 対象読者
- 第2回を完了した方
- Phase 1-1(VPC、Subnet、IGW)を作成済みの方
1-4. 想定環境
- Terraform: v1.9.x
- Phase 1-1: 実行済み
2. Phase 1-2で作成するもの
2-1. リソース一覧
| リソース | 数量 | 配置場所 | 日額費用(24h) | 月額費用 |
|---|---|---|---|---|
| NAT Gateway | 2 | Public Subnet × 2 | $3.00 | $90 |
| Elastic IP | 2 | NAT Gateway用 | 無料 | 無料 |
| Route Table (Public) | 1 | - | 無料 | 無料 |
| Route Table (Private) | 2 | AZ × 2 | 無料 | 無料 |
| Route Table Association | 6 | Subnet × 6 | 無料 | 無料 |
合計: 16リソース
コスト: 約$3.00/日(約$90/月) - NAT Gatewayのみ課金
💰 NAT Gatewayコスト詳細:
- 時間料金: $0.062/時間 × 2 = $0.124/時間
- 日額: $0.124 × 24h = $2.976 ≈ $3.00/日
- 月額: $3.00 × 30日 = $90/月
2-2. 構成図での位置づけ
Phase 1-2で追加するのは、配置構成図の以下の部分です:
VPC: 10.0.0.0/16
├─ Internet Gateway ← 第2回で作成済み
├─ Route Table: Public (0.0.0.0/0 → IGW) ← 今回作成
├─ Route Table: Private-A (0.0.0.0/0 → NAT Gateway A) ← 今回作成
├─ Route Table: Private-C (0.0.0.0/0 → NAT Gateway C) ← 今回作成
│
├─ Public Subnet A: 10.0.1.0/24 ← 第2回で作成済み
│ └─ NAT Gateway A ← 今回作成
│ └─ Elastic IP A ← 今回作成
│
├─ Public Subnet C: 10.0.2.0/24 ← 第2回で作成済み
│ └─ NAT Gateway C ← 今回作成
│ └─ Elastic IP C ← 今回作成
│
├─ Private Subnet A: 10.0.10.0/24 ← 第2回で作成済み
├─ Private Subnet C: 10.0.11.0/24 ← 第2回で作成済み
├─ DB Subnet A: 10.0.20.0/24 ← 第2回で作成済み
└─ DB Subnet C: 10.0.21.0/24 ← 第2回で作成済み
3. NAT GatewayとRoute Tableの役割
3-1. NAT Gatewayとは
NAT Gateway (Network Address Translation Gateway) は、Private Subnet内のリソースがインターネットにアクセスするためのゲートウェイです。
特徴:
- Private Subnet → Internet: OK(アウトバウンド)
- Internet → Private Subnet: NG(インバウンド)
料理で例えると:
- NAT Gateway = 厨房の勝手口
- 仕入れ業者(インターネット)から材料を受け取る
- お客さん(インターネット)は勝手口から入れない
なぜ必要?
ECS Taskは Private Subnetに配置しますが、以下の通信が必要です:
- ECRからDockerイメージをpull
- Secrets ManagerからDB パスワードを取得
- CloudWatch Logsにログを送信
これらはすべてNAT Gateway経由でインターネットに出る必要があります。
3-2. Route Tableとは
Route Tableは、通信経路を定義するルーティングテーブルです。
Public Subnet用のRoute Table:
| 送信先 | ターゲット | 意味 |
|---|---|---|
| 10.0.0.0/16 | local | VPC内はローカル通信 |
| 0.0.0.0/0 | igw-xxx | それ以外はIGW経由 |
Private Subnet用のRoute Table:
| 送信先 | ターゲット | 意味 |
|---|---|---|
| 10.0.0.0/16 | local | VPC内はローカル通信 |
| 0.0.0.0/0 | nat-xxx | それ以外はNAT Gateway経由 |
料理で例えると:
- Route Table = 配達員への指示書
- 「店内(VPC内)の配達は直接、外部(Internet)は玄関(IGW)または勝手口(NAT)経由で」
3-3. Multi-AZ構成のNAT Gateway
設計の選択肢:
| 構成 | NAT Gateway数 | 月額費用 | 可用性 |
|---|---|---|---|
| Single-AZ | 1 | $45 | 低 |
| Multi-AZ | 2 | $90 | 高 |
今回の選択: Multi-AZ(NAT Gateway × 2)
理由:
- ✅ 片方のAZが障害でも、もう片方で稼働継続
- ✅ 本番環境で推奨される構成
- ⚠️ コストは2倍
学習用の最小構成:
- NAT Gatewayを1つに削減可能(約$45/月の節約)
- ただし、Multi-AZの利点は失われる
4. ファイル構成
4-1. Phase 1-2で追加するファイル
terraform/phase1-network/
├── main.tf ← 第2回で作成済み
├── variables.tf ← 第2回で作成済み
├── terraform.tfvars ← 第2回で作成済み
├── vpc.tf ← 第2回で作成済み
├── subnet.tf ← 第2回で作成済み
├── igw.tf ← 第2回で作成済み
├── nat.tf 👉 今回作成
├── route_table.tf 👉 今回作成
└── outputs.tf ← 第2回で作成済み(更新)
4-2. 追加ファイルの詳細
| ファイル名 | 役割 | 行数 |
|---|---|---|
| nat.tf | NAT Gateway、EIPの定義 | 35 |
| route_table.tf | Route Table、Route、Associationの定義 | 90 |
合計: 2ファイル、約125行
5. ファイル別コード解説
5-1. nat.tf - NAT Gateway × 2
ファイルの役割:
- Elastic IP × 2を作成
- NAT Gateway × 2を作成
コード:
# terraform/phase1-network/nat.tf
# ==========================================
# Elastic IP × 2(NAT Gateway用)
# ==========================================
resource "aws_eip" "nat" {
count = 2
domain = "vpc"
tags = {
Name = "${var.project_name}-nat-eip-${count.index + 1}"
}
}
# ==========================================
# NAT Gateway × 2
# ==========================================
resource "aws_nat_gateway" "main" {
count = 2
allocation_id = aws_eip.nat[count.index].id
subnet_id = aws_subnet.public[count.index].id
tags = {
Name = "${var.project_name}-nat-gateway-${count.index + 1}"
}
depends_on = [aws_internet_gateway.main]
}
ポイント:
-
Elastic IP:
- domain = "vpc": VPC用のEIP
- NAT Gateway用の固定IPアドレス
-
NAT Gateway:
- allocation_id: 紐付けるEIPのID
- subnet_id: Public Subnetに配置(重要!)
- depends_on: IGWが先に作成されることを保証
なぜPublic Subnetに配置?
NAT Gatewayは、Private Subnetからの通信をインターネットに転送するため、自身はPublic Subnetに配置する必要があります。
Private Subnet内のECS Task
↓
NAT Gateway(Public Subnet内)
↓
Internet Gateway
↓
インターネット
5-2. route_table.tf - Route Table × 2
ファイルの役割:
- Public Subnet用のRoute Tableを作成
- Private Subnet用のRoute Tableを作成(AZ × 2)
- Subnetとの関連付け
コード:
# terraform/phase1-network/route_table.tf
# ==========================================
# Public Subnet用のRoute Table
# ==========================================
resource "aws_route_table" "public" {
vpc_id = aws_vpc.main.id
tags = {
Name = "${var.project_name}-public-rt"
}
}
# Public RouteTable用のルート(0.0.0.0/0 → IGW)
resource "aws_route" "public_internet_gateway" {
route_table_id = aws_route_table.public.id
destination_cidr_block = "0.0.0.0/0"
gateway_id = aws_internet_gateway.main.id
}
# Public Subnetとの関連付け
resource "aws_route_table_association" "public" {
count = 2
subnet_id = aws_subnet.public[count.index].id
route_table_id = aws_route_table.public.id
}
# ==========================================
# Private Subnet用のRoute Table(AZ × 2)
# ==========================================
resource "aws_route_table" "private" {
count = 2
vpc_id = aws_vpc.main.id
tags = {
Name = "${var.project_name}-private-rt-${count.index + 1}"
}
}
# Private RouteTable用のルート(0.0.0.0/0 → NAT Gateway)
resource "aws_route" "private_nat_gateway" {
count = 2
route_table_id = aws_route_table.private[count.index].id
destination_cidr_block = "0.0.0.0/0"
nat_gateway_id = aws_nat_gateway.main[count.index].id
}
# Private Subnetとの関連付け
resource "aws_route_table_association" "private" {
count = 2
subnet_id = aws_subnet.private[count.index].id
route_table_id = aws_route_table.private[count.index].id
}
# DB Subnetとの関連付け
resource "aws_route_table_association" "db" {
count = 2
subnet_id = aws_subnet.db[count.index].id
route_table_id = aws_route_table.private[count.index].id
}
ポイント:
-
Public Route Table:
- 1つだけ作成
- 0.0.0.0/0 → IGW
- Public Subnet × 2と関連付け
-
Private Route Table:
- AZごとに2つ作成
- 0.0.0.0/0 → NAT Gateway(AZごとに異なる)
- Private Subnet × 2、DB Subnet × 2と関連付け
なぜPrivate Route Tableが2つ?
Multi-AZ構成では、各AZが独立して動作できるようにするため、Route TableもAZごとに分けます。
AZ-1a:
Private Subnet A → Private RT A → NAT Gateway A
AZ-1c:
Private Subnet C → Private RT C → NAT Gateway C
5-3. outputs.tf - 出力の更新
第2回で作成したoutputs.tfに、NAT Gateway IDを追加:
# terraform/phase1-network/outputs.tf
# ... 既存のoutput ...
output "nat_gateway_ids" {
description = "NAT Gateway IDs"
value = aws_nat_gateway.main[*].id
}
output "public_route_table_id" {
description = "Public Route Table ID"
value = aws_route_table.public.id
}
output "private_route_table_ids" {
description = "Private Route Table IDs"
value = aws_route_table.private[*].id
}
6. 実行手順
6-1. ファイル追加
Phase 1-1のディレクトリに、2つのファイルを追加:
cd terraform/phase1-network
# nat.tfを作成
# route_table.tfを作成
# outputs.tfを更新
6-2. terraform plan
実行計画の確認:
terraform plan
実行結果:
Terraform will perform the following actions:
# aws_eip.nat[0] will be created
# aws_eip.nat[1] will be created
# aws_nat_gateway.main[0] will be created
# aws_nat_gateway.main[1] will be created
# aws_route_table.public will be created
# aws_route_table.private[0] will be created
# aws_route_table.private[1] will be created
# aws_route.public_internet_gateway will be created
# aws_route.private_nat_gateway[0] will be created
# aws_route.private_nat_gateway[1] will be created
# aws_route_table_association.public[0] will be created
# aws_route_table_association.public[1] will be created
# aws_route_table_association.private[0] will be created
# aws_route_table_association.private[1] will be created
# aws_route_table_association.db[0] will be created
# aws_route_table_association.db[1] will be created
Plan: 16 to add, 0 to change, 0 to destroy.
ポイント:
- Plan: 16 to add: 16個のリソースが追加される
- 既存のリソース(VPC、Subnet、IGW)は変更なし
6-3. terraform apply
実際にリソースを作成:
terraform apply
確認メッセージ:
Do you want to perform these actions?
Enter a value: yes 👈 yesと入力
実行結果:
aws_eip.nat[0]: Creating...
aws_eip.nat[1]: Creating...
aws_route_table.public: Creating...
aws_route_table.private[0]: Creating...
aws_route_table.private[1]: Creating...
...
aws_nat_gateway.main[0]: Still creating... [1m0s elapsed]
aws_nat_gateway.main[1]: Still creating... [1m0s elapsed]
...
aws_nat_gateway.main[0]: Creation complete after 2m15s
aws_nat_gateway.main[1]: Creation complete after 2m18s
...
Apply complete! Resources: 16 added, 0 changed, 0 destroyed.
所要時間: 約3-4分(NAT Gatewayの作成に時間がかかる)
7. 動作確認
7-1. AWSコンソールで確認
NAT Gateway:
- AWSコンソール → VPC → NAT Gateways
- 2つのNAT Gatewayが作成されていることを確認
- myproject-nat-gateway-1(AZ-1a)
- myproject-nat-gateway-2(AZ-1c)
- State: Available
Elastic IP:
- VPC → Elastic IPs
- 2つのEIPが作成されていることを確認
- 各EIPがNAT Gatewayに関連付けられている
Route Table:
- VPC → Route Tables
- 3つのRoute Tableが作成されていることを確認
- myproject-public-rt
- myproject-private-rt-1
- myproject-private-rt-2
Public Route Tableの内容:
| 送信先 | ターゲット | ステータス |
|---|---|---|
| 10.0.0.0/16 | local | active |
| 0.0.0.0/0 | igw-xxx | active |
Private Route Table-1の内容:
| 送信先 | ターゲット | ステータス |
|---|---|---|
| 10.0.0.0/16 | local | active |
| 0.0.0.0/0 | nat-xxx1 | active |
7-2. Subnet関連付けの確認
Public Route Table:
- Public Subnet A
- Public Subnet C
Private Route Table-1:
- Private Subnet A
- DB Subnet A
Private Route Table-2:
- Private Subnet C
- DB Subnet C
8. トラブルシューティング
8-1. エラー1: EIPの上限超過
エラーメッセージ:
Error: Error allocating EIP: AddressLimitExceeded
原因:
- AWSアカウントのEIP上限(デフォルト5個)に達している
解決策:
- 使っていないEIPを削除
- AWS Supportにリクエストして上限緩和
8-2. エラー2: NAT Gatewayの作成に失敗
エラーメッセージ:
Error: creating EC2 NAT Gateway: InvalidSubnet.ID
原因:
- Subnet IDが正しくない
- Phase 1-1が完了していない
解決策:
# Phase 1-1の状態を確認
terraform output vpc_id
terraform output public_subnet_ids
# 値が正しく出力されるか確認
8-3. エラー3: Route Tableの関連付けエラー
エラーメッセージ:
Error: Resource.AlreadyAssociated
原因:
- Subnetが既に別のRoute Tableに関連付けられている
解決策:
AWSコンソールでSubnetの関連付けを確認し、不要な関連付けを削除。
9. コスト最適化(学習用)
9-1. NAT Gatewayを1つに削減
学習用の最小構成:
nat.tfを以下のように変更:
# NAT Gateway × 1に削減
resource "aws_eip" "nat" {
count = 1 # 2 → 1に変更
domain = "vpc"
tags = {
Name = "${var.project_name}-nat-eip-${count.index + 1}"
}
}
resource "aws_nat_gateway" "main" {
count = 1 # 2 → 1に変更
allocation_id = aws_eip.nat[count.index].id
subnet_id = aws_subnet.public[count.index].id
tags = {
Name = "${var.project_name}-nat-gateway-${count.index + 1}"
}
depends_on = [aws_internet_gateway.main]
}
route_table.tfも修正:
# Private Route Table × 1に統合
resource "aws_route_table" "private" {
count = 1 # 2 → 1に変更
vpc_id = aws_vpc.main.id
tags = {
Name = "${var.project_name}-private-rt"
}
}
resource "aws_route" "private_nat_gateway" {
count = 1 # 2 → 1に変更
route_table_id = aws_route_table.private[0].id
destination_cidr_block = "0.0.0.0/0"
nat_gateway_id = aws_nat_gateway.main[0].id
}
# Private Subnetとの関連付け(すべて同じRoute Tableに)
resource "aws_route_table_association" "private" {
count = 2
subnet_id = aws_subnet.private[count.index].id
route_table_id = aws_route_table.private[0].id # [count.index] → [0]に変更
}
resource "aws_route_table_association" "db" {
count = 2
subnet_id = aws_subnet.db[count.index].id
route_table_id = aws_route_table.private[0].id # [count.index] → [0]に変更
}
節約額: 約$45/月
デメリット:
- Multi-AZ構成ではなくなる
- AZ-1aが障害の場合、Private Subnetからインターネットに出られない
10. 🎉 Phase 1完了!
10-1. できたこと
✅ NAT Gateway × 2の作成
✅ Route Tableの作成
✅ ネットワーク基盤の完成
✅ Multi-AZ構成の実現
10-2. Phase 1で作成したリソース(合計)
| Phase | リソース数 | 主要リソース |
|---|---|---|
| Phase 1-1 | 9 | VPC、Subnet × 6、IGW |
| Phase 1-2 | 16 | NAT Gateway × 2、Route Table × 2 |
| 合計 | 25 | - |
10-3. 🎯 マイルストーン1達成!
🎉 ネットワーク基盤が完成しました!
VPC、Subnet、NAT Gateway、Route Tableが
Multi-AZ構成で稼働しています。
11. 次回予告
第4回: Phase 2-1 - Security Group設計
次回は、Phase 2として以下を作成します:
- Security Group × 5
- alb-public
- ecs-front
- alb-internal
- ecs-api
- rds
Security Groupの設計思想と、最小権限の原則について学びます。