こんにちは!クラウドの海を日々漂いながら、パケットの行く末を見守るSREのテックリードです。
「クラウドは使った分だけ払えばいいから安上がり」なんて言葉を信じて構築を始めたものの、月末の請求書を見て「えっ、この『Data Transfer』って何!? なんでこんなに高いの?」と椅子から転げ落ちそうになった経験、ありませんか?
実は、AWSやGCPといったメガクラウドの世界には、「アベイラビリティゾーン(AZ)という境界線をまたぐ時に発生する通行料」という、初心者泣かせの隠れたコストが存在します。
今日は、VPC設計の要となる「NATゲートウェイ」をテーマに、お財布に優しく、かつ障害に強いネットワークをどう作るべきか、郵便配達の仕組みに例えて丁寧に紐解いていきましょう!
—
1. そもそも「プライベートサブネット」と「NATゲートウェイ」の関係って?
まずは基本のキから。ネットワークの世界を「セキュリティ万全のオフィスビル」に例えてみましょう。
- パブリックサブネット: ビルの「受付ロビー」です。外の人が自由に出入りできます。
- プライベートサブネット: ビルの「奥にある機密執務室」です。部外者は立ち入り禁止。インターネットからも直接アクセスできません。
- NATゲートウェイ: 執務室(プライベート)から外へ手紙を出したい時にだけ動いてくれる「専属の郵便局員さん」です。
プライベートサブネットに置かれたサーバー(EC2など)は、セキュリティのために外からの通信を遮断していますが、OSのアップデートや外部APIの呼び出しのために「自分から外へ繋ぎたい」時があります。その時、この郵便局員さん(NATゲートウェイ)に手紙を託して、外の世界へ届けてもらうのです。
—
2. 「1人体制」の罠:クロスAZデータ転送コスト
さて、ここからが本題です。
AWSなどのクラウドには、物理的に離れたデータセンター群である「AZ(アベイラビリティゾーン)」が複数あります。例えば「東京リージョンのAZ-A」と「AZ-C」のような形です。
コストを節約しようとして、「NATゲートウェイは1つだけ(AZ-Aに配置)にして、AZ-AとAZ-Cの両方のサーバーで共有しよう!」と設計したとします。これが「1人体制」の構成です。
郵便配達で考える「通行料」
AZ-Aにいるサーバーが手紙を出すのはスムーズです。同じ町内に郵便局があるからです。
しかし、AZ-Cにいるサーバーが手紙を出そうとすると、どうなるでしょうか?
1. AZ-Cのサーバーが手紙を書く。
2. わざわざ隣の町(AZ-A)まで重い荷物を運んでいく。
3. AZ-AのNATゲートウェイがそれを受け取ってインターネットへ出す。
この「隣の町(AZ)へ荷物を運ぶ」という行為に対して、クラウド事業者は「AZ間データ転送費用(Cross-AZ Data Transfer Cost)」という通行料を請求します。
「1GBあたり数円でしょ?」と侮ることなかれ。ログの転送や大きなファイルのダウンロードが頻繁に起きる環境では、この通行料が積み重なって、NATゲートウェイ自体の利用料よりも遥かに高額な「謎の課金」として牙を剥くのです。
—
3. 「各町内配置」のすすめ:高可用性とコスト最適化
この問題を解決するのが、「各AZに1つずつNATゲートウェイを置く」という冗長化パターンです。
「えっ、NATゲートウェイを2つに増やしたら、基本料金が2倍になっちゃうじゃないですか!」
その通りです。NATゲートウェイの設置には「1時間あたりの稼働費」がかかります。しかし、中〜大規模な通信が発生するシステムでは、「基本料金の増加」よりも「AZ間通行料の削減」の方が、結果的に安くなるケースが非常に多いのです。
さらに、もう一つ決定的なメリットがあります。それは「高可用性(壊れにくさ)」です。
- 1つだけ配置の場合: AZ-Aがもし災害などでダウンしたら、AZ-Cのサーバーも道連れでインターネットに出られなくなります。全滅です。
- 各AZ配置の場合: AZ-Aがダウンしても、AZ-Cのサーバーは自分の町内の郵便局を使って平然と通信を続けられます。
—
4. 実践!Terraformで構築する「賢い」NATゲートウェイ設計
それでは、実際にどのように構築するのか、インフラをコードで管理する「Terraform(テラフォーム)」の例を見てみましょう。
「各AZにNATゲートウェイを作る」という処理を、1つずつ手動でやるのではなく、ループを使ってスマートに記述します。
# 1. まずは利用可能なAZのリストを取得します
data "aws_availability_zones" "available" {
state = "available"
}
# 2. 各AZごとにNATゲートウェイ用の「固定IP(EIP)」を確保します
resource "aws_eip" "nat" {
# AZの数だけ作成するループ処理です
count = length(data.aws_availability_zones.available.names)
vpc = true
tags = {
Name = "nat-eip-${data.aws_availability_zones.available.names[count.index]}"
}
}
# 3. 本題!各AZの「パブリックサブネット」にNATゲートウェイを配置します
resource "aws_nat_gateway" "main" {
# ここでもAZの数だけ作成します
count = length(data.aws_availability_zones.available.names)
# 自分のAZにある固定IPを紐付けます
allocation_id = aws_eip.nat[count.index].id
# 自分のAZにあるパブリックサブネットに設置します
# これにより、通信がAZをまたがない「地産地消」の形になります
subnet_id = aws_subnet.public[count.index].id
tags = {
Name = "nat-gw-${data.aws_availability_zones.available.names[count.index]}"
}
}
# 4. 各AZのプライベートサブネット用ルートテーブルを設定
resource "aws_route_table" "private" {
count = length(data.aws_availability_zones.available.names)
vpc_id = aws_vpc.main.id
route {
# 全てのインターネット宛通信(0.0.0.0/0)を
# 「同じAZにあるNATゲートウェイ」へ流すように設定します
cidr_block = "0.0.0.0/0"
nat_gateway_id = aws_nat_gateway.main[count.index].id
}
tags = {
Name = "private-rt-${data.aws_availability_zones.available.names[count.index]}"
}
}
この設定のポイントは、nat_gateway_id = aws_nat_gateway.main[count.index].id の部分です。これにより、AZ-AのサーバーはAZ-AのNATへ、AZ-CのサーバーはAZ-CのNATへ、という「最短ルート」が自動的に構成されます。
—
5. まとめ:どっちを選べばいいの?
最後に、判断基準を整理しましょう。
| 項目 | 1つのNAT GWを共有 | 各AZにNAT GWを配置 |
| :— | :— | :— |
| 初期コスト | 安い(1台分の稼働費) | 高い(AZ数分の稼働費) |
| 通信コスト | 高い(AZ間転送費が発生) | 安い(AZ間転送費ゼロ) |
| 信頼性(HA) | 低い(1箇所壊れると全滅) | 高い(AZごとに独立) |
| 適したフェーズ | 開発環境・超小規模システム | 本番環境・データ通信量が多いシステム |
初心者のうちは「まずは1つ」で始めても構いません。ですが、サービスが成長し、データ転送量が増えてきたら、必ずこの「地産地消のネットワーク設計」を思い出してください。
「パケットがどこを通って、どこでお金が発生しているのか」を意識できるようになれば、あなたはもう立派なクラウドエンジニアの仲間入りです。
一歩ずつ、理想のインフラを作り上げていきましょう!応援しています。
コメント