物理スイッチでは対応しきれなくなったとき
以前、3台の物理ホスト上でKVM仮想マシンを約20台動かすラボ環境を管理していたことがある。最初はLinux Bridgeで問題なく動いていた。しかし、VMグループごとにネットワークを分離したり(dev、staging、production)、トラフィックをVLANで隔離したり、SDNを試してみたいと思った途端、Linux Bridgeでは対応しきれなくなった。VLANの設定は一つひとつ手動でやる必要があり、ネットワークトポロジー全体を俯瞰する方法がなく、何より重要なのはVM間のトラフィックを監視できないことだった。
そこでOpen vSwitchに移行した。慣れてしまえば、名前ほどエンタープライズ向けで難しいものではない。
従来のLinux Bridgeが抱える実際の問題
Linux Bridgeはシンプルな用途には十分だ——VMをLANに接続したり、インターネットを共有したりするなら問題ない。しかし規模が大きくなると、次のような問題にすぐ直面する:
- VLANの設定が煩雑:サブインターフェース(
eth0.10、eth0.20など)を一つずつ作り、VLANごとに個別のブリッジが必要になる。10個のVLANがあれば10個のブリッジを管理することになり、把握するのが非常に難しい。 - 可視性がない:どのトラフィックがどこへ流れているのか、どのVMがどのVMと通信しているのかわからない。デバッグにはtcpdumpで当てずっぽうに調べるしかない。
- SDNと統合できない:Linux BridgeはOpenFlowをサポートしていない。OpenFlowとは、中央のコントローラー(OpenDaylight、ONOS、OpenStackのNeutronなど)からスイッチの動作をプログラムするためのプロトコルだ。
- ボンディング+VLANの組み合わせが複雑:Linux Bridge上で複数NICのボンディングとVLANを組み合わせるのは、長くて複雑な設定作業になる。
VMが2〜3台なら心配する必要はない。しかし中規模のKVM/Proxmox環境で作業する場合や、SDN/OpenStackを学びたい場合は、より強力なツールが必要になる。
Open vSwitchとは何か、何が違うのか
Open vSwitch(OVS)は、Linuxカーネル上で動作するオープンソースの仮想スイッチで、仮想化環境向けに設計されている。VLAN(802.1Q)、LACPボンディング、トンネリング(VXLAN、GRE、Geneve)、OpenFlowを完全サポートし、独自の設定データベース(OVSDB)を持つ。
Linux Bridgeとは異なり、OVSはすべてを統一されたCLI(ovs-vsctl)で管理する。設定はOVSDBを通じて自動的に永続化される——マシンを再起動しても設定は失われない。さらに重要なのは、OVSはOpenFlowプロトコルを通じてSDNコントローラーによって制御できるという点だ。
簡単に言えば、Linux Bridgeは電器店で買うアンマネージドスイッチのようなもの。OVSはCiscoのマネージドスイッチ——VLANあり、監視あり、プログラム可能だ。
Ubuntu/DebianへのOpen vSwitchのインストール
パッケージのインストール
# Ubuntu 22.04 / Debian 12
sudo apt update
sudo apt install -y openvswitch-switch openvswitch-common
# サービスの動作確認
sudo systemctl status openvswitch-switch
# バージョン確認
ovs-vsctl --version
CentOS/Rocky Linuxへのインストール
# 事前にEPELリポジトリを有効化する
sudo dnf install -y epel-release
sudo dnf install -y openvswitch
sudo systemctl enable --now openvswitch
最初の仮想スイッチを作成する
インストール後、OVSブリッジを作成する(Linux Bridgeに相当するが、OVSが管理するもの):
# br-ovsという名前のOVSブリッジを作成
sudo ovs-vsctl add-br br-ovs
# 物理インターフェースeth1をブリッジに追加
sudo ovs-vsctl add-port br-ovs eth1
# 現在の設定の概要を表示
sudo ovs-vsctl show
ovs-vsctl showの出力は次のようになる:
abc12345-xxxx-xxxx-xxxx-xxxxxxxxxxxx
Bridge br-ovs
Port br-ovs
Interface br-ovs
type: internal
Port eth1
Interface eth1
ovs_version: "3.1.0"
OVSでのVLAN設定
VLANの設定をみると、OVSがLinux Bridgeよりどれだけ優れているかがよくわかる。3つのVMグループを分離する必要があった:VLAN 10(dev)、VLAN 20(staging)、VLAN 30(production)。
アクセスポート — VMを1つのVLANにのみ所属させる
# VM1はVLAN 10(dev)に所属 — vnet0ポートにタグを割り当てる
sudo ovs-vsctl add-port br-ovs vnet0 tag=10
# VM2はVLAN 20(staging)に所属
sudo ovs-vsctl add-port br-ovs vnet1 tag=20
# VM3はVLAN 30(production)に所属
sudo ovs-vsctl add-port br-ovs vnet2 tag=30
トランクポート — すべてのVLANを通すアップリンク
# ルーター/物理スイッチへの接続ポート、すべてのVLANを通す
sudo ovs-vsctl add-port br-ovs eth1 trunks=10,20,30
# またはすべてのVLANを通す(制限なし)
sudo ovs-vsctl set port eth1 trunks=[]
この設定により、VLAN 10のVMはVLAN 20のVMと通信できない——ファイアウォールルールを追加することなく、レイヤー2で完全に分離される。
各VLANのサブネットを素早く計算したいとき——たとえばVLAN 10に192.168.10.0/24、VLAN 20に10.20.0.0/24を使う場合——私はよくtoolcraft.app/ja/tools/developer/ip-subnet-calculatorを使っている。CIDRを入力するだけで、ネットワーク範囲、ブロードキャストアドレス、使用可能なホスト数がすぐに表示され、手動計算の手間が省ける。
OVSでのボンディング(LACP)設定
このブログにはLinuxネットワークボンディングに関する記事がすでにあるので、理論的な説明は繰り返さない。OVSでの設定方法だけを説明する——Linux Bridgeとは異なり、はるかにシンプルだ。
LACPを使って2枚のNICをボンドする
# 以前追加したシングルポートを削除
sudo ovs-vsctl del-port br-ovs eth1
# eth1 + eth2をLACPでボンド
sudo ovs-vsctl add-bond br-ovs bond0 eth1 eth2 \
bond_mode=balance-tcp \
lacp=active
# ボンドの状態を確認
sudo ovs-appctl bond/show bond0
OVSには3つのボンドモードがあり、それぞれ異なる状況に適している:
- active-backup:1つがアクティブ、1つがスタンバイ。最もシンプルで、スイッチ側のLACPサポートが不要。リンク障害時に自動フェイルオーバー。
- balance-slb:送信元MACによる負荷分散。LACPは不要だが、送信方向のみの負荷分散となる。
- balance-tcp:フロー(送信元/宛先IP+ポート)による負荷分散。最も効率的で2枚のNICの帯域幅をフル活用できるが、対向スイッチがLACP 802.3adをサポートしている必要がある。
トンネリング:複数ホストをまたいだOVSの接続
個人的に最も気に入っている機能がトンネリングだ。3台の物理ホストがあり、それぞれでOVSを動かしているとき、ホストA上のVMとホストB上のVMが同じスイッチに接続されているかのように通信できるようにしたい——物理スイッチ側に何も設定せずに。
解決策:OVS間でVXLANトンネルを使用する。
# ホストA(IP: 192.168.1.10)側の設定
# ホストB(192.168.1.20)へのトンネルを作成
sudo ovs-vsctl add-port br-ovs vxlan-to-hostB \
-- set interface vxlan-to-hostB \
type=vxlan \
options:remote_ip=192.168.1.20 \
options:key=100
# ホストB(IP: 192.168.1.20)側の設定
# ホストAへの折り返しトンネルを作成
sudo ovs-vsctl add-port br-ovs vxlan-to-hostA \
-- set interface vxlan-to-hostA \
type=vxlan \
options:remote_ip=192.168.1.10 \
options:key=100
トンネルを作成した後、異なるホスト上の同一VLANのVMはオーバーレイネットワーク越しに互いにpingできるようになる。トラフィックはUDPポート4789にカプセル化され、通常のパケットとして物理ネットワーク上を流れる——物理スイッチは内部のVLANについて何も知る必要がない。
実際によく使うデバッグコマンド
# OVSの全設定を表示
sudo ovs-vsctl show
# フローテーブルを表示(アクティブなOpenFlowルール)
sudo ovs-ofctl dump-flows br-ovs
# ポートの統計情報を表示(パケット数、バイト数、エラー数)
sudo ovs-ofctl dump-ports br-ovs
# 各インターフェースの状態を表示
sudo ovs-vsctl list interface
# 全フローを削除(デフォルトのフォワーディングにリセット)
sudo ovs-ofctl del-flows br-ovs
# あるポートのトラフィックを別ポートにミラーリング(キャプチャ用)
sudo ovs-vsctl -- set Bridge br-ovs mirrors=@m \
-- --id=@vnet0 get Port vnet0 \
-- --id=@mirror_port get Port eth-monitor \
-- --id=@m create Mirror name=monitor \
select-dst-port=@vnet0 \
select-src-port=@vnet0 \
output-port=@mirror_port
このトラフィックミラーリングはデバッグ時に非常に役立つ:実行中のVMに影響を与えることなく、あるVMのすべてのトラフィックを別のインターフェースにクローンしてWiresharkでキャプチャできる。
OpenFlowを使ったSDNの統合
OpenStack、Kubernetesのネットワーキングを学んでいる場合や、SDNコントローラーを試してみたい場合——OVSはOpenFlowをそのまま(out of the box)サポートしている。OVSをSDNコントローラーに接続する設定例を示す:
# br-ovsをOpenDaylight/ONOSコントローラーに接続
sudo ovs-vsctl set-controller br-ovs tcp:192.168.1.100:6653
# コントローラーへの接続状態を確認
sudo ovs-vsctl get-controller br-ovs
# OpenFlowルールを手動で追加(例:特定のMACからのトラフィックをドロップ)
sudo ovs-ofctl add-flow br-ovs \
"in_port=1,dl_src=aa:bb:cc:dd:ee:ff,actions=drop"
# 追加したルールを確認
sudo ovs-ofctl dump-flows br-ovs
コントローラーがない場合、OVSは通常のスイッチとして動作する(フェイルオープンモード)。コントローラーが接続されると、フローテーブルのルールが配信され、スイッチの動作を完全に制御できるようになる。
ユースケース別の最適なアプローチ
約2年間ラボでOVSを使ってきた経験から、状況に応じた選択基準を紹介する:
- KVM/libvirt向けにシンプルなブリッジだけ必要? Linux Bridgeで十分。無駄に複雑にしないこと。
- 複数VM間のVLAN隔離が必要? タグ付きポートを使ったOVS——ブリッジ+VLANインターフェースよりはるかに素早く設定できる。
- マルチホストのVMネットワーキング? OVS+VXLANトンネル。物理スイッチのVLANサポートも、ハードウェア側の設定も不要。
- OpenStack/SDNの学習? OVSは必須。Neutron OVSドライバーは多くのOpenStackディストリビューションのデフォルトだ。
- 高帯域幅+冗長性が必要? balance-tcp+LACPのOVSボンド——複雑な設定なしで両方を実現できる。
OVSには最初の学習曲線があるが、ブリッジ/ポート/インターフェースのモデルを理解してしまえば、設定は非常に論理的だ。最も難しいのは、フローテーブルのルールが衝突したときのデバッグだ——そんなときはovs-ofctl dump-flowsとovs-appctl fdb/showの2つのコマンドが救世主になる。
