【実務・中級編】 ネットワーク仮想化の基礎:ブリッジ接続とNAT接続の挙動 – クラウドインフラと仮想化ネットワーク実践ガイド

仮想化ネットワークの「解像度」を上げろ:ブリッジとNATの挙動をパケットレベルで解剖する

システム運用やAPI開発の現場で、「なぜかVM(仮想マシン)から外部に繋がらない」「コンテナの疎通確認でパケットが迷子になる」といった事態に遭遇したことはありませんか?

クラウドインフラを使いこなす上で、ネットワーク仮想化は「魔法」ではありません。今回は、ハイパーバイザーが裏側で何をやっているのか、その泥臭いパケットの足取りを追跡しながら、ブリッジ接続とNAT接続の本質を解説します。

—

1. 物理NICと仮想NICの架け橋:仮想ブリッジの正体

仮想ブリッジ(virbr0やdocker0など)は、物理的なL2スイッチをソフトウェアで再現したものです。

ブリッジ接続の挙動:レイヤー2の延長戦

ブリッジ接続において、VMはホストと同じ物理ネットワーク(L2セグメント)に所属しているように振る舞います。ハイパーバイザーは、物理NICを「プロミスキャスモード(無差別受信モード)」にし、仮想NICからのフレームを物理スイッチへ、物理スイッチからのフレームを仮想NICへ、MACアドレス学習に基づいて転送します。

現場のTips:
tcpdump を使って確認すると、VMの通信がホストの物理NICを経由してそのまま外に流れているのが見えるはずです。

# ホスト側でVMの通信をパケットキャプチャする(-e でMACアドレスを表示)
tcpdump -i eth0 -e ether host 52:54:00:xx:xx:xx

なぜこれが重要か?

Web APIを設計する際、VMにグローバルIPを直結させる構成ではこのブリッジ接続が必須です。ホスト側のファイアウォール(iptablesやnftables)が、ブリッジを通るパケットを「Forward」ルールで許可しているか、常に確認が必要です。

—

2. ネットワークの「隠れ蓑」:NAT接続の仕組み

次に、開発環境で最もお世話になるNAT接続(Network Address Translation)です。

NATの挙動:プライベート空間からの脱出

NATは、VMをホスト内部の閉じたネットワークに閉じ込め、ホストのIPを「代表」として外に出す技術です。

1. 送信元IPの書き換え: VMからのパケットがホストに届くと、ホストは送信元IPを自身のIPに書き換えます(SNAT / MASQUERADE)。
2. コネクション追跡: カーネル内の「conntrack」テーブルに「VMのIP/ポート ⇔ ホストのポート」の対応を記録します。
3. 戻りパケットの制御: 外部からの応答が戻ってきた際、conntrackテーブルを参照し、元のVMへとパケットを折り返します。

実用例:iptables での NAT 設定(要点のみ)

LinuxホストでNATを実現する場合、以下のようなルールが裏で動いています。

# 送信元が192.168.122.0/24のパケットを、eth0のIPにマスカレードする
iptables -t nat -A POSTROUTING -s 192.168.122.0/24 -o eth0 -j MASQUERADE

# 戻りパケットのためにステートフルな許可を追加
iptables -A FORWARD -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT

—

3. 開発現場で役立つデバッグ手順

APIが繋がらない時、頭の中で以下のフローをトレースしてください。

ステップ1:コンテナ/VM内からの疎通確認

まずは curl での挙動を確認します。-v オプションを忘れずに。

# VM内からAPIサーバーへリクエスト(疎通の可否とレスポンスヘッダーを確認)
curl -Iv http://api.example.com

ステップ2:ホスト側の conntrack を覗く

もし「送信はできているはずなのに返事が来ない」なら、conntrackのエントリが飽和していないか確認しましょう。

# 現在のコネクション追跡数を確認(上限に達すると新しい通信が破棄される)
sysctl net.netfilter.nf_conntrack_count

ステップ3:Pythonによる簡易的なポートスキャン

特定のポートがフィルタリングされているか、Pythonでサクッと確認するツールを持つと、障害対応のスピードが劇的に上がります。

import socket

def check_port(host, port):
    # タイムアウトを設定し、ポートの疎通を確認
    with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
        s.settimeout(2)
        try:
            s.connect((host, port))
            print(f"Port {port} on {host} is OPEN")
        except Exception as e:
            print(f"Port {port} on {host} is CLOSED or FILTERED: {e}")

check_port("192.168.122.10", 8080)

—

まとめ:SREとしての視点

ブリッジ接続は「透明性」、NAT接続は「保護と効率」をもたらします。
Web APIの構成を考える際、外部からの直接アクセスが必要なコンポーネント(ロードバランサーなど)にはブリッジを、内部的なマイクロサービスにはNAT環境(KubernetesのPodネットワークなど)を採用するのが定石です。

ネットワークのトラブルシューティングは、OSI参照モデルを頭に描きながら、パケットがどの地点で「捨てられているのか」「書き換えられているのか」を追いかける作業です。コマンドの暗記ではなく、「パケットの旅路」をイメージできるようになることこそが、優秀なクラウドエンジニアへの第一歩です。

現場の泥臭いパケット解析、楽しんでいきましょう。もし詰まったら、まずは tcpdump と iptables -L -vn から覗いてみてください。答えは必ずそこにあります。

コメント

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