LinuxでのECMPルーティング設定:iproute2で複数ゲートウェイのロードバランシングを実現

Networking tutorial - IT technology blog
Networking tutorial - IT technology blog

課題:使っていない帯域幅にもコストを払っている

トラフィックの多いサーバーノードを2つのISPで運用することは、V8エンジンを搭載していながら4気筒しか使っていないような状況に似ています。デフォルトでは、Linuxは単一のデフォルトゲートウェイに依存します。すべての送信トラフィックが1つのパイプに押し込まれる一方で、料金を払っている2つ目のリンクはアイドル状態のままです。プライマリリンクがダウンした場合、手動で介入するまで接続は完全に切断されてしまいます。

標準的なフェイルオーバースクリプトは冗長性の問題は解決しますが、リソースの過小利用という課題は解決しません。ECMPを使用すると、Linuxカーネルは送信トラフィックを一度に複数のパスに分散させることができます。これは、1Gbpsの光回線と500Mbpsのバックアップリンクを統合し、利用可能な合計帯域幅を最大化するための強力な手法です。

私は、1メガビットの差が重要となるエッジルーターやVPNコンセントレーターでこの構成を導入してきました。高価なハードウェアロードバランサーを必要とせずに、基本的なサーバー管理とプロフェッショナルレベルのネットワークエンジニアリングの間のギャップを埋めることができます。

LinuxカーネルにおけるECMPの仕組み

ECMPは、ルーティングテーブル内の単一の宛先(通常は 0.0.0.0/0 ルート)に対して複数の「ネクストホップ」を追加することで機能します。パケットがシステムから送信される際、カーネルはハッシュアルゴリズムに基づいてパスを選択します。

フローベース vs パケットベースのハッシュ

よくある懸念は、ECMPがラウンドロビンのようにパケットを分割してしまうのではないかという点です。もしLinuxがパケットを1つずつ交互に異なるISPへ送信すると、到着順序の入れ替わり(アウトオブオーダー)によりTCPのパフォーマンスが崩壊してしまいます。代わりに、現代のカーネルはフローベースのハッシュを使用します。送信元IP、宛先IP、およびポートを分析することで、カーネルは特定の「フロー」に属するすべてのパケットが同じパスを通るように保証します。これにより、パケットの順序を維持しながら、異なるユーザーセッションをすべての利用可能なゲートウェイに分散させることができます。

プロのヒント: デフォルトでは、Linuxはレイヤー3(IPアドレス)のみに基づいてハッシュを行うことが多いです。レイヤー4(ポート番号)を使用してより良い分散を得るには、以下のsysctl設定を有効にします。

sudo sysctl -w net.ipv4.fib_multipath_hash_policy=1

実践:iproute2によるECMPの実装

具体的なシナリオを見てみましょう。サーバーに2つのネットワークインターフェースがあると仮定します。

  • eth1: ISP A (ゲートウェイ: 192.168.1.1)
  • eth2: ISP B (ゲートウェイ: 192.168.2.1)

ステップ1:既存ルートの整理

既存の静的なデフォルトルートは、新しいマルチパス設定と競合します。まず現在のテーブルを確認してください。

ip route show

デフォルトルートが存在する場合は、一度削除して白紙の状態にします。

sudo ip route del default

ステップ2:マルチパスルートの追加

ここで重要になるのが nexthop キーワードです。次のコマンドを使用して、2つのプロバイダー間でトラフィックを50/50に分割します。

sudo ip route add default \
    nexthop via 192.168.1.1 dev eth1 weight 1 \
    nexthop via 192.168.2.1 dev eth2 weight 1

weight パラメーターは非常に柔軟です。もしISP Aが1Gbpsの専用線で、ISP Bが500MbpsのStarlinkである場合、ISP Aを weight 2、ISP Bを weight 1 に設定します。これにより、カーネルは新しいフローの約66%を高速なリンクに送信するようになります。

ステップ3:トラフィック分散の確認

再度 ip route show を実行します。デフォルトルートの下に multipath エントリが表示されるはずです。分散が機能しているか確認するために、2つの異なるパブリックIPに対してtracerouteを実行してみましょう。

traceroute 8.8.8.8
traceroute 1.1.1.1

ハッシュが機能していれば、最初のホップが2つのゲートウェイIPの間で切り替わるのが確認できるはずです。

「デッドゲートウェイ」の罠

標準の iproute2 コマンドは静的です。カーネルはケーブルが抜けたこと(リンクDOWN)は検知できますが、ローカルリンクがUPのままISPの上流ネットワークが故障したことは検知できません。もしISP Aがダウンしてもインターフェースがアクティブなままだと、カーネルはトラフィックの50%をブラックホールに投げ込み続けます。

本番環境ではヘルスチェック機構が必要です。主に2つのオプションがあります。

  1. 監視スクリプト: シンプルなBashスクリプトで、特定のインターフェース経由で外部ターゲット(1.1.1.1など)にpingを送信します。失敗した場合、スクリプトがルートの更新をトリガーします。
  2. ダイナミックルーティング: ミッションクリティカルな構成では、FRR (Free Range Routing) を使用します。BFD (Bidirectional Forwarding Detection) を処理でき、ミリ秒単位でリンク切れを検知してルーティングテーブルを自動的に更新できます。

設定の永続化

手動で実行した ip route コマンドは再起動後に消えてしまいます。Netplanを使用しているUbuntuやDebianシステムでは、/etc/netplan/*.yaml ファイルでこれらのルートを定義できます。ただし、Netplanのマルチパス構文は扱いにくいことが多いため、多くの管理者は post-up スクリプトやシンプルなsystemdサービスを使用して、ネットワークスタックの初期化後に nexthop コマンドを再適用する方法を好みます。

成功のための重要な考慮事項

  • ソースIPの選択: サーバー自体が接続を開始する場合、どのソースIPを使用するかを知る必要があります。iptablesnftables のNAT(マスカレード)ルールが、両方の送信インターフェースを処理できるように構成されていることを確認してください。
  • MTUの整合性: MTU(最大転送単位)を一貫させてください。光回線が1500バイトで、VPNベースのバックアップが1400バイトである場合、ページの読み込み失敗やSSHセッションの停止が発生する可能性があります。
  • Conntrack: nf_conntrack カーネルモジュールがアクティブであることを確認してください。これにより、ファイアウォールは接続がどのパスで開始されたかを記憶し、戻りトラフィックを正しく処理できるようになります。

結論

ECMPは、マルチホームのLinuxサーバーから最大限のパフォーマンスを引き出すための最も費用対効果の高い方法です。パッシブなバックアップリンクをアクティブな資産に変え、合計帯域幅を即座に増強できます。真のハイアベイラビリティを実現するには監視レイヤーを追加する必要がありますが、コアとなるルーティングロジックはカーネルに組み込まれており、すぐに活用できる状態にあります。

Share: