はじめに
2025年11月よりNATゲートウェイにリージョナルモードなるものが登場しました。
パブリックサブネットが必要ない・別途EIPの取得が不要など、いくつかメリットがあります。
本記事ではTerraformでの構築を試してみます。
ゾーナルモードとリージョナルモードの違い
NATゲートウェイには現在2つのモードがあります。
ゾーナルモード(従来)
これまで一般的だった構成です。NATゲートウェイは単一のAZで動作します。
AZごとにNATを置き、プライベートサブネットからは「同じAZのNAT」に向くようにルートテーブルを分ける、というパターンです。AZ障害時に他AZのトラフィックが巻き込まれないようにするための工夫ですが、リソースの数とルートテーブルの本数が増えがちです。
リージョナルモード
NATゲートウェイ1台で複数AZをカバーします。
ポイントは以下の3つです。
- パブリックサブネットが不要:リージョナルNATゲートウェイは独立したリソースとして動作し、AWSが内部的にルートテーブル(IGWへのルート設定済み)を自動生成します。
- AZへの自動拡張・縮小:プライベートサブネット内のENI(≒EC2やLambdaなど)を検知して、必要なAZへ自動で広がります。AZにワークロードがなくなれば縮小します。
- ルートテーブルが1本でOK:複数AZのプライベートサブネットから同じNATゲートウェイIDに向けて1本のルートを書くだけで済みます。
新しいAZへワークロードが配置されてから、リージョナルNATゲートウェイがそのAZに拡張するまで 最大60分 かかります。拡張完了までは既存AZのNATを経由する形でトラフィックは処理されるため、通信が止まることはありません。
比較
| 観点 | ゾーナル | リージョナル |
|---|---|---|
| NATゲートウェイの数 | AZごとに1台 | VPC内に1台 |
| パブリックサブネット | 必要 | 不要 |
| ルートテーブル | AZごとに分割 | 1本で共有可能 |
| AZ拡張時の作業 | NAT追加・RT追加 | 不要(自動拡張) |
| AZあたりIPアドレス上限 | 8 | 32 |
| プライベートNAT | 対応 | 非対応 |
| 課金単位(時間) | NAT 1台あたり | AZあたり |
| 課金単位(データ処理) | データ処理料金(同額) | データ処理料金(同額) |
料金には注意が必要です。 リージョナルモードは「AZごとに時間料金がかかる」課金体系のため、3AZに展開している間は3AZぶんの時間料金が発生します。データ処理料金(GBあたり)はゾーナル・リージョナル共通です。最新の正確な料金は Amazon VPC Pricing を確認してください。
プライベートNATには非対応 です。NATゲートウェイをオンプレやTransit Gateway方面の通信用(プライベートNAT)として使う場合は、引き続きゾーナルモードを利用する必要があります。
構築するAWS構成
今回は動作確認用にシンプルな構成で試します。
- VPC(10.10.0.0/16)
- プライベートサブネット(10.10.1.0/24、ap-northeast-1a)
- インターネットゲートウェイ
- NATゲートウェイ(リージョナルモード)
- プライベートサブネットからNATゲートウェイへのルート
- EC2インスタンス(SSM接続用、Windows)
リージョナルモードの特徴である「パブリックサブネット不要」を実感できる構成にしています。
ディレクトリ構成
regional_natgateway/
├── main.tf # プロバイダー定義・モジュール呼び出し
├── terraform.tf # Terraformバージョン・プロバイダーバージョン
├── variables.tf # 変数定義
└── modules/
├── variables.tf # モジュール内の変数定義
├── vpc.tf # VPC・サブネット・NAT・RT・IGW
├── security_group.tf # セキュリティグループ
└── ec2.tf # EC2インスタンス
コード解説
terraform.tf(バージョン定義)
terraform {
required_version = "~> 1.15.0"
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 6.44.0"
}
}
}
availability_mode の指定は AWSプロバイダーの6.24.0以上のバージョンが必要です。古いバージョンを使っているプロジェクトでは併せてバージョンアップが必要になる場合があります。
main.tf(モジュール呼び出し)
provider "aws" {
region = "ap-northeast-1"
}
module "sample_regional_natgateway" {
source = "./modules"
project = var.project
}
variables.tf
variable "project" {
type = string
default = "regional_nat_gateway"
}
modules/vpc.tf
# VPC
resource "aws_vpc" "main" {
cidr_block = "10.10.0.0/16"
instance_tenancy = "default"
enable_dns_support = true
enable_dns_hostnames = true
tags = {
Name = "${var.project}-vpc"
}
}
# プライベートサブネット
resource "aws_subnet" "prisub_a" {
vpc_id = aws_vpc.main.id
cidr_block = "10.10.1.0/24"
availability_zone = "ap-northeast-1a"
tags = {
Name = "${var.project}-prisuba"
}
}
# プライベートルートテーブル(NATゲートウェイへ)
resource "aws_route_table" "prirt_a" {
vpc_id = aws_vpc.main.id
route {
cidr_block = "0.0.0.0/0"
gateway_id = aws_nat_gateway.main.id
}
tags = {
Name = "${var.project}_prirt"
}
}
resource "aws_route_table_association" "prirt_associate_a" {
subnet_id = aws_subnet.prisub_a.id
route_table_id = aws_route_table.prirt_a.id
}
# インターネットゲートウェイ
resource "aws_internet_gateway" "main" {
vpc_id = aws_vpc.main.id
}
# NATゲートウェイ(リージョナルモード)
resource "aws_nat_gateway" "main" {
vpc_id = aws_vpc.main.id
availability_mode = "regional"
tags = {
Name = "${var.project}_NatGW"
}
}
ゾーナルモードでは必要だった subnet_id と allocation_id(EIP)が、リージョナルモードでは不要になります。vpc_id を指定するだけでVPC全体をカバーするNATゲートウェイが構築できます。
resource "aws_nat_gateway" "main" {
vpc_id = aws_vpc.main.id
availability_mode = "regional"
# subnet_id は不要
# allocation_id も不要(EIP不要)
}
リージョナルNATゲートウェイには 自動モード と 手動モード があります。
- 自動モード(推奨):IPアドレスとAZ拡張をAWSがすべて管理
- 手動モード:自分でIPアドレスと各AZのNAT展開を制御
特に要件がなければ、上記のように availability_mode = "regional" だけ指定すれば自動モードで作成されます。BYOIP(自前のIPアドレス)を持ち込みたい場合などは手動モードを使います。
modules/ec2.tf(接続テスト用EC2)
data "aws_key_pair" "ec2_key_pair" {
key_name = "your_key_pair_name"
}
data "aws_iam_role" "ec2_ssm_role" {
name = "AmazonSSMRoleForInstancesQuickSetup"
}
resource "aws_instance" "main" {
ami = "ami-xxxxxxxxxxxxxxxxxx"
instance_type = "t3.medium"
key_name = data.aws_key_pair.ec2_key_pair.key_name
subnet_id = aws_subnet.prisub_a.id
vpc_security_group_ids = [aws_security_group.sg_ec2.id]
iam_instance_profile = data.aws_iam_role.ec2_ssm_role.id
tags = {
Name = "${var.project}_ec2"
}
}
プライベートサブネットに置いたEC2にSSMフリートマネージャーやセッションマネージャーで接続して、外部への通信を確認します。
modules/security_group.tf
resource "aws_security_group" "sg_ec2" {
name = "${var.project}_sg_ec2"
vpc_id = aws_vpc.main.id
ingress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["10.10.0.0/16"]
}
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
tags = {
Name = "${var.project}_sg_ec2"
}
}
実行と動作確認
構築
terraform init
terraform plan
terraform apply
apply が完了すると、マネジメントコンソールのNATゲートウェイ画面に Availability mode: Regional のNATゲートウェイが1つだけ作成されているはずです。Subnet IDの欄が空になっていて、サブネット紐付けがないのが従来との見た目の違いです。
動作確認
以下の画面のようにFleetManagerで接続できれば成功です。
EC2からWeb検索ができるか確認するのも良いと思います。
後片付け
terraform destroy
まとめ
- リージョナルモードは1つのNATゲートウェイで複数AZをカバーできる新しい構成
- パブリックサブネットやEIPの定義が不要になるため構成がシンプルにできる
- Terraformでは
availability_mode = "regional"を指定するだけで利用可能 - プライベートNATが必要な場合は引き続きゾーナルモードを使う
公式サイトにもあるように、今後はリージョナルモードを利用するのが推奨構成となりました。
この機能に気づくのに半年空いてしまったので今後は情報収集をしっかりしようと反省しています💦
