メインコンテンツまでスキップ

VPC: CIDR設計と2 AZ×3層ネットワークのコスト判断

前章までで、Terraform を実行する専用の IAM ユーザーができました。ここから実際に AWS のリソースを積み上げていきます。最初に作るのは VPC です。ALB も ECS も RDS も、自分がどのサブネットに属するかを作成時に指定する必要があり、そのサブネットを持つ VPC が先になければ作れません。1

VPC そのものは CIDR ブロック 1 つを持つだけのリソースで、実質的な設計判断はサブネットの区切り方に現れます。この章では、Tasuku が使う10.0.0.0/16という CIDR の根拠を扱います。続けて、2 つのアベイラビリティゾーンに public・private-app・private-data の 3 層を配置する理由と、プライベートサブネットの出口となる NAT Gateway を 2 つ用意する根拠へ進みます。

4.1. VPC と CIDR の基礎​

VPC (Virtual Private Cloud) は、AWS アカウント内に論理的に切り出された仮想ネットワークです。作成時に指定するのは IPv4 の CIDR ブロック 1 つだけで、そのアドレス空間の中にサブネットを切り出していきます。

CIDR ブロックのサイズには制約があります。AWS が許可するのは/16(65,536 個の IP アドレス)から/28(16 個)までの範囲で、これより大きくも小さくもできません。2 プライベート IP アドレスの範囲 (RFC 1918) を使うのが通例で、10.0.0.0/8、172.16.0.0/12、192.168.0.0/16のいずれかから選びます。

サブネットにも同じ/16〜/28の制約があります。ただしサブネットでは、CIDR ブロックの先頭 4 個と末尾 1 個、計 5 個の IP アドレスを AWS が予約するため、実際に使える IP 数はブロックサイズより 5 少なくなります。/24なら 256 個から 5 個引いて 251 個です。3 4 予約される 5 個の内訳は、ネットワークアドレス、VPC ルータ用、DNS サーバー用、AWS の将来利用向け、そしてブロードキャストアドレスです。3

VPC 全体の CIDR 設計では、使い切らずに余裕を持たせることが AWS Well-Architected Framework でも推奨されています。5 小さすぎる VPC やサブネットを作ってしまうと、後から追加の CIDR ブロックを足すか、サブネットを作り直す羽目になります。

4.2. Tasuku の CIDR 設計​

Tasuku のネットワークには10.0.0.0/16を割り当てます。RFC 1918 の範囲内で、AWS が提供する VPC の例でもそのまま使われる値です。2 65,536 個の IP アドレス空間のうち、この章で実際に使うのは 6 個の/24サブネットだけです。

層AZ1AZ2用途
public10.0.0.0/2410.0.1.0/24ALB (第6章)
private-app10.0.10.0/2410.0.11.0/24ECS Fargate (第8・10章)
private-data10.0.20.0/2410.0.21.0/24RDS (第9章)

採番は詰めていません。層ごとに 10 刻みで空番を残し、10.0.2.0/24から10.0.9.0/24のような間を空けています。第 11 章で環境を分離するときや、将来サブネットを追加するときに、既存の番号を採番し直さずに済むようにするためです。

/24は 251 個の IP アドレスを持ちます。3 Fargate タスクは 1 つにつき 1 つの ENI (Elastic Network Interface) を専有するため、6 実行するタスク数がそのままこの 251 個を消費します。ただし 6 つのサブネット全体で 256 個の/24のうち 6 個しか使っておらず、CIDR 空間そのものには十分な余裕があります。

4.3. 2 AZ × 3 層のサブネット構成​

Tasuku のネットワークは、2 つのアベイラビリティゾーンに public・private-app・private-data の 3 層を配置します。

publicはインターネットゲートウェイへの経路を持つサブネットで、ALB を置きます。private-appはインターネットゲートウェイへの経路を持たず、NAT Gateway 経由でのみ外向き通信ができるサブネットで、ECS Fargate のタスクを置きます。private-dataは private-app と同じくインターネットゲートウェイへの経路を持ちませんが、NAT Gateway への経路も持たせず、RDS を置きます。

3 層に分ける理由は、層ごとに必要な通信の向きが違うためです。ALB は不特定多数からの着信を受ける必要があり、public サブネット以外の選択肢がありません。ECS Fargate のタスクは、着信は不要な一方、コンテナイメージの取得やログ送信のために外向きの通信が必要です。7 RDS は管理サービスでありインターネットからアクセスされるべきではなく8、かつこの構成では RDS からインターネットへの発信も必要としません。

アベイラビリティゾーンの指定には、ap-northeast-1aのような文字列を直接書き込みません。aws_availability_zonesデータソースで実行時に取得した一覧の先頭 2 つを使います。AWS はアベイラビリティゾーンの物理的な場所をアカウントごとにランダムな名前へ割り当てており、あるアカウントのus-east-1aが別のアカウントのus-east-1aと同じ場所を指すとは限りません。9 コードに具体的な名前を固定してしまうと、別のアカウントで動かしたときに意図と違うゾーンに展開されかねません。

なお、この一覧に 2 つ以上のアベイラビリティゾーンが含まれることはコード上でチェックしていません。Tasuku が使うap-northeast-1は 4 つのアベイラビリティゾーンを持つため実害はありませんが、10 アベイラビリティゾーンが 1 つしかないリージョンに同じコードを持ち込む場合は、先に一覧の件数を確認してください。

4.4. インターネットゲートウェイとパブリックサブネットの経路​

インターネットゲートウェイ (IGW) は、VPC とインターネットの間の出入り口です。VPC に 1 つだけアタッチし、vpc_idを指定するだけで済みます。11

public サブネット 2 つの経路は、どちらも同じ IGW に向かうだけです。IGW はアベイラビリティゾーンに紐づかないため、2 つのサブネットで別々のルートテーブルを用意する意味がなく、1 枚を共有します。ルート内容は次の 1 行だけです。

0.0.0.0/0 → インターネットゲートウェイ

4.5. NAT Gateway: プライベートサブネットの出口とコスト判断​

ここから常時課金が始まります

NAT Gateway は、稼働している時間に対する課金と、処理したデータ量に対する課金の 2 軸で費用が発生します。12 このリソースを作った時点で、この連載ではじめて「使っていなくても時間課金される」リソースが登場します。具体的な料金は改定されるため本文には書きません。試す場合はAWSの料金ページで現行の料金を確認し、使い終えたら削除してください。

private-app サブネットに置く ECS Fargate のタスクは、コンテナイメージの取得やログ送信のために外向きの通信が必要です。7 private-app サブネットは IGW への経路を持たないため、この外向き通信は NAT Gateway を経由させます。

NAT Gateway の数には 2 つの選択肢があります。

1 つ目は、2 つのアベイラビリティゾーンで 1 個の NAT Gateway を共有する構成です。時間課金を半分に抑えられますが、その NAT Gateway が乗っているアベイラビリティゾーンが利用不可になると、もう一方の健全なアベイラビリティゾーンにある private-app サブネットもインターネットへの出口を失います。

2 つ目は、アベイラビリティゾーンごとに 1 個ずつ、計 2 個の NAT Gateway を用意する構成です。NAT Gateway の高可用性は単一のアベイラビリティゾーン内に閉じており、アベイラビリティゾーンを跨いだ耐障害性を得るにはアベイラビリティゾーンごとに 1 個が必要だというのが、AWS の複数の公式ドキュメントで一致している見解です。13 14 15 16

Tasuku では 2 個の構成を選びます。第 1 章で、1 つのアベイラビリティゾーンが利用不可になってもサービスを継続する構成にすると決めているためです。17 NAT Gateway だけアベイラビリティゾーンをまたぐ単一障害点にしてしまっては、この前提と矛盾します。

コストの増分は、時間課金の分だけです。NAT Gateway が処理するデータの量に対する課金は、同じアベイラビリティゾーン内で見る限り NAT Gateway の個数に依存せず、そのアベイラビリティゾーンを通過するトラフィックの総量だけで決まります。18 2 個にして増えるのは、稼働時間分の固定費だけです。

なお、AWS は「Regional NAT Gateway」という可用性モードも提供しています。1 つの論理的な NAT Gateway が、ワークロードの存在するアベイラビリティゾーンへ自動的に展開されるしくみで、Terraform 上もaws_nat_gateway1 個で書けます。19 ただし、この機能はアベイラビリティゾーンごとに帯域を確保する動作をしており、20 コードの見た目が 1 個になることと、実際の課金がアベイラビリティゾーンごとに 2 個用意する構成より安くなることは別の話です。後者は執筆時点で確認できていません。コスト構造が確定していないこの選択肢を主教材にはせず、Tasuku では採用せず、選択肢の 1 つとしてここで触れるにとどめます。

4.6. ルートテーブルの設計​

Tasuku のネットワークには 4 枚のルートテーブルを用意します。

ルートテーブル対象サブネットルート
publicpublic-az1, public-az2 (共有)0.0.0.0/0 → IGW
private-app-az1private-app-az10.0.0.0/0 → NAT Gateway (az1)
private-app-az2private-app-az20.0.0.0/0 → NAT Gateway (az2)
private-dataprivate-data-az1, private-data-az2 (共有)なし

public が 1 枚で足りる理由は前節で述べた通りです。private-app は、自分のアベイラビリティゾーンにある NAT Gateway にしか向けられないため、アベイラビリティゾーンごとに 1 枚必要です。

private-data には、VPC 内部向けの暗黙の local ルート(全ルートテーブルに自動的に入る、CIDR 内向けの削除不能なルート)以外、意図的にルートを追加しません。RDS はインターネットからアクセスされるべきではなく8、かつこの構成ではインターネットへの能動的な通信も必要としないためです。21 public・private-app・private-data という 3 層に分けた意味は、このルートテーブルの中身の違いにそのまま表れています。public はどこへでも出ていける、private-app は NAT Gateway 越しにだけ出ていける、private-data は VPC 内部以外どこへも出ていけない(ECS タスクからの接続は VPC 内部通信のため到達可能)、という 3 段階です。

いずれのサブネットも、VPC 作成時に暗黙で使われる「メインルートテーブル」には乗せません。6 つのサブネットすべてに、どのルートテーブルに属するかを明示的に関連付けます。

4.7. セキュリティグループチェーンの方針 (実装は後続章で)​

ここまでのルートテーブルの設計は、通信が「どこまで届きうるか」を決めるものでした。実際にどの通信を許可するかは、セキュリティグループが担います。

Tasuku で最終的に必要になるセキュリティグループは 3 つで、許可の連鎖は次の図の通りです。

セキュリティグループ許可する送信元作成する章
ALB用インターネット (80番→443番ポート)第6章→第7章
ECSタスク用ALB用SGのみ第8章
RDS用ECSタスク用SGのみ第9章

この章の時点では、ALB・ECS・RDS のいずれもまだ存在しません。存在しないリソースへの通信を許可するルールは書けないため、実体の作成はそれぞれのリソースを作る章に委ねます。第3章で IAM ロールを予告表だけにとどめたのと同じ考え方です。

この章で実際に手を入れるのは、VPC 作成時に自動的にできる既定のセキュリティグループだけです。既定のセキュリティグループは、何も設定しなければ同一セキュリティグループ内からの通信をすべて許可し、外向きの通信もすべて許可した状態のままになります。使う予定のないリソースにこの既定のセキュリティグループが誤って付いてしまったときの影響を抑えるため、ingress と egress のルールを両方とも空にして閉じます。22 23

4.8. Terraform 実装​

ここまでの設計をvpc.tfに書いていきます。既存のvariables.tfには VPC 全体の CIDR を変数として追加し、VPC・サブネット・ルーティングといったこの章の主要なリソースはvpc.tf1 ファイルに収めます。第 2 章のbudget.tf、第 3 章のiam.tfと同じく、1 つの章が主に足すリソースを 1 つのファイルにまとめる方針です。あわせて、後続の章が参照する値をまとめるoutputs.tfをこの章から新設します。

まず、6 つのサブネットの定義をローカル値としてまとめます。

locals {
# 2 AZ × 3 層 (public / private-app / private-data) の6サブネット定義
# az_key ("az1"/"az2") を全リソースの for_each キーに揃え、AZ の対応関係を一貫させる
subnets = {
"public-az1" = { tier = "public", az_key = "az1", cidr = "10.0.0.0/24", public_ip_launch = true }
"public-az2" = { tier = "public", az_key = "az2", cidr = "10.0.1.0/24", public_ip_launch = true }
"private-app-az1" = { tier = "private-app", az_key = "az1", cidr = "10.0.10.0/24", public_ip_launch = false }
"private-app-az2" = { tier = "private-app", az_key = "az2", cidr = "10.0.11.0/24", public_ip_launch = false }
"private-data-az1" = { tier = "private-data", az_key = "az1", cidr = "10.0.20.0/24", public_ip_launch = false }
"private-data-az2" = { tier = "private-data", az_key = "az2", cidr = "10.0.21.0/24", public_ip_launch = false }
}

# az1 → names[0]、az2 → names[1] (data.aws_availability_zones.available の取得順)
az_key_to_index = { az1 = 0, az2 = 1 }

# 層ごとに az_key をキーにしたサブネットキーの逆引き。NAT Gateway とルートテーブルの
# for_each をこの az_key で揃えることで、AZ の取り違えを構造的に防ぐ
public_subnets_by_az = { for k, v in local.subnets : v.az_key => k if v.tier == "public" }
private_app_subnets_by_az = { for k, v in local.subnets : v.az_key => k if v.tier == "private-app" }
private_data_subnet_keys = { for k, v in local.subnets : k => k if v.tier == "private-data" }
}

for_eachはこの連載ではじめて使うメタ引数です。6 つの似た形のサブネットを個別のブロックとして書く代わりに、1 つのresourceブロックに定義のマップを渡し、Terraform にブロック分の展開をまかせます。ポイントは、public・private-app それぞれのfor_eachのキーを"az1"/"az2"という共通の文字列に揃えていることです。あるアベイラビリティゾーンの NAT Gateway が、別のアベイラビリティゾーンのルートテーブルに紐づいてしまうような取り違えを、この共通キーによって防ぎます。

アベイラビリティゾーンの一覧は、実行時にデータソースとして取得します。

data "aws_availability_zones" "available" {
state = "available"
}

VPC 本体とサブネットを作ります。

resource "aws_vpc" "main" {
cidr_block = var.vpc_cidr

# DNS ホスト名は既定 false のため明示する
enable_dns_support = true
enable_dns_hostnames = true

tags = {
Name = "${local.name_prefix}-vpc"
}
}

resource "aws_subnet" "this" {
for_each = local.subnets

vpc_id = aws_vpc.main.id
cidr_block = each.value.cidr
availability_zone = data.aws_availability_zones.available.names[local.az_key_to_index[each.value.az_key]]
map_public_ip_on_launch = each.value.public_ip_launch

tags = {
Name = "${local.name_prefix}-${each.key}"
}
}

enable_dns_hostnamesは既定でfalseのため、明示的にtrueを指定しています。24 aws_subnetのmap_public_ip_on_launchも既定はfalseで、25 public サブネットの 2 つだけeach.value.public_ip_launch経由でtrueを渡しています。

インターネットゲートウェイと、public サブネット用のルートテーブルです。

resource "aws_internet_gateway" "main" {
vpc_id = aws_vpc.main.id

tags = {
Name = "${local.name_prefix}-igw"
}
}

resource "aws_route_table" "public" {
vpc_id = aws_vpc.main.id

route {
cidr_block = "0.0.0.0/0"
gateway_id = aws_internet_gateway.main.id
}

tags = {
Name = "${local.name_prefix}-public-rt"
}
}

resource "aws_route_table_association" "public" {
for_each = local.public_subnets_by_az

subnet_id = aws_subnet.this[each.value].id
route_table_id = aws_route_table.public.id
}

続いて、NAT Gateway と private-app 用のルートテーブルです。

resource "aws_eip" "nat" {
for_each = local.public_subnets_by_az

domain = "vpc"

tags = {
Name = "${local.name_prefix}-nat-eip-${each.key}"
}

depends_on = [aws_internet_gateway.main]
}

resource "aws_nat_gateway" "this" {
for_each = local.public_subnets_by_az

allocation_id = aws_eip.nat[each.key].id
subnet_id = aws_subnet.this[each.value].id

tags = {
Name = "${local.name_prefix}-nat-${each.key}"
}

depends_on = [aws_internet_gateway.main]
}

resource "aws_route_table" "private_app" {
for_each = local.private_app_subnets_by_az

vpc_id = aws_vpc.main.id

route {
cidr_block = "0.0.0.0/0"
nat_gateway_id = aws_nat_gateway.this[each.key].id
}

tags = {
Name = "${local.name_prefix}-private-app-rt-${each.key}"
}
}

resource "aws_route_table_association" "private_app" {
for_each = local.private_app_subnets_by_az

subnet_id = aws_subnet.this[each.value].id
route_table_id = aws_route_table.private_app[each.key].id
}

aws_eipとaws_nat_gatewayのどちらにも、インターネットゲートウェイへのdepends_onを明示しています。EIP や NAT Gateway は内部的にインターネットゲートウェイの存在を前提にしており、暗黙の依存関係の解決順に任せるとエラーになることがあるため、明示するのが公式の推奨です。26 27

private-data 用のルートテーブルと、既定のセキュリティグループです。

resource "aws_route_table" "private_data" {
vpc_id = aws_vpc.main.id

tags = {
Name = "${local.name_prefix}-private-data-rt"
}
}

resource "aws_route_table_association" "private_data" {
for_each = local.private_data_subnet_keys

subnet_id = aws_subnet.this[each.value].id
route_table_id = aws_route_table.private_data.id
}

resource "aws_default_security_group" "main" {
vpc_id = aws_vpc.main.id
}

aws_default_security_groupは、他のリソースと性質が異なります。新しく何かを作るのではなく、VPC 作成時に自動でできる既定のセキュリティグループを Terraform の管理下に引き取り、ingress・egress のルールを何も書かないことで、既存のルールをすべて削除します。28

最後に、後続の章が参照する値をoutputs.tfにまとめます。

output "vpc_id" {
description = "第4章で作成したVPCのID (第6/8/9章で参照)"
value = aws_vpc.main.id
}

output "public_subnet_ids" {
description = "publicサブネット (ALB用、第6章で参照) のIDリスト"
value = [for k, v in aws_subnet.this : v.id if local.subnets[k].tier == "public"]
}

output "private_app_subnet_ids" {
description = "private-appサブネット (ECS Fargate用、第8/10章で参照) のIDリスト"
value = [for k, v in aws_subnet.this : v.id if local.subnets[k].tier == "private-app"]
}

output "private_data_subnet_ids" {
description = "private-dataサブネット (RDS用、第9章で参照) のIDリスト"
value = [for k, v in aws_subnet.this : v.id if local.subnets[k].tier == "private-data"]
}

output "nat_gateway_public_ips" {
description = "NAT GatewayのパブリックIP (動作確認・将来の外部API許可リスト用)"
value = [for k, v in aws_nat_gateway.this : v.public_ip]
}

この連載は第 11 章までモジュール化を行わない単一構成のため、後続の章はoutputs.tfを経由せずaws_subnet.this["public-az1"].idのように直接参照できます。それでもoutputs.tfを用意するのは、terraform outputで値を確認できるようにするためと、この章が公開する値を 1 か所にまとめておくためです。

4.9. 動作確認​

terraform fmt -check -recursive -diffとterraform validateを実行し、構文とプロバイダスキーマに問題がないことを確認します。29 この 2 つのコマンドが保証するのは構文とスキーマの整合までで、実際に AWS へapplyした結果までは保証しません。

terraform planを実行すると、次の 22 件の追加が計画に表示されるはずです。

  • aws_vpc 1 件
  • aws_subnet 6 件
  • aws_internet_gateway 1 件
  • aws_route_table (public) 1 件 + aws_route_table_association 2 件
  • aws_eip 2 件
  • aws_nat_gateway 2 件
  • aws_route_table (private-app) 2 件 + aws_route_table_association 2 件
  • aws_route_table (private-data) 1 件 + aws_route_table_association 2 件

aws_default_security_groupは新しくリソースを作るのではなく既存の既定セキュリティグループを引き取る特殊な動作のため、28 上の 22 件とは別枠で、変更として表示される可能性があります。

applyのあとは、terraform outputで VPC ID や NAT Gateway のパブリック IP を確認できます。NAT Gateway のパブリック IP は、外部の API を IP アドレスで許可リスト化するような場面で、後の章から参照することになります。

本章のまとめ​

  • VPC が持つ実質的な設計判断は、CIDR の割り当てとサブネットの区切り方に現れます。Tasuku は10.0.0.0/16に 2 AZ × 3 層 (public / private-app / private-data) の 6 サブネットを切り出しました
  • NAT Gateway はアベイラビリティゾーンごとに 2 個用意します。単一構成よりコストはかかりますが、第 1 章で決めた「1 アベイラビリティゾーン障害でも継続する」構成に必要な選択です
  • ルートテーブルは 4 枚で、public・private-app・private-data という 3 層の性質の違いが、そのままルートの中身の違いに表れています
  • セキュリティグループは、この章では既定のものを閉じるだけにとどめ、ALB・ECS・RDS 用の実体は使う章でそれぞれ作ります
  • for_eachをこの連載ではじめて導入し、6 つの同型なサブネットと NAT Gateway をまとめて定義しました

次に読む​

  • 第 5 章: S3: バケット設計原則と tfstate 管理の定石
  • 第 3 章: IAM: ポリシー JSON の読み書きと、Terraform 実行権限の現実解
  • 連載の目次: 全 12 章の構成と読み進め方

Footnotes​

  1. 出典: 第 1 章。ALB・ECS・RDS がいずれも作成時にサブネットの指定を要求すること、置き場となるサブネットを持つ VPC が先に必要であることを説明しています。 ↩

  2. 出典: VPC CIDR blocks。VPC の CIDR ブロックが/16netmask から/28netmask の範囲であることを示しています。 ↩ ↩2

  3. 出典: Subnet CIDR blocks。サブネット CIDR の先頭 4 個と末尾 1 個、計 5 個の IP アドレスが AWS によって予約されることと、その内訳を示しています。 ↩ ↩2 ↩3

  4. 出典: When there are not enough IP addresses for launching instances or scaling。使用可能 IP アドレス数を2^(32 - prefix length) - 5で計算する式と、/24が 251 個になる例を示しています。 ↩

  5. 出典: REL02-BP03 Ensure IP subnet allocation accounts for expansion and availability。将来の拡張のため VPC 内に未使用の CIDR 空間を残しておくことを推奨しています。 ↩

  6. 出典: Amazon ECS task networking options for Fargate。Fargate タスクには既定で ENI が 1 つ割り当てられ、1 つのタスクが同時に持てる ENI は 1 つだけだとしています。 ↩

  7. 出典: Amazon ECS task definition differences for Fargate。private サブネットの Fargate タスクがコンテナイメージを取得するには、インターネットへの経路をルーティングできる NAT Gateway が必要だとしています。 ↩ ↩2

  8. 出典: Best practices for creating a VPC for Amazon RDS for Db2。RDS のようにインターネットからアクセスされるべきでないリソースには private subnet を使うとしています。 ↩ ↩2

  9. 出典: Availability Zone IDs for your AWS resources。AWS がアベイラビリティゾーンの物理的な場所をアカウントごとにランダムな名前へ割り当てていることを示しています。 ↩

  10. 出典: AWS Availability Zones。ap-northeast-1の AZ ID がapne1-az1からapne1-az4の 4 つであることを示しています。 ↩

  11. 出典: Resource: aws_internet_gateway。vpc_idを指定するだけで VPC へのアタッチが完結するとしています。 ↩

  12. 出典: Pricing for NAT gateways。NAT Gateway が稼働時間と処理データ量の両方に対して課金されるとしています。 ↩

  13. 出典: Using the NAT gateway for centralized IPv4 egress。高可用性のためにはアベイラビリティゾーンごとに 1 つの NAT Gateway を使うべきだとしています。 ↩

  14. 出典: One to Many: Evolving VPC Design。NAT Gateway の高可用性は単一のアベイラビリティゾーンに閉じており、アベイラビリティゾーンを跨ぐ高可用性には最低 2 つの NAT Gateway が必要だとしています。 ↩

  15. 出典: Using NAT Gateways with multiple-Amazon VPCs at scale。NAT Gateway はアベイラビリティゾーンごとに動作するため、アベイラビリティゾーンごとに 1 つずつ配置することを推奨しています。 ↩

  16. 出典: VPC and Subnet Considerations。アベイラビリティゾーンをまたぐ独立性のため、アベイラビリティゾーンごとに NAT Gateway を作成することを推奨しています。 ↩

  17. 出典: 第 1 章。1 つのアベイラビリティゾーンが利用不可になってもサービスを継続する構成にすることを非機能要件として明示しています。 ↩

  18. 出典: Using NAT Gateways with multiple-Amazon VPCs at scale。同一アベイラビリティゾーン内で処理されるトラフィックの総コストは NAT Gateway の個数に依存せず、増えるのは稼働時間分の課金だけだと、具体例つきで説明しています。 ↩

  19. 出典: Introducing Amazon VPC Regional NAT Gateway。1 つの NAT Gateway がワークロードの存在するアベイラビリティゾーンへ自動的に展開される、リージョナルな可用性モードを紹介しています。 ↩

  20. 出典: 同上。「RNAT supports 5 Gbps of bandwidth for each AZ and automatically scales up to 100 Gbps」とあり、アベイラビリティゾーンごとに帯域を確保する動作をしていることを示しています。同ブログは料金の詳細を公式料金ページに委ねており、単一アベイラビリティゾーン相当の課金で済むとは述べていません。 ↩

  21. 出典: Security best practices for Amazon RDS for MySQL and MariaDB instances。RDS インスタンスを private subnet に配置し、同一 VPC 内のアプリケーションサーバーからのみアクセスを許可することを推奨しています。 ↩

  22. 出典: Security Hub CSPM controls for Amazon EC2。既定のセキュリティグループが inbound・outbound どちらのトラフィックも許可しないことを検証するコントロール[EC2.2]を定義しています。 ↩

  23. 出典: Security control recommendations for protecting infrastructure。既定のセキュリティグループは削除できないため、ルールを制限する設定に変更することを推奨しています。 ↩

  24. 出典: Resource: aws_vpc。enable_dns_hostnamesの既定値がfalseであるとしています。 ↩

  25. 出典: Resource: aws_subnet。map_public_ip_on_launchの既定値がfalseであるとしています。 ↩

  26. 出典: Resource: aws_nat_gateway。インターネットゲートウェイへの明示的な依存関係をdepends_onで追加することを推奨する例を示しています。 ↩

  27. 出典: Resource: aws_eip。EIP の関連付けにインターネットゲートウェイの存在が前提になる場合があり、depends_onで明示的な依存関係を設定するよう注記しています。 ↩

  28. 出典: Resource: aws_default_security_group。この resource は新規作成ではなく既存の既定セキュリティグループを管理下に「adopt」し、設定を書かなければ既存の ingress・egress ルールをすべて削除するとしています。 ↩ ↩2

  29. 出典: 連載の目次。本連載の Terraform コードはterraform fmtとterraform validateを通しており、validate が保証するのは構文と provider スキーマとの整合までで、実際に AWS へ apply した結果は保証しないと明示しています。 ↩