【実務・中級編】 パブリックサブネットの定義とインターネットゲートウェイ(IGW)の接続 – クラウド&コンテナネットワーク実践ガイド

クラウドの世界へようこそ。AWSのVCPやGCPのVPC、そしてKubernetesのポッドネットワークを横断しながら、「パケットの気持ち」になってインフラの挙動を追うのが我々SREの醍醐味だ。

今日は、クラウドネットワークの基本中の基本でありながら、いまだに「なぜこのルートテーブルが必要なのか」「パケットは境界線でどう化けるのか」という本質を見落としがちなテーマを取り上げる。

「パブリックサブネットの定義とインターネットゲートウェイ(IGW)の接続」だ。

教科書通りの「パブリック=外からアクセスできる場所」というフワッとした理解を捨て、ルーティングの数学的必然性と、NATゲートウェイやIGWが裏で行っている泥臭いパケット書き換え(SNAT/DNAT)のリアルな挙動を、現場の視点から徹底的に解き明かしていこう。

—

1. パブリックサブネットとは何か?「パブリック」の正体を暴く

まず大前提として、クラウドのVPC内において、サブネット自体に「パブリック」「プライベート」という魔法のフラグが最初から焼き付けられているわけではない。

AWSのVPCやGCPのVPCを思い出してほしい。サブネットを作成するときに指定するのは、単にCIDRブロック(例: 10.0.1.0/24)だけだ。では、何がそのサブネットを「パブリック」たらしめているのか?

答えはシンプル。「インターネットゲートウェイ(IGW)へのルート(経路)を持つルートテーブルが関連付けられているかどうか」、これだけだ。

プライベートサブネットとの決定的な違い

  • プライベートサブネット: トラフィックの宛先がVPC内(local)か、せいぜい同一リージョン内のVPCピアリングやTGW(Transit Gateway)に限られており、デフォルトルート(0.0.0.0/0)の向け先がNATゲートウェイや仮想プライベートゲートウェイを向いている。
  • パブリックサブネット: ルートテーブルに 0.0.0.0/0 のターゲットとして IGW が明示的に設定されている。

つまり、「パブリックサブネット」とは、外の世界(インターネット)へ直接パスポートなしで飛び出せる片道切符、あるいは世界中から直接キャッチボールができるグラウンドのようなものだ。

—

2. パケットの往復運動:IGWの接続とルーティングの裏側

パブリックサブネットにあるリソース(例えば、Nginxを動かしているEC2インスタンス)に、インターネット上のユーザーがアクセスするシーンを想像してほしい。ここで不可欠なのが、ルーティングとアドレス変換(NAT)のコンビネーションだ。

シーケンス:パケットがIGWを通過する瞬間

1. クライアントからのリクエスト(外 → 中)

  • ユーザーのブラウザから、パブリックIPアドレス(Elastic IP等)を持つEC2のプライベートIP宛てにTCP SYNパケットが飛ぶ。
  • パケットは世界中のルーターをホップし、クラウドプロバイダーのEdgeルーターに到達。そこでIGWを通過する。
  • この時、IGWは宛先IPアドレスをパブリックIPから、EC2が持つVPC内のプライベートIPアドレス(例: 10.0.1.100)に書き換える(DNAT: Destination NAT)。

2. EC2での処理とレスポンス(中 → 外)

  • EC2は自身のIPが 10.0.1.100 であると認識してリクエストを受け取り、Nginxがレスポンスを返す。
  • 送信元IP 10.0.1.100、宛先IPはクライアントのパブリックIPとなる。
  • パケットがパブリックサブネットのルートテーブルに従ってIGWに向かう。

3. IGWでのSNAT(Source NAT)

  • 10.0.1.100 というプライベートIPアドレスのままでは、グローバルインターネットの世界ではルーティングできない(RFC 1918で定められたプライベートIPはインターネット上にルーティングされないため)。
  • そこでIGWは、パケットの送信元IPを再びアタッチされているパブリックIP(Elastic IP等)に書き換える(SNAT)。
  • これにより、クライアント側には「正しいサーバーから返事が返ってきた」ように見える。

—

3. 実務で直面する設定とインフラコード(Terraformの例)

言葉だけでは味気ないので、実際にIaC(Infrastructure as Code)でこの「パブリックサブネットとIGWの結びつき」をどう表現するのかを見ておこう。Terraformを使った標準的な構築コードだ。

# 1. VPCの作成
resource "aws_vpc" "main" {
  cidr_block           = "10.0.0.0/16"
  enable_dns_hostnames = true
  enable_dns_support   = true

  tags = {
    Name = "production-vpc"
  }
}

# 2. インターネットゲートウェイ(IGW)の作成とVPCへのアタッチ
resource "aws_internet_gateway" "gw" {
  vpc_id = aws_vpc.main.id

  tags = {
    Name = "production-igw"
  }
}

# 3. パブリックサブネットの作成
resource "aws_subnet" "public" {
  vpc_id            = aws_vpc.main.id
  cidr_block        = "10.0.1.0/24"
  availability_zone = "ap-northeast-1a"
  
  # これをtrueにすることで、このサブネットで起動したインスタンスに自動でパブリックIPが付与される
  map_public_ip_on_launch = true

  tags = {
    Name = "production-public-subnet-1a"
  }
}

# 4. パブリック用ルートテーブルの作成
resource "aws_route_table" "public" {
  vpc_id = aws_vpc.main.id

  # デフォルトルート(0.0.0.0/0)の向け先をIGWに指定
  route {
    cidr_block = "0.0.0.0/0"
    gateway_id = aws_internet_gateway.gw.id
  }

  tags = {
    Name = "production-public-rt"
  }
}

# 5. ルートテーブルとパブリックサブネットの関連付け(Association)
resource "aws_route_table_association" "public" {
  subnet_id      = aws_subnet.public.id
  route_table_id = aws_route_table.public.id
}

このコードの肝は、aws_route_table 内の route ブロックだ。ここに 0.0.0.0/0 と gateway_id を記述した瞬間から、このサブネットに紐づくすべてのリソースは、世界中と繋がるパブリックな存在へと生まれ変わる。

—

4. 現場のSREが教える「よくあるハマりポイント」とデバッグ手法

「コードを書いてIGWも紐付けたのに、外からつながらない!」
これは、夜間対応でSREが最も冷や汗をかく瞬間のひとつだ。だいたい犯人は以下のどれかに絞られる。

ハマりポイント1: セキュリティグループ(SG)とネットワークACL(NACL)の勘違い

パブリックサブネットにあるからといって、無防備に全開放されているわけではない。

  • セキュリティグループ(ステートフル): インバウンドで許可した通信に対するアウトバウンドは、自動的に許可される(戻りのパケットを気にしなくてよい)。
  • ネットワークACL(ステートレス): サブネット境界にある門番。インバウンドだけでなく、アウトバウンド(戻り通信のポート範囲:通常はエフェメラルポート 1024-65535)も明示的に許可しないと、パケットが外に出ていけない。 「外から来れるのに返事ができない」という現象の大半はNACLのエフェメラルポート抜けが原因だ。

ハマりポイント2: アプリケーション側のバインド設定

インフラ側でパケットがEC2まで届いていても、アプリが 127.0.0.1 (localhost) にしかリスンしていなかったら、外からのパケットは門前払いされる。
例えば、Node.jsやPythonのWeb APIサーバーを立ち上げるとき、次のように 0.0.0.0 (すべてのインターフェース)にバインドさせなければならない。

# Python (Flask) の正しいバインド例
from flask import Flask
app = Flask(__name__)

@app.route("/")
def health_check():
    return {"status": "ok"}, 200

if __name__ == "__main__":
    # 127.0.0.1 ではなく 0.0.0.0 を指定して外部からのアクセスを受け付ける
    app.run(host="0.0.0.0", port=80)

デバッグ時の鉄板コマンド

もし疎通に悩んだら、感情的に設定をいじる前に、現場のエンジニアらしく論理的にパケットを追おう。パブリックサブネット内のインスタンスにSSH/SSMで入り、次のようなコマンドで現状をあぶり出す。

# 1. ちゃんと外(インターネット)にパケットが出ていけるか確認
curl -I https://api.ipify.org

# 2. どのポートで何が待ち受けているか(プロセスとバインドIPの確認)
sudo ss -tulpn | grep LISTEN

# 3. インターフェース単位でパケットのドロップやエラーが起きていないか確認
ip -s link

—

まとめ

パブリックサブネットとインターネットゲートウェイ(IGW)の関係は、一見するとただの「外への出口」に見える。だがその実態は、緻密なルーティングルールと、L3/L4レイヤーでのアドレス変換(SNAT/DNAT)が高速に、かつ透明に行われている巨大なルーター群の表れに他ならない。

インフラストラクチャを設計・運用する我々は、ただ動くものを作るのではなく、「今、パケットがどこを通り、どのゲートウェイでどう変換されているか」を脳内でビジュアライズできなければならない。

この基本原則さえ押さえておけば、どれほど複雑なマルチVPC環境やKubernetesのIngress制御が絡んできたとしても、迷うことなくトラブルシューティングの糸口が見つかるはずだ。さあ、今日も安全で堅牢なネットワークをデプロイしよう。

コメント

タイトルとURLをコピーしました