AWS VPCの構成要素—パブリック/プライベートサブネットの見分け方とNATの置き場所

新しく作ったVPCのルートテーブルを describe-route-tables で覗くと、最初はこれだけしか入っていません。

Destination      Target    Status   Propagated
10.0.0.0/16      local     active   No

ここに 0.0.0.0/0 → igw-xxxx の1行を足したサブネットが「パブリックサブネット」、足していないサブネットが「プライベートサブネット」です。コンソールのどこを探しても「このサブネットをパブリックにする」というチェックボックスは無くて、ルートテーブルの中身だけで決まる。調べてみて一番「そうだったのか」と思ったのがここでした。

IAMの4要素からAssumeRoleまで、ここ3本は「誰が何をできるか」の話でした。今回からはネットワーク側、「リソースをどこに置き、どこと通信できるか」に移ります。対象はVPC・サブネット・ルートテーブル・インターネットゲートウェイ(IGW)・NATゲートウェイの5つで、何が何にぶら下がっているか、ルートテーブルをどう読むか、NATをどこに置くかを、Amazon VPC User Guideの記述に沿って押さえます。なお、途中の手順でNATゲートウェイを作る箇所だけは時間課金が発生するので、そこは明示します。

目次

「パブリックサブネット」という設定項目は存在しない

見分け方はルートテーブルの 0.0.0.0/0 の向き先

公式ドキュメントの「Subnets for your VPC」には、サブネットの種類についてこう書かれています。

The subnet type is determined by how you configure routing for your subnets.

つまりサブネットの属性としてパブリック/プライベートがあるのではなく、関連付けたルートテーブルにインターネットゲートウェイ宛てのルートがあるかどうかで事後的に分類されるだけです。検索サジェストでよく出る「パブリックサブネット プライベートサブネット 見分け方」への答えは、ルートテーブルの 0.0.0.0/0 の行を見る、で尽きています。

同じページでは4種類に分けられていて、表にするとこうなります。

種類ルートテーブルの特徴置くものの例
Public subnet0.0.0.0/0 → igw-xxxx があるALB、踏み台、NATゲートウェイ
Private subnetIGW宛てのルートが無い。外に出るならNATデバイス経由アプリサーバー、ECSタスク
VPN-only subnet仮想プライベートゲートウェイ宛てのルートはあるがIGW宛ては無いオンプレとだけ話すサーバー
Isolated subnetVPCの外へのルートが一切無い(localのみ)RDS、ElastiCache

最後のIsolated subnetは、ルートテーブルに最初から入っている local だけの状態です。RDSのように「VPC内からしか叩かない」リソースは、NAT経由のルートすら要らないので、この形に落ち着くことが多いと思います。

パブリックIPがあってもルートが無ければ届かない

逆方向の勘違いもあります。「EC2にパブリックIPv4を付けたのにSSHできない」というケースで、セキュリティグループを何度見直しても直らないとき、原因がルートテーブルだったことがありました。「Enable internet access for a VPC using an internet gateway」のセクションに、図の説明としてこう書かれています。

Because there is no route to the internet gateway, instances in the private subnet can’t communicate with the internet, even if they have public IP addresses.

パブリックIPは「外から名指しできる住所」であって、「外に通じる道」ではありません。道はルートテーブルが担当します。インターネットと通信するために必要なものを公式の記述から拾うと、次の4つが揃って初めて成立します。

  • インターネットゲートウェイがVPCにアタッチされている
  • サブネットのルートテーブルに 0.0.0.0/0(IPv6なら ::/0)→ IGW のルートがある
  • リソースにパブリックIPv4アドレスまたはElastic IPが付いている
  • セキュリティグループとネットワークACLが該当トラフィックを許可している

4つ目のセキュリティグループとネットワークACLは次回まとめて扱うので、今回は上の3つに集中します。

前提—VPCはリージョン、サブネットはAZに属する

用語の前提を先に固定しておきます。VPCはリージョン単位のリソースで、複数のアベイラビリティーゾーン(AZ)をまたぎます。一方でサブネットは1つのAZに完全に収まる必要があり、AZをまたげません。公式には “Each subnet must reside entirely within one Availability Zone and cannot span zones.” とあります。「マルチAZ構成」とは、AZごとにサブネットの組を作り、同じ役割のリソースを各AZのサブネットに分散させることを指します。

5つの部品が何にぶら下がっているか

文章だけだと関係が頭に残らないので、東京リージョンに2AZ構成を組んだ場合の図を置きます。各部品が「どの箱の中にあるか」「どの箱に1つだけか」を見てください。

VPCの構成要素の包含関係 リージョンの中にVPCがあり、VPCの境界にインターネットゲートウェイが1つアタッチされる。VPCの中にAZごとのパブリックサブネットとプライベートサブネットがあり、パブリックサブネットにNATゲートウェイを置く。各サブネットのルートテーブルの0.0.0.0/0の向き先がigwかnatかで種類が決まる インターネット リージョン ap-northeast-1 インターネットゲートウェイ VPC 10.0.0.0/16 IGWはVPCに1つ AZ ap-northeast-1a パブリックサブネット 10.0.0.0/24 ルート: 0.0.0.0/0 → igw-xxxx NATゲートウェイ(Elastic IP付き) プライベートサブネット 10.0.10.0/24 ルート: 0.0.0.0/0 → nat-xxxx EC2 / ECS(パブリックIPなし) AZ ap-northeast-1c パブリックサブネット 10.0.1.0/24(+NAT) プライベートサブネット 10.0.11.0/24 ルートテーブルはサブネットごとに1つ関連付け (明示しなければメインルートテーブルが使われる)

VPCとCIDR—/16〜/28、あとから縮められない

VPCを作るときに必ず指定するのがIPv4のCIDRブロックで、許されるサイズは /16(65,536アドレス)から /28(16アドレス)までです。「VPC CIDR blocks」のページではRFC 1918のプライベート範囲(10.0.0.0/8172.16.0.0/12192.168.0.0/16)から取ることが推奨されていて、加えて 172.17.0.0/16 はCloud9やSageMaker AIが内部で使うので避けるように、という注意書きもあります。

サジェストの「vpc cidr 決め方」で迷う人が多い理由は、作成後にCIDRを変更・縮小できないからです。できるのはセカンダリCIDRの追加(デフォルトでVPCあたり5ブロックまで)と、セカンダリの切り離しだけで、プライマリCIDRは切り離せません。既存ブロックのサイズ変更も不可です。将来VPCピアリングやDirect Connectで他のネットワークと繋ぐ可能性があるなら、社内で重複しない範囲を最初に確保しておくのが無難で、個人的には迷ったら /16 を取って、サブネットを /24/20 で切っています。

サブネットとAZ—5つの予約アドレス

サブネットのCIDRはVPCのCIDRの部分集合で、サブネット同士は重複できません。サイズはVPCと同じく /16/28 です。ここで知っておくべきなのが、各サブネットの先頭4つと末尾1つはAWSが予約していて使えないという仕様です。「Subnet CIDR blocks」の説明を 10.0.0.0/24 に当てはめると次の通りです。

  • 10.0.0.0: ネットワークアドレス
  • 10.0.0.1: VPCルーター用にAWSが予約
  • 10.0.0.2: DNSサーバー用にAWSが予約(VPCの範囲の先頭+2がRoute 53 Resolverのアドレス)
  • 10.0.0.3: 将来のためにAWSが予約
  • 10.0.0.255: ブロードキャストアドレス(VPCはブロードキャスト非対応だが予約)

Pythonの ipaddress/16/24 に切りながら、使える数を数えてみます。

import ipaddress

vpc = ipaddress.ip_network("10.0.0.0/16")
print(f"VPC {vpc}: {vpc.num_addresses} addresses")
for i, sn in enumerate(vpc.subnets(new_prefix=24)):
    if i >= 3:
        break
    reserved = [sn.network_address + k for k in range(4)] + [sn.broadcast_address]
    print(f"  {sn}: reserved={[str(r) for r in reserved]} usable={sn.num_addresses - 5}")

for p in (28, 26, 24, 20):
    n = ipaddress.ip_network(f"10.0.0.0/{p}")
    print(f"/{p}: {n.num_addresses:>5} addresses -> usable {n.num_addresses - 5:>5}")
VPC 10.0.0.0/16: 65536 addresses
  10.0.0.0/24: reserved=['10.0.0.0', '10.0.0.1', '10.0.0.2', '10.0.0.3', '10.0.0.255'] usable=251
  10.0.1.0/24: reserved=['10.0.1.0', '10.0.1.1', '10.0.1.2', '10.0.1.3', '10.0.1.255'] usable=251
  10.0.2.0/24: reserved=['10.0.2.0', '10.0.2.1', '10.0.2.2', '10.0.2.3', '10.0.2.255'] usable=251
/28:    16 addresses -> usable    11
/26:    64 addresses -> usable    59
/24:   256 addresses -> usable   251
/20:  4096 addresses -> usable  4091

/28 だと16アドレスのうち11しか使えません。LambdaやECSのようにENIを大量に消費するサービスをプライベートサブネットに置くなら、/24 でも足りなくなることがあるので、「予約5個」込みで見積もるのがポイントです。

IGWはVPCに1つ、NATはサブネット(=AZ)に置く

図の中で「どこに1つか」が違う2つのゲートウェイを整理します。インターネットゲートウェイはVPCにアタッチする部品で、1つのVPCに同時にアタッチできるIGWは1つです(クォータの備考に “You can attach only one internet gateway to a VPC at a time.” とあります)。特定のサブネットに属するものではなく、VPC全体の「外への出口」です。

一方NATゲートウェイはサブネットの中に作るので、必然的に1つのAZに属します。「NAT gateway basics」の冒頭は “Each NAT gateway is created in a specific Availability Zone and implemented with redundancy in that zone.” で、続けて、そのAZが落ちると他のAZからそのNATを使っているリソースもインターネットに出られなくなるため、AZごとにNATを作りルーティングも同じAZに向けるように、と書かれています。図でAZ-cにもNATを置いているのはこのためです。

ルートテーブルの読み方—local・メイン・最長一致

localルートは消せない

ルートテーブルの各行は「Destination(宛先CIDR)」と「Target(送り先)」の組です。そしてどのルートテーブルにも、VPCのCIDRを宛先にした local というルートが最初から入っていて、これはVPC内通信の既定ルートで削除できません。VPCにセカンダリCIDRを足すと、そのCIDR用の local ルートも自動で追加されます。

面白いのは、local より狭い宛先のルートなら追加できる点です。「Subnet route tables」の Rules and considerations にある通り、宛先がVPC内のサブネットのCIDR全体と一致していれば、ターゲットとしてNATゲートウェイ・ネットワークインターフェース・Gateway Load Balancerエンドポイントを指定できます。サブネット間の通信をファイアウォールアプライアンスに通す構成はこれで組みます。基礎の段階では「そういう穴が用意されている」と覚えておけば十分だと思います。

メインルートテーブルと暗黙の関連付け

VPCを作るとメインルートテーブルが自動で1つ付いてきます。サジェストに「vpc ルートテーブル メイン」が出るのは、この扱いが分かりにくいからでしょう。ルールを公式から拾うと次の通りです。

  • サブネットは必ず1つのルートテーブルに関連付く。明示的に関連付けなければ、暗黙にメインルートテーブルが使われる
  • 1つのサブネットに関連付くルートテーブルは同時に1つだけ。逆に1つのルートテーブルを複数のサブネットで共有するのは可
  • メインルートテーブルは削除できない。ルートの追加・削除・変更はできる
  • カスタムルートテーブルは、関連付けが無い状態でのみ削除できる
  • 別のカスタムルートテーブルを「メイン」に昇格させることができる(replace-route-table-association)
メインルートテーブルカスタムルートテーブル
作られるタイミングVPC作成時に自動自分で作る
サブネットとの関係未指定のサブネットが暗黙に使う明示的に関連付けたサブネットだけが使う
削除不可関連付けが無ければ可
推奨される使い方local だけの初期状態のまま置いておくサブネットごとに用途別に作り、明示的に関連付ける

推奨の行は公式の “One way to protect your VPC is to leave the main route table in its original default state.” をそのまま採っています。メインにIGW宛てのルートを足してしまうと、以後うっかり作ったサブネットが全部パブリックになる、という事故が起きるためです。新しいサブネットは必ずカスタムルートテーブルに明示的に紐づける、と決めておくと安全です。

優先度は最長一致、同じ宛先なら静的ルートが勝つ

ルートテーブルに複数の行があるとき、どれが使われるかは「Route priority」のページに整理されています。原則は最長プレフィックス一致(longest prefix match)で、宛先が最も具体的なルートが選ばれます。

Destination       Target                   Propagated
10.0.0.0/16       local                    No
172.31.0.0/16     pcx-11223344556677889    No
0.0.0.0/0         igw-12345678901234567    No

この例では 172.31.0.5 宛ては /16 の方が /0 より長いのでピアリング接続へ、VPC内宛ては local へ、それ以外はIGWへ流れます。宛先が完全に同じルートが並んだ場合は、公式の優先順位リストの順で、①最長プレフィックス、②静的ルート(IGW・NAT・ピアリングなど手で足したもの)、③プレフィックスリストを参照するルート、④伝播ルート(Direct Connect BGP → VPN静的 → VPN BGP)、となります。「vpc ルートテーブル 伝播」のサジェストに出てくる伝播ルートは、仮想プライベートゲートウェイを付けて route propagation を有効にするとVPNの経路が自動で入ってくる仕組みのことで、静的ルートと宛先が被ると静的ルートが優先されます。

もう1つ、サジェストの「vpc ルートテーブル ブラックホール」は、ルートの Stateblackhole になった状態を指します。EC2 APIリファレンスの Route データ型には “The blackhole state indicates that the route’s target isn’t available” とあり、たとえばNATゲートウェイを削除したのにそれを指すルートが残っていると、ルートだけが blackhole で残り、そのサブネットからの外向き通信は黙って捨てられます。

インターネットゲートウェイとNATゲートウェイの違い

サジェストで最も多かったのが「インターネットゲートウェイ natゲートウェイ 違い」でした。両方とも「外に出る」ための部品なので混ざりやすいのですが、役割・置き場所・料金が全部違います。

インターネットゲートウェイNATゲートウェイ(パブリック)Egress-only IGW
方向双方向(外から開始する接続も通す)VPC内から開始する外向きのみIPv6の外向きのみ
置き場所VPCにアタッチ(1つ)パブリックサブネットの中(AZ単位)VPCにアタッチ
必要なIPリソース側にパブリックIPv4かEIPNAT自身にElastic IP(作成時必須)リソース側にIPv6
スケール水平スケール・冗長・帯域制約なし5 Gbps〜自動で100 Gbpsまで水平スケール・冗長
料金無料(データ転送料のみ)時間課金+処理データ量課金無料(データ転送料のみ)
セキュリティグループ対象外関連付け不可(NACLは効く)対象外

IGWは「1対1のNAT」も担当している

調べていて意外だったのは、インターネットゲートウェイ自身もNATをしているという記述です。EC2インスタンスは自分のプライベートIP(たとえば 10.0.0.45)しか知らず、パブリックIPv4やElastic IPはインスタンスのOSからは見えません。公式の「IP addresses and NAT」の節ではこう説明されています。

The internet gateway logically provides the one-to-one NAT on behalf of your instance, so that when traffic leaves your VPC subnet and goes to the internet, the reply address field is set to the public IPv4 address or Elastic IP address of your instance, and not its private IP address.

つまりIGWは「パブリックIP 1つ ⇔ プライベートIP 1つ」を対応付ける1対1のNATで、NATゲートウェイは「多数のプライベートIP → 1つのElastic IP」にまとめる多対1のNAT(いわゆるNAPT)です。IPv6はアドレスがグローバルに一意なのでIGWは変換せず、そのぶん「外向きだけ許す」ためにEgress-only IGWという別部品が用意されています。

料金面では、IGW自体は無料ですが、2024年2月以降はパブリックIPv4アドレスそのものに1アドレスあたり0.005 USD/時がかかります。プライベートサブネットにリソースを寄せる理由は、セキュリティだけでなくこのIPv4課金を抑える意味もあります。

NATゲートウェイの制約と料金

「NAT gateway basics」から、設計で効いてくる数字を抜き出します。

  • 帯域は5 Gbpsから自動で100 Gbpsまでスケール、パケット処理は100万pps〜1,000万pps。超えるとドロップされる
  • IPv4アドレス1つあたり、同一宛先(IP・ポート・プロトコルの組)に対して最大55,000同時接続。アドレスを最大8個(プライマリ1+セカンダリ7)まで付けて上限を上げられる
  • パブリックNATゲートウェイに関連付けられるElastic IPはデフォルト2個まで(クォータ引き上げで8個まで)
  • セキュリティグループは関連付けできない。制御したければサブネットのネットワークACLで行う。NATは送信元ポート1024〜65535を使う
  • AZあたり5個までがデフォルトクォータ
  • 東京リージョンの料金は0.062 USD/時 + 処理データ1 GBあたり0.062 USD。置いておくだけで月額およそ45 USD、2AZなら倍

最後の料金が、個人の検証環境でNATゲートウェイを作りっぱなしにして後悔する定番ポイントです。料金のページにある節約策は2つで、NATと同じAZにリソースを置いてAZ間転送を避けること、S3やDynamoDBへの通信が主ならゲートウェイ型VPCエンドポイントを作ってNATを経由させないことです。後者は追加料金なしで、NATのデータ処理料金を丸ごと削れます。

2025年11月追加のRegional NATゲートウェイ—パブリックサブネットが要らなくなる

サジェストに「natゲートウェイ リージョナル」が混ざっていたので調べたところ、2025年11月19日のWhat’s NewでNATゲートウェイのリージョナル可用性モードが発表されていました。従来のNATゲートウェイは「zonal NAT gateway」と呼び分けられ、公式ガイドの「Regional NAT gateways for automatic multi-AZ expansion」には次のように書かれています。

  • リージョナルNATゲートウェイはパブリックサブネットに置く必要がない。VPCを指定して作るだけで、ワークロード(ENI)が存在するAZに自動で展開・収縮する
  • 展開には最大60分かかり、それまでは既存AZのNATがクロスAZで処理する
  • 作成時にIGW宛てのルートを持つ専用ルートテーブルが自動で作られる
  • AZあたり最大32 IPアドレス(zonalは8)
  • プライベート接続タイプ(private NAT)は非対応。その用途はzonalのまま
  • GovCloudと中国リージョンを除く全商用リージョンで利用可能
aws ec2 create-nat-gateway --vpc-id vpc-0a1b2c3d4e5f67890 --availability-mode regional
{
    "NatGateway": {
        "NatGatewayId": "nat-0f1e2d3c4b5a69788",
        "VpcId": "vpc-0a1b2c3d4e5f67890",
        "State": "pending",
        "ConnectivityType": "public",
        "AvailabilityMode": "regional"
    }
}

上の構成図は、基礎を押さえる目的で従来のzonal構成(AZごとにパブリックサブネット+NAT)で描いています。既存の教材や現場の構成図はほぼこの形なので、まずこれを読めるようになってから、「新規ならリージョナルで公開サブネットを減らせる」という選択肢を持つ、という順番が良いと思います。

手順—2層構成をCLIで組む順番

部品の関係が分かったところで、図の構成をAWS CLI(v2.35系)で組む順番をステップにします。各コマンドは「Add internet access to a subnet」「Work with NAT gateways」で案内されているものです。ステップ6以降だけ課金が発生するので、試すときはそこで一度止めて考えてください。EC2 APIの書き込み系コマンドは --dry-run を付けると権限チェックだけ行って DryRunOperation エラーで返ってくるので、権限の確認だけしたい場合はそれで足ります。

1
VPCを作る。aws ec2 create-vpc --cidr-block 10.0.0.0/16 を実行し、返ってくる Vpc.VpcId(vpc-0a1b…)を控える。この時点でメインルートテーブルとデフォルトNACL・デフォルトSGが自動で付いてくる
2
サブネットをAZ指定で2つ作る。aws ec2 create-subnet --vpc-id vpc-0a1b… --cidr-block 10.0.0.0/24 --availability-zone ap-northeast-1a(パブリック用)と、--cidr-block 10.0.10.0/24(プライベート用)。どちらもまだメインルートテーブルに暗黙で関連付いた、localだけの状態
3
IGWを作ってVPCにアタッチする。aws ec2 create-internet-gatewayigw-… を得て、aws ec2 attach-internet-gateway --internet-gateway-id igw-… --vpc-id vpc-0a1b…。アタッチしただけでは、まだどのサブネットもパブリックにならない
4
パブリック用ルートテーブルを作り、IGWルートを足し、サブネットに関連付ける。aws ec2 create-route-table --vpc-id vpc-0a1b…aws ec2 create-route --route-table-id rtb-pub… --destination-cidr-block 0.0.0.0/0 --gateway-id igw-…aws ec2 associate-route-table --route-table-id rtb-pub… --subnet-id subnet-pub…。この3つ目でようやく「パブリックサブネット」になる
5
パブリックサブネットの自動パブリックIP割り当てを有効にする。aws ec2 modify-subnet-attribute --subnet-id subnet-pub… --map-public-ip-on-launch。デフォルトVPC以外のサブネットはこの属性がオフなので、忘れると起動したEC2にパブリックIPが付かない
6
【ここから課金】Elastic IPを確保し、パブリックサブネットにNATゲートウェイを作る。aws ec2 allocate-address --domain vpceipalloc-… を得て、aws ec2 create-nat-gateway --subnet-id subnet-pub… --allocation-id eipalloc-…--subnet-id に指定するのは必ずパブリックサブネット。状態が pending から available になるまで数分かかる。IGWをアタッチしていないVPCで実行すると failed になり、FailureCodeGateway.NotAttached が入る
7
プライベート用ルートテーブルを作り、NATルートを足し、プライベートサブネットに関連付ける。aws ec2 create-route-table --vpc-id vpc-0a1b…aws ec2 create-route --route-table-id rtb-priv… --destination-cidr-block 0.0.0.0/0 --nat-gateway-id nat-…aws ec2 associate-route-table --route-table-id rtb-priv… --subnet-id subnet-priv…

組み終わったら、VPC内のルートテーブルを一覧して「どのサブネットがどのテーブルに紐づき、0.0.0.0/0がどこを向いているか」を確認します。

aws ec2 describe-route-tables \
  --filters Name=vpc-id,Values=vpc-0a1b2c3d4e5f67890 \
  --query 'RouteTables[].{Id:RouteTableId,Main:Associations[0].Main,Subnet:Associations[0].SubnetId,Routes:Routes[].[DestinationCidrBlock,GatewayId||NatGatewayId,State]}' \
  --output json
[
    {
        "Id": "rtb-0c9d8e7f6a5b43210",
        "Main": true,
        "Subnet": null,
        "Routes": [["10.0.0.0/16", "local", "active"]]
    },
    {
        "Id": "rtb-0a1b2c3d4e5f67891",
        "Main": false,
        "Subnet": "subnet-0f1e2d3c4b5a69700",
        "Routes": [["10.0.0.0/16", "local", "active"],
                   ["0.0.0.0/0", "igw-0e1d2c3b4a5f60987", "active"]]
    },
    {
        "Id": "rtb-0a1b2c3d4e5f67892",
        "Main": false,
        "Subnet": "subnet-0f1e2d3c4b5a69701",
        "Routes": [["10.0.0.0/16", "local", "active"],
                   ["0.0.0.0/0", "nat-0f1e2d3c4b5a69788", "active"]]
    }
]

メインルートテーブルには関連付けが無く(Subnet: null)、local だけが残っている。これが「メインは触らない」状態です。検証が終わったら、NATゲートウェイの削除とElastic IPの解放(delete-nat-gatewayrelease-address)を忘れないようにしてください。Elastic IPは関連付けを外した状態でも課金対象です。

つまずきポイント—プライベートサブネットから外に出られない

最後に、構成を組んだあとで一番よくある「プライベートサブネットのEC2が yum update もできない」系の症状を、パケットの通り道に沿って切り分けます。鍵になるのは、NATゲートウェイ自身も、自分が置かれたサブネットのルートテーブルに従うという点です。

プライベートサブネットからインターネットへ出るときの経路 プライベートサブネットのEC2が送った通信は、プライベート側ルートテーブルの0.0.0.0/0でNATゲートウェイへ送られ、NATが送信元アドレスを自分のプライベートIPに変換し、NATが置かれたパブリックサブネットのルートテーブルの0.0.0.0/0でインターネットゲートウェイへ送られ、IGWがElastic IPに1対1変換してインターネットへ出る5段階 プライベートサブネットのEC2 送信元 10.0.10.25 → 宛先 203.0.113.10 ① 外向きのパケットを送る プライベート側ルートテーブル 0.0.0.0/0 → nat-xxxx(最長一致で選ばれる) ② NATゲートウェイへ転送 NATゲートウェイ(パブリックサブネット内) 送信元を 10.0.10.25 → 10.0.0.45 に変換 (NAT自身のプライベートIP) ③ NATのサブネットのルートを参照 パブリック側ルートテーブル 0.0.0.0/0 → igw-xxxx この行が無いとNATは外に出られない ④ インターネットゲートウェイへ インターネットゲートウェイ 10.0.0.45 → Elastic IP に1対1変換 ⑤ 外へ(応答は同じ道を逆にたどる) インターネット ルートテーブルは2回参照される

NATを置いたサブネットがパブリックになっていない

図の③④で詰まるパターンです。NATゲートウェイを作るときに --subnet-id へプライベートサブネットを渡してしまうと、NATは外向きのパケットを受け取っても、自分のサブネットのルートテーブルにIGW宛てのルートが無いので外に出せません。コンソールの作成画面でもサブネットはプルダウンで選ぶだけなので、チェックは効きません。NATの SubnetIddescribe-nat-gateways で確認し、そのサブネットのルートテーブルに 0.0.0.0/0 → igw があるかを見てください。

ルートの State が blackhole になっている

NATゲートウェイを作り直したり、IGWをデタッチしたりすると、古いIDを指していたルートは blackhole になります。describe-route-tables の出力で "State": "blackhole" の行があれば、そのルートを新しいターゲットに replace-route で差し替えます。エラーは出ず、パケットが静かに消えるのが厄介なところです。

NATは正しいのに通信が通らない—AZとSG/NACLを疑う

ルートが全部正しいのに通らないときは、ルートテーブル以外の層です。NATがAZ-aにあってリソースがAZ-cにいる構成でAZ-aが障害中、というケースは「NAT gateway basics」が明示的に注意している状況ですし、NATが使うポート1024〜65535をネットワークACLの戻り方向で塞いでいるケースもあります。ネットワークACLの戻り方向(エフェメラルポート)の扱いは、セキュリティグループとの違いとあわせて次回のテーマにします。

まとめ

  • パブリック/プライベートはサブネットの属性ではなく、関連付けたルートテーブルに 0.0.0.0/0 → igw があるかどうかで決まる。公式の分類は public / private / VPN-only / isolated の4つ
  • VPCはリージョン単位、サブネットは1つのAZに収まる。CIDRは /16/28 で、プライマリCIDRは後から変更できない。各サブネットの先頭4+末尾1の5アドレスは予約済み
  • ルートテーブルの local は削除不可。メインルートテーブルは初期状態のまま残し、サブネットは用途別のカスタムルートテーブルに明示的に関連付ける
  • ルートの選択は最長プレフィックス一致が原則で、同じ宛先なら静的ルートが伝播ルートに勝つ。ターゲットが消えたルートは blackhole になって通信を黙って捨てる
  • IGWはVPCに1つ・無料・双方向・1対1NAT。NATゲートウェイはパブリックサブネット内にAZ単位で置き、Elastic IP必須、東京で0.062 USD/時+0.062 USD/GB、セキュリティグループは付けられない
  • NATゲートウェイ自身も自分のサブネットのルートテーブルに従うため、置き場所を間違えると外に出られない。2025年11月のリージョナルNATゲートウェイならパブリックサブネット自体が不要になる
よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!
目次