【実務・中級編】 デュアルスタック(IPv4/IPv6)環境におけるプライベートサブネットのルーティング優先順位 – クラウド&コンテナネットワーク実践ガイド

デュアルスタック時代のネットワーク設計:IPv4/IPv6混在環境での「出口戦略」を完全攻略する

こんにちは。日々クラウドのインフラ設計と格闘しているSREです。

最近、「インフラのデュアルスタック化(IPv4/IPv6)」という言葉を耳にする機会が増えましたね。AWSやGCPで構成を組む際、何気なく「IPv4のみ」で済ませてきたネットワーク設計も、いよいよIPv6を避けて通れないフェーズに入ってきました。

しかし、現場で設計を始めると、「NATゲートウェイ」と「Egress-Only IGW」の使い分けや、ルーティングの優先順位で頭を抱えるエンジニアが後を絶ちません。今回は、パケットがクラウドの境界を越える際、OSがどのようなロジックで出口を選択しているのか、その泥臭い実態に切り込んでいきましょう。

—

1. NATゲートウェイとEgress-Only IGW:役割の「本質」を見極める

まず大前提として、パケットを外の世界(インターネット)へ送り出す際の「出口」の考え方を整理します。

IPv4:NATゲートウェイの「身代わり」戦略

IPv4はアドレス枯渇問題により、プライベートサブネット内のインスタンスはグローバルIPを持ちません。そこでNATゲートウェイが、内部のプライベートIPを自身のパケットヘッダーで書き換え(SNAT)、あたかも自分が通信しているかのように振る舞います。これは「IPマスカレード」という、もはや伝統芸に近い仕組みです。

IPv6:Egress-Only IGWの「門番」戦略

一方、IPv6はアドレスが膨大にあるため、NATは不要です。しかし、セキュリティの観点から「外からは接続させたくない(Inbound禁止)」という要件は残ります。ここで登場するのが Egress-Only IGW です。これはNATゲートウェイとは異なり、IPの書き換えを行いません。単に「内部から外への通信は通すが、外からの着信パケットは遮断する」というステートフルなフィルタリングを行う「門番」なのです。

—

2. デュアルスタックにおけるルーティングの優先順位

ここがエンジニアの腕の見せ所です。デュアルスタック環境で curl を打ったとき、OSはどちらのプロトコルを優先するのでしょうか。

RFC 6724が規定する「Default Address Selection」

OS(LinuxやmacOS)は、アプリケーション側で明示的に指定しない限り、RFC 6724 に基づいて宛先IPを選択します。現代のOSは「IPv6が利用可能であれば、優先的にIPv6を使用する」というポリシーを持っていることがほとんどです。

しかし、インフラ側でルーティングテーブル(route または ip route)が適切に設定されていないと、パケットは迷子になります。

【ルーティングテーブルの設定例(Terraformの概念イメージ)】

# IPv4のデフォルトルートはNATゲートウェイへ
resource "aws_route" "ipv4_nat" {
  route_table_id         = aws_route_table.private.id
  destination_cidr_block = "0.0.0.0/0"
  nat_gateway_id         = aws_nat_gateway.main.id
}

# IPv6のデフォルトルートはEgress-Only IGWへ
resource "aws_route" "ipv6_egress" {
  route_table_id              = aws_route_table.private.id
  destination_ipv6_cidr_block = "::/0"
  egress_only_internet_gateway_id = aws_egress_only_internet_gateway.main.id
}

この設定がある状態で、アプリケーションが通信を行うと、OSのスタックは「宛先がAAAAレコード(IPv6)を返せばIPv6を優先、なければIPv4(Aレコード)へフォールバックする」という挙動をとります。

—

3. 実践:アプリケーションからの挙動確認とデバッグ

では、実際にアプリケーションからどのような挙動になるか確認しましょう。Pythonの requests ライブラリなどを使う際、IPv6が有効な環境だと勝手にIPv6が選ばれます。

Pythonでのテストコード

import requests
import socket

# 特定のドメインに対してどちらのIPが優先されるか確認
url = "https://api.example.com"
# 内部的に socket.getaddrinfo が呼ばれ、OSの優先順位に従う
try:
    response = requests.get(url, timeout=5)
    print(f"Status: {response.status_code}")
    # 接続先のIPを表示させてみると、IPv6アドレスが表示されるはずです
    print(f"Connected to: {response.raw._connection.sock.getpeername()[0]}")
except Exception as e:
    print(f"通信失敗: {e}")

もし通信が失敗したら?(トラブルシューティングTips)

現場で最も多いのが「IPv6のルートはあるのに、セキュリティグループがIPv6を許可していない」というケースです。

1. ping6 で導通確認:
ping6 google.com が通るか。通らなければ Egress-Only IGW までのルート、またはSGのインバウンド/アウトバウンドルールを疑ってください。
2. curl で強制指定:
IPv4とIPv6、どちらで詰まっているか切り分けるためにフラグを使いましょう。

# 強制的にIPv4で通信
   curl -4 -v https://api.example.com
   
   # 強制的にIPv6で通信
   curl -6 -v https://api.example.com

これで -6 のときだけタイムアウトするなら、インフラ層(IGW/SG)のミスが確定します。

—

最後に:SREとしての心構え

デュアルスタックネットワークは、単なる「アドレスの追加」ではありません。プロトコルごとの「出口の管理」と「セキュリティポリシーの二重管理」を意味します。

特に、IPv4のNATゲートウェイには課金が発生しますが、IPv6のEgress-Only IGWは(AWSの場合)無料である、といったコスト面の違いも設計上の重要な要素です。将来を見据えてIPv6をメインルートに据える設計にするのか、あるいは現状のIPv4資産を守りつつ徐々に移行するのか。

技術は常に進化しますが、「パケットがどこを通り、誰によって制御されているか」というネットワークの基礎的な視点さえあれば、どんなクラウド環境でも必ず解決の糸口は見つかります。

次回の運用作業では、ぜひ ip -6 route コマンドを叩いて、自分の環境の「出口」がどうなっているか、じっくり観察してみてください。そこには、あなたが設計した通りの美しいルーティングの世界が広がっているはずです。

コメント

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