午前2時の危機:シングルWANでは不十分な理由
火曜日の午前2時、私のスマートフォンが鳴り響いた。深夜まで作業していた工事業者が、本社の主要な光回線を切断してしまったのだ。バックアップ用のLTE回線があったにもかかわらず、サーバーは通信不能に陥った。なぜか?ルーティングテーブルが死んだインターフェースにパケットを送り続けようとしていたからだ。半分寝ぼけた状態でコンソール経由でログインし、デフォルトルートを削除して新しいものを追加しなければならなかった。あの夜、私は二度と手動介入に頼らないと決意した。
中小企業の多くは、2つのISPを扱うには高価なCiscoやJuniperのルーターが必要だと思っている。しかし現実には、Ubuntu ServerはカーネルにすでにビルドインされているツールであるIPRoute2とNetplanを使って、デュアルWANシナリオを十分に処理できる。私はこのアプローチを本番環境で適用してきたが、結果は一貫して安定しており、負荷分散による帯域幅の向上と、自動フェイルオーバーによる安心感の両方をもたらしている。
基本概念:ポリシーベースルーティング(PBR)
標準的なルーティングは、宛先IPアドレスを参照して動作する。8.8.8.8に到達したい場合、カーネルはルーティングテーブルを参照してデフォルトゲートウェイ経由で送信する。デュアルWAN構成では、これだけでは不十分だ。ポリシーベースルーティング(PBR)が必要になる。
PBRを使うと、送信元アドレスやその他の条件に基づいてルーティングを決定できる。ISP1から入ってきたパケットのレスポンスは、必ずISP1経由で返さなければならない。ルーティングテーブルが1つしかない場合、サーバーはISP1へのレスポンスをISP2経由で送ろうとすることがあり、ISPはそれをなりすましパケットとして即座に破棄してしまう。この問題を解決するために、各ISP用に個別のルーティングテーブルと、負荷分散用のメインテーブルを作成する。より複雑な動的ルーティングが必要な場合は、FRRoutingによる動的ルーティングも選択肢の一つだ。
実践:UbuntuでのデュアルWAN設定
この設定では、次の環境を前提とする:
- インターフェース eth0(ISP1): IP 1.1.1.2、ゲートウェイ 1.1.1.1
- インターフェース eth1(ISP2): IP 2.2.2.2、ゲートウェイ 2.2.2.1
- インターフェース eth2(LAN): IP 192.168.1.1
ステップ1:Netplanによるネットワークインターフェースの設定
まず、インターフェースを定義する。UbuntuはNetplanを使用するため、/etc/netplan/01-netcfg.yamlを編集する。カーネルが混乱しないよう、ここではグローバルなデフォルトゲートウェイを設定しない点に注意。代わりに、ルートは手動で定義する。
network:
version: 2
renderer: networkd
ethernets:
eth0:
addresses:
- 1.1.1.2/24
eth1:
addresses:
- 2.2.2.2/24
eth2:
addresses:
- 192.168.1.1/24
sudo netplan applyで変更を適用する。この時点では、サーバーにデフォルトゲートウェイは存在しない。ISPのローカルサブネットとは通信できるが、インターネットには到達できない状態だ。
ステップ2:カスタムルーティングテーブルの定義
システムに2つの新しいルーティングテーブルの存在を伝える必要がある。/etc/iproute2/rt_tablesを編集し、末尾に2行追加する:
# sudo nano /etc/iproute2/rt_tables
100 isp1
200 isp2
ステップ3:ルーティングロジックの作成
次に、これらのテーブルにルートを設定する。各テーブルに独自のデフォルトゲートウェイを持たせ、特定のインターフェース発のトラフィックが同じインターフェースに留まるようにする。再起動後も設定が維持されるよう、これらのコマンドはスクリプトまたはsystemdサービスにまとめておくことを推奨する。なお、iproute2のtcコマンドを使ったQoS設定と組み合わせることで、各WANの帯域幅をより細かく制御することもできる。
# ISP1テーブルのセットアップ
ip route add 1.1.1.0/24 dev eth0 src 1.1.1.2 table isp1
ip route add default via 1.1.1.1 table isp1
# ISP2テーブルのセットアップ
ip route add 2.2.2.0/24 dev eth1 src 2.2.2.2 table isp2
ip route add default via 2.2.2.1 table isp2
# 受信経路と同じ経路でトラフィックを返すためのルールを追加
ip rule add from 1.1.1.2 table isp1
ip rule add from 2.2.2.2 table isp2
ステップ4:負荷分散の実装
ここからが核心部分だ。nexthopパラメータを使って、両方のゲートウェイにトラフィックを分散させる。一方のISPが高速な場合はウェイトを割り当てることができる(例:200Mbpsの回線にweight 2、100Mbpsの回線にweight 1)。
sudo ip route add default scope global \
nexthop via 1.1.1.1 dev eth0 weight 1 \
nexthop via 2.2.2.1 dev eth1 weight 1
Linuxカーネルはこれでフロー単位の負荷分散を行う。これにより、1つの接続(ファイルのダウンロードなど)はパケットの順序乱れを防ぐために1つのWANに留まりつつ、複数のユーザーやアプリケーションは両方の回線に分散される。
ヘルスチェックスクリプトによるフェイルオーバーの自動化
負荷分散は素晴らしいが、ISP1がダウンした場合、カーネルは依然としてトラフィックの50%をブラックホールに送り込もうとするかもしれない。障害を検出してルーティングテーブルを更新する仕組みが必要だ。keepalivedのような複雑なツールもあるが、このような特定のタスクには、シンプルなbashスクリプトの方がよりシンプルで信頼性が高く、デバッグも容易なことが多い。
#!/bin/bash
# 基本的なフェイルオーバースクリプト
ISP1_GW="1.1.1.1"
ISP2_GW="2.2.2.1"
CHECK_IP="8.8.8.8"
check_alive() {
ping -I $1 -c 3 -W 2 $CHECK_IP > /dev/null 2>&1
return $?
}
while true; do
check_alive eth0
ISP1_STAT=$?
check_alive eth1
ISP2_STAT=$?
if [ $ISP1_STAT -ne 0 ] && [ $ISP2_STAT -eq 0 ]; then
# ISP1ダウン、ISP2アップ - 全トラフィックをISP2に切り替え
ip route replace default via $ISP2_GW dev eth1
elif [ $ISP1_STAT -eq 0 ] && [ $ISP2_STAT -ne 0 ]; then
# ISP2ダウン、ISP1アップ - 全トラフィックをISP1に切り替え
ip route replace default via $ISP1_GW dev eth0
elif [ $ISP1_STAT -eq 0 ] && [ $ISP2_STAT -eq 0 ]; then
# 両方アップ - 負荷分散を復元
ip route replace default scope global \
nexthop via $ISP1_GW dev eth0 weight 1 \
nexthop via $ISP2_GW dev eth1 weight 1
fi
sleep 5
done
このスクリプトをバックグラウンドサービスとして実行する。特定のインターフェース経由でGoogleのDNSにpingを送り、一方が失敗するとメインテーブルのデフォルトルートを変更する。ISPが復旧すると、自動的に負荷分散状態に戻る。
動作確認とモニタリング
トラフィックがどの経路を通っているか確認するには、単純にpingを使うだけでは不十分だ。インターフェースフラグ付きのtracerouteを使おう:
traceroute -i eth0 google.com
traceroute -i eth1 google.com
現在のアクティブなルートとウェイトを確認するには、次のコマンドを実行する:
ip route show
送信元ベースのルーティングが有効になっているか、ポリシールールを確認する。また、各WANの実際のスループットを計測するには、iperf3とmtrを使ったベンチマークが非常に役立つ:
ip rule show
まとめ
UbuntuでのデュアルWAN構築に、プロプライエタリなソフトウェアや高価なハードウェアは必要ない。IPRoute2の複数ルーティングテーブルとシンプルなヘルスチェックスクリプトを組み合わせることで、耐障害性と高パフォーマンスを兼ね備えたシステムを構築できる。
この構成を使ってきた私の経験では、市販の「マルチWAN」ルーターの多くよりも、ISPの一時的な障害をはるかにスムーズに処理できることが分かっている。スクリプトをシンプルに保ち、モニタリングを常に稼働させることを忘れずに。次に午前2時のトラブルシューティングが発生しても、サーバーが自動的にフェイルオーバーしてくれていることに感謝するはずだ。

