【実務・中級編】 ENI(Elastic Network Interface)のセカンダリプライベートIPアドレスとアタッチメント制御 – クラウドインフラと仮想化ネットワーク実践ガイド

はじめに:1つのEC2に複数IPをぶら下げるという「魔術」の裏側

こんにちは。日夜、AWSのVPCルートテーブルやKubernetesのCNIプラグインが織りなすパケットの迷宮に潜り込んでいるシニアSREの私です。

Web APIの設計や、エンタープライズシステムのレガシー移行をリードしていると、一度はこんな壁にぶつかるはずです。
「1台のEC2インスタンス(あるいはコンテナ基盤)に対して、複数のグローバルIPやプライベートIPを割り当て、それぞれ異なるルーティングやアクセス制御を行いたい」

AWSの世界では、これを実現するために ENI(Elastic Network Interface) と、1つのENIに対して複数の セカンダリプライベートIPアドレス を付与する機能が用意されています。

しかし、ここで多くのインフラエンジニアやアプリケーション開発者が、ネットワークの基本原則とAWSの「仮想空間」のギャップにハマり、冷や汗をかくことになります。
「あれ? OS側にはIPを設定したのに、外から通信が届かないぞ?」
「ARP(Address Resolution Protocol)の挙動が、オンプレミスの物理ルーターと全然違う……!」

今回は、パケットがAWSの仮想ルーターの網目をどう駆け抜けていくのか、そのリアルな挙動と、実務で絶対に知っておくべきProxy ARPのカラクリを、現場の泥臭い知見を交えて徹底解説します。

—

1. AWS仮想ネットワークにおけるARPとProxy ARPの正体

オンプレミスの物理ネットワークであれば、同一セグメント内の通信は ARP Request(「誰々というIPアドレスを使っている人は、自分のMACアドレスを教えて!」というブロードキャスト)によって解決されます。

しかし、AWSのVPC(Virtual Private Cloud)は、従来の物理レイヤー2(L2)の常識が通用しない「ソフトウェア定義のネットワーク(SDN)」です。VPC内のサブネットは、AWSが管理する強大な仮想ルーター群によってルーティングされています。

セカンダリIPとENIアタッチメントの裏側の挙動

1つのEC2インスタンス(正確にはそのハイパーバイザー上の仮想マシン)にセカンダリプライベートIPアドレス(例: 10.0.1.50)を割り当てたとき、OSのネットワークスタック(Linuxの eth0 など)から見ると、単にIPが追加されたように見えます。

しかし、VPCの外部や別のENIからこのセカンダリIP宛てにパケットを送る際、AWSの仮想ルーターはどのように相手のMACアドレスを特定しているのでしょうか?

ここで登場するのが Proxy ARP(プロキシARP) です。

AWSの仮想ルーターは、EC2インスタンスのセカンダリIPアドレス宛てのARPリクエストを受信すると、ターゲットのインスタンス自身ではなく、AWS側の仮想ルーター(あるいはENIのアタッチメント)が代わりに応答(ARP Reply)します。
「そのIP宛てのパケットなら、私のMACアドレス(ENIのMAC)宛てに投げてくれれば、責任を持ってインスタンスに届けるよ」と、AWSが裏で代理人(Proxy)として振る舞っているのです。

この仕組みがあるおかげで、ユーザーはVPC内で複雑なL2スイッチングの構成を意識せずとも、複数のIPアドレスを柔軟にEC2に持たせることができます。

—

2. 実務で直面する罠:OS側(Linux)の設定不備とルーティング

このAWSのProxy ARPと仮想ルーターの挙動を理解していないと、次のようなトラブルに見舞われます。
「AWSコンソール上でセカンダリIPを追加し、セキュリティグループも開けたのに、APIサーバーにリクエストが届かない」

原因の大半は、Linux OS側のネットワーク設定(特に rp_filter やルーティングテーブル)が、AWSの仮想ネットワークの作法に追いついていないことです。

Linux OS側でのセカンダリIP設定例(Ubuntu / NetworkManager)

実務でよく使われるUbuntu環境を例に、ENIのセカンダリIPをOSに認識させる設定ファイルをみていきましょう。/etc/netplan/ 配下のYAMLファイルを設定します。

# /etc/netplan/50-cloud-init.yaml
network:
  version: 2
  ethernets:
    eth0:
      dhcp4: true
      # プライマリIPに加えて、セカンダリIPをエイリアスとしてバインドする
      addresses:
        - 10.0.1.10/24  # プライマリプライベートIP
        - 10.0.1.50/24  # セカンダリプライベートIP

設定を反映するには、以下のコマンドを叩きます。

sudo netplan apply

【SREの現場のTips】複数ENIを跨ぐ場合の強敵「逆方向パスフィルター(rp_filter)」

1つのEC2に複数のENI(例: eth0 と eth1)をアタッチし、それぞれのENIにセカンダリIPをぶら下げた瞬間、パケットが非対称ルーティング(Asymmetric Routing)を引き起こし、kernelによってドロップされることがあります。

これを防ぐためには、カーネルパラメータの調整が不可欠です。/etc/sysctl.d/ にカスタム設定ファイルを作成します。

# /etc/sysctl.d/99-aws-eni-routing.conf
# 複数ENI環境でリバースパスフィルタリング(rp_filter)を厳格モードから緩いモードへ変更
# これを忘れると、パケットの往復経路が不一致を起こして通信がロストします
net.ipv4.conf.all.rp_filter = 2
net.ipv4.conf.default.rp_filter = 2
net.ipv4.conf.eth0.rp_filter = 2
net.ipv4.conf.eth1.rp_filter = 2

設定後は、以下のコマンドで即座にカーネルへ反映させます。

sudo sysctl --system

—

3. 疎通確認とデバッグの作法(PythonスクリプトによるAPI疎通テスト)

セカンダリIPや複数ENIの構成を変更した際、インフラが正しくルーティングを行っているかを検証するために、私はよく簡単な検証用Pythonスクリプトを書いてテストします。

以下のコードは、特定のセカンダリIP(バインドされたWeb APIサーバー)に対して、特定の送信元IPやインターフェースを意識しながらリクエストを投げる実践的なサンプルです。

import socket
import urllib.request
import json

def test_api_connection(target_url, source_ip):
    """
    特定のセカンダリIPを送信元(Source IP)として指定し、
    AWS上のWeb APIへHTTPリクエストを送信する検証スクリプト
    """
    print(f"[*] 接続先: {target_url}")
    print(f"[*] 指定送信元IP (Source IP): {source_ip}")

    # ソケットのバインドをカスタマイズするためのカスタムHTTPパーサー・ハンドラー
    class SourceIPBindingHandler(urllib.request.HTTPHandler):
        def http_open(self, req):
            return self.do_open(self._create_connection, req)

        def _create_connection(self, address, timeout=None, source_address=None):
            # AWSの仮想ルーター経由で特定のセカンダリIPからパケットを送り出すよう強制
            sock = socket.create_connection(address, timeout, source_address=(source_ip, 0))
            return sock

    # ハンドラーを適用したOpenerDirectorの作成
    opener = urllib.request.build_opener(SourceIPBindingHandler)

    try:
        # リクエスト送信
        response = opener.open(target_url, timeout=5)
        print(f"[+] 通信成功! HTTPステータス: {response.getcode()}")
        print(f"[+] レスポンスボディ: {response.read().decode('utf-8')}")
    except Exception as e:
        print(f"[-] 通信エラー発生: {e}")
        print("    -> Tips: セキュリティグループ、ルートテーブル、およびOS側のrp_filterを確認してください。")

if __name__ == "__main__":
    # 検証用のエンドポイントとセカンダリIPの例
    API_ENDPOINT = "http://10.0.1.100/healthz"
    SECONDARY_SOURCE_IP = "10.0.1.50"
    
    test_api_connection(API_ENDPOINT, SECONDARY_SOURCE_IP)

このスクリプトを実行し、もしタイムアウトや No route to host が発生する場合は、AWSのVPCルートテーブルが該当セカンダリIPのトラフィックを正しくキャプチャできているか、あるいはEC2のOS内ルーティング(ip route)が意図したENIに向いているかを疑うべきです。

—

4. トラブルシューティングのチェックリスト

最後に、現場で障害対応に追われたシニアが頭の中で真っ先に思い浮かべる「デバッグのステップ」をチェックリストとして残しておきます。

1. AWSコンソールの確認

  • 該当のセカンダリIPが、正しいENIに「Assigned」されているか?
  • ENIの「Src/Dst Check(送信元/宛先チェック)」が無効化されているか?(※NATインスタンスやルーターとして動かす場合は無効化が必須です)

2. セキュリティグループの罠

  • セカンダリIP宛てのトラフィックは、プライマリIPとは独立したセキュリティグループの評価を受けません。ENI自体(あるいはアタッチされたインスタンス)に紐づくセキュリティグループのインバウンドルールが適用されているか確認してください。

3. OS上のルーティングテーブル(ip route)

  • 複数のENIがある場合、デフォルトルート(default via ... dev eth0)以外に、ポリシーベースルーティング(ip rule)を設定して、セカンダリENIからの戻りパケットが正しいインターフェースに帰るようにルーティングテーブルが分割されているか確認しましょう。

—

おわりに

AWSのENIとセカンダリIP、そしてその裏でうごめくProxy ARPの挙動は、一見するとブラックボックスのように思えます。しかし、パケットがどこを通り、誰が代理でARPに応答しているのかを頭の中でイメージできるようになると、インフラストラクチャは途端に素直でコントローラブルな相棒に変わります。

Web APIの設計やマイクロサービスのネットワーク分離において、この技術は強力な武器になります。日々のインフラ運用の片隅で、この記事があなたのトラブルシューティングの助けになれば幸いです。

コメント

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