【実務・中級編】 AWS VPCにおけるIPv6デュアルスタックサブネットの設計とアドレス自動割当の仕組み – クラウドインフラと仮想化ネットワーク実践ガイド

AWS VPCにおけるIPv6デュアルスタックサブネット設計の極意:SLAACの裏側と現場のトラブルシューティング

こんにちは。数々の修羅場をくぐってきたシニアSREの私です。

現代のクラウドインフラ設計において、IPv4アドレス枯渇問題はもはや遠い未来の話ではなく、日々のコストや設計上の足枷となっています。特に大規模なマイクロサービス群や、世界中からリクエストを受け付けるWeb API基盤を運用していると、「いつまでプライベートIPv4のプライベートIP重複(CIDRパズル)に悩まされなければならないんだ…」と頭を抱えた経験がある方も多いはずです。

そこで救世主となるのが、AWS VPCにおけるIPv6デュアルスタック環境の構築です。しかし、IPv4の作法だけで育ったエンジニアが、いきなり「じゃあ明日からIPv6ね」とアサインされると、SLAAC(Stateless Address Autoconfiguration)の挙動や、ルートテーブルのデフォルトルート設定、セキュリティグループの解釈の違いなどで見事にハマります。

今回は、AWSのVPCおよびサブネットにおけるIPv6 CIDRの付与から、インスタンス起動時にグローバルユニキャストアドレス(GUA)が降ってくるまでの裏側のメカニズム、そして現場で役立つ実用的なデバッグ手法まで、徹底的に解説していきましょう。

—

1. なぜ今、AWSでIPv6デュアルスタックなのか?

AWS VPCにおけるIPv6は、単なる「IPv4の代替え」ではありません。最大の違いは、NAT(Network Address Translation)が基本的に存在しないという点です。

IPv4の世界では、プライベートサブネットにあるEC2から外へ出るためにはNATゲートウェイが必須であり、その時間経過に伴う課金や、ポート枯渇(SNATポートのエグゾーション)に怯える生活を送る必要がありました。しかし、IPv6はすべてがグローバルユニキャストアドレス(GUA)です。VPC内のリソースは、RFC 4291およびRFC 4862に基づくSLAACによって、世界中で一意な正真正銘のグローバルIPを持ちます。

もちろん、「じゃあセキュリティはどうなるんだ? 全世界から丸見えじゃないか?」という当然の疑問が湧きますよね。そこを守るのが、AWSのセキュリティグループ(Security Group)とネットワークACL(NACL)、そしてインターネットへの出入り口を制御するEgress-Only Internet Gateway(EIGW)のコンビネーションです。このあたりの設計思想の切り替えが、IPv6導入の最初の関門となります。

—

2. VPCとサブネットへのIPv6 CIDRブロック付与の仕組み

AWSでIPv6を有効化する場合、自前でIPアドレスを適当に決めることはできません。AWSが管理するIPv6プールから、/56プレフィックスのIPv6 CIDRブロックをVPCに割り当て、それを分割して/64プレフィックスを各サブネットに割り当てるのがAWSの標準仕様(RFC標準に準拠)です。

アーキテクチャの基本ルール

  • VPC CIDR: /56 ブロック(AWSから自動割当。Bring Your Own IP / BYOIPも可能ですが基本はAmazonプール)
  • サブネット CIDR: /64 ブロック(IPv6のSLAAC要件として、サブネットプレフィックスは必ず /64 である必要があります)

Terraformを使って、このデュアルスタックVPCとサブネットを構築する際の標準的なコードを見てみましょう。実務でそのまま使えるようにコメントを付与しています。

# 1. デュアルスタック対応のVPCを作成(IPv6 CIDRはAWSから自動割当)
resource "aws_vpc" "main" {
  cidr_block                       = "10.100.0.0/16"
  assign_generated_ipv6_cidr_block = true # AWSのプールから /56 のIPv6 CIDRを自動付与する

  tags = {
    Name = "prod-dualstack-vpc"
  }
}

# 2. パブリックサブネットの作成(IPv4とIPv6の両方を設定)
resource "aws_subnet" "public" {
  vpc_id                          = aws_vpc.main.id
  cidr_block                      = "10.100.1.0/24"
  # VPCに割り当てられた /56 から、任意の /64 セグメントを切り出す(例: 0番目のサブネット)
  ipv6_cidr_block                 = cidrsubnet(aws_vpc.main.ipv6_cidr_block, 8, 0) 
  assign_ipv6_address_on_creation = true # 起動時に自動でIPv6を割り当てるフラグ

  map_public_ip_on_launch         = true # IPv4のパブリックIP自動割当
  availability_zone               = "ap-northeast-1a"

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

ここで重要なのが assign_ipv6_address_on_creation = true というパラメータです。これを有効にすることで、後述するSLAACによるアドレス自動割当の魔法がインスタンス起動時に裏側で走ります。

—

3. インスタンス起動時のSLAACとルーティングの裏側

「サブネットにIPv6アドレスを割り当てたら、どうやってEC2インスタンスが自身のIPを知るのか?」

ここで登場するのが、IPv6の根幹技術であるSLAAC(Stateless Address Autoconfiguration:ステートレスアドレス自動設定、RFC 4862)と、ICMPv6を用いたNDP(Neighbor Discovery Protocol、RFC 4861)です。

通信・割当のシーケンス(裏側の挙動)

1. インスタンス起動: EC2インスタンスがサブネット内で起動し、NICが初期化されます。
2. リンクローカルアドレスの生成: インスタンスは自身のMACアドレス等をベースに、自動的にリンクローカルアドレス(fe80::/10)を自己生成し、DAD(Duplicate Address Detection:重複アドレス検出)を行ってネットワーク内での重複がないことを確認します。
3. ルーター要請(Router Solicitation / RS): インスタンスはリンクローカルアドレスを送信元として、ICMPv6のRSメッセージ(タイプ133)をマルチキャスト(ff02::2)でネットワーク上に投げ、「誰かルーターのプレフィックス情報を教えてくれ」と叫びます。
4. ルーター広告(Router Advertisement / RA): AWSの仮想ルーター(VPCインフラ)がこれに応答し、ICMPv6のRAメッセージ(タイプ132)を返します。このRAの中に、サブネットの /64 プレフィックスが含まれています。
5. GUA(グローバルユニキャストアドレス)の生成: インスタンスは、RAから受け取った /64 プレフィックスと、自身のMACアドレスから生成したインターフェースID(あるいはプライバシー拡張アドレス)を結合し、グローバルユニキャストアドレス(GUA)を自己生成(SLAAC)します。

ルートテーブルの構成

IPv6を外に繋ぐためのルートテーブル設定も、IPv4とは名前と挙動が異なります。

| 宛先 (Destination) | ターゲット (Target) | 説明 |
| :— | :— | :— |
| ::/0 | igw-xxxxxxxx (Internet Gateway) | パブリックサブネット用。すべてのIPv6トラフィックをインターネットへ逃がす。 |
| ::/0 | eigw-xxxxxxxx (Egress-Only IGW) | プライベートサブネット用。内発的な通信は外に出せるが、外部からのインバウンド通信を完全にブロックする。 |

—

4. 現場で役立つ!IPv6接続のデバッグと検証手法

インフラ構築が終わったら、正しくIPv6で通信ができているか検証しましょう。「動かないな?」と思ったときにシニアSREが現場で叩く、実践的なコマンドとスクリプトをご紹介します。

トラブルシューティング手順

まずは、インスタンス内部に入り、自身のIPv6アドレスがSLAACで正しくアサインされているか確認します。

# 1. インターフェースのIPv6アドレス確認
ip -6 addr show

# 実行例のイメージ:
# inet6 2406:da18:xxxx:xxxx::xxxx/64 scope global dynamic mngtmpaddr 
#       valid_lft 86400sec preferred_lft 14400sec
# inet6 fe80::xxxx:xxxx:xxxx:xxxx/64 scope link 
#       valid_lft forever preferred_lft forever

ここで scope global dynamic となっていれば、SLAACによる割当が成功しています。

次に、外の世界(例えばGoogleのパブリックDNS 2001:4860:4860::8888 や、IPv6対応のWeb API)への疎通確認を行います。

# 2. IPv6によるping(ping6)の実行
ping6 -c 4 2001:4860:4860::8888

# 3. curlでIPv6を強制してWeb APIや外部サーバーにリクエストを投げる
curl -g -6 https://api.ipify.org
# ※ -g はIPv6アドレスをURL形式で扱う際にブラケット [] の解釈を安全にするために推奨、-6はIPv6使用強制

Python (Requests / Fetch API風) でのIPv6デュアルスタック動作確認

Web APIサーバーサイドを開発している際、「クライアントから本当にIPv6で接続がきているか?」をログやコードで検証したい場合があります。Pythonの requests や標準ライブラリでは、DNS解決時にAAAAレコード(IPv6)が優先されるため、デフォルトでデュアルスタック環境ではIPv6が優先的に使われます。

以下は、現在の接続元IPアドレス(IPv6かどうか)を判定してログ出力する実用的なPythonスニペットです。

import socket
import requests

def check_outbound_ipv6():
    """
    自ホストが現在IPv6で外部と通信できているかを検証するスクリプト
    """
    target_url = "https://api64.ipify.org?format=json" # IPv4/IPv6両方からアクセス可能なIP確認API
    
    try:
        # タイムアウトを3秒に設定してリクエスト
        response = requests.get(target_url, timeout=3.0)
        data = response.json()
        
        client_ip = data.get("ip")
        
        # コロンが含まれていればIPv6アドレスと判定
        if ":" in client_ip:
            print(f"[SUCCESS] IPv6で外部と通信しています。グローバルIP: {client_ip}")
        else:
            print(f"[WARNING] IPv4で通信しています。IPv6ルートを確認してください。 IP: {client_ip}")
            
    except requests.exceptions.RequestException as e:
        print(f"[ERROR] 外部への通信に失敗しました: {e}")

if __name__ == "__main__":
    check_outbound_ipv6()

—

5. 失敗しないためのシニアSREからの実務Tips

最後に、実際に商用環境でIPv6を導入したチームが必ずハマる「罠」をいくつかシェアしておきます。これらを避けるだけで、夜間障害のフラグをひとつ回収できます。

1. セキュリティグループの「IPv4/IPv6の二重管理」を忘れない

  • セキュリティグループのルールは、IPv4(0.0.0.0/0)とIPv6(::/0)で完全に独立しています。「IPv4は閉じたけど、IPv6のインバウンドルールに ::/0 のSSH(ポート22)がオープンなままだった」というインシデントは後を絶ちません。ルールを追加・削除する際は必ず両方を確認する習慣をつけましょう。

2. OS側のファイアウォール(iptables / ufw / firewalld)の罠

  • AWSのセキュリティグループが完璧でも、EC2のOS内部(例: UbuntuやRHEL)で ip6tables がデフォルトで厳格にブロックしていたり、逆に緩すぎたりするケースがあります。クラウド側のセキュリティだけでなく、OS側のレイヤーでもIPv6の挙動を意識してください。

3. コスト面の誤解

  • 「IPv6のパブリックIPは有料なのか?」という質問をよく受けますが、AWSではVPC内のIPv6 CIDRブロックの付与自体は無料です。ただし、インターネットゲートウェイを通過するトラフィックや、パブリックサブネット間のデータ転送量に関する料金体系はIPv4と概ね同様に適用されるため、完全にコストフリーになるわけではない点に注意してください。

—

まとめ

AWS VPCにおけるIPv6デュアルスタックサブネットの設計は、一度仕組みを理解してしまえば、IPv4の複雑なNAT設計から解放される非常に強力な武器となります。

SLAACによる自動アドレス割当の挙動、/56 から /64 へのプレフィックス設計、そしてEIGWを用いたセキュアなアウトバウンド通信の確立。これらを正しく押さえることで、モダンで拡張性の高いクラウドネットワーク基盤を構築できます。

次回のインフラ設計の際には、ぜひ「最初からIPv6デュアルスタック前提」でアーキテクチャを描いてみてください。きっと、スッキリとした美しいネットワーク設計ができるはずです。それでは、良きSREライフを!

コメント

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