Linux上でBGPとBIRD2を使用したAnycastの実装:HA向上と低レイテンシ化の極意

Network tutorial - IT technology blog
Network tutorial - IT technology blog

なぜAnycastは大規模システムのゴールドスタンダードなのか?

グローバルなDNSシステムを運用していると想像してみてください。ニューヨークのユーザーは米国のサーバーに、ホーチミンのユーザーはベトナムのサーバーに、単一のIPアドレスだけで自動的に接続させるにはどうすればよいでしょうか?シンガポールのサーバークラスターが突然メンテナンスに入った場合、手動操作なしで数秒以内にトラフィックを香港に自動転送するにはどうすればよいでしょうか?

その答えはAnycastにあります。Unicast(1つのIPが1つのサーバーに対応)とは異なり、Anycastでは地理的に異なる場所にある複数のサーバーが、同じIPアドレスをグローバルルーティングテーブルに広報(advertise)することを可能にします。ルーターは(通常はAS Pathに基づいて)最短距離を計算し、ユーザーを最も近いノードに誘導します。

この記事では、BIRD2の使用方法を解説します。これは20MB未満のRAMしか消費しない非常に軽量なルーティングデーモンですが、数百万のルートを処理する能力を持っています。筆者はこのソリューションを内部DNSシステムに適用し、遠隔拠点のレイテンシを150msから20ms未満に短縮することに成功しました。

ラボ環境の構成

実装には、少なくとも2台のLinuxサーバー(Ubuntu 22.04が推奨)と、BGPをサポートするルーターが必要です。ラボ環境であれば、VyOSやBIRDを実行している別のLinux仮想マシンをセントラルルーターとして使用できます。

  • Anycast IP: 10.10.10.10/32(全サービス共通のIP)
  • Node 1: IP 192.168.1.11, AS 65001
  • Node 2: IP 192.168.1.12, AS 65001
  • セントラルルーター: IP 192.168.1.1, AS 65000

ネットワーク帯域とサブネットの計算は欠かせないステップです。手動計算が面倒な場合は、IP Subnet Calculatorを使用してIP範囲とブロードキャストを素早く取得し、後のフィルター設定でのミスを防ぎましょう。

ステップ1:BIRD2のインストール

BIRD2は、現在のほとんどのLinuxディストリビューションの公式リポジトリで提供されています。インストールは非常に簡単です。

sudo apt update && sudo apt install bird2 -y

メインの設定ファイルは /etc/bird/bird.conf にあります。変更を加える前に、必ず元のファイルをバックアップしておきましょう。

ステップ2:Dummyインターフェースの設定

レイヤー2の競合を避けるため、Anycast IPを物理ネットワークカードに直接割り当てるべきではありません。代わりに、仮想インターフェース(dummy)を作成します。このIPは各ノードに独立して存在することになります。

# anycast0という名前のdummyインターフェースを作成
sudo ip link add dev anycast0 type dummy
sudo ip addr add 10.10.10.10/32 dev anycast0
sudo ip link set anycast0 up

再起動後もこの設定を維持するには、Netplanまたは /etc/network/interfaces で定義してください。

ステップ3:IP広報のためのBGP設定

これがシステムの心臓部です。BIRD2を設定して、ルーターに対して「私は10.10.10.10のIPを持っています。トラフィックをこちらへ送ってください」と伝えます。

Node 1の /etc/bird/bird.conf を編集します:

log syslog all;
router id 192.168.1.11;

protocol device {
    scan time 10;
}

# dummyインターフェースからIPを取得
protocol direct {
    ipv4;
    interface "anycast0";
}

# Linuxカーネルがパケットを処理できるようにルートを注入
protocol kernel {
    ipv4 { export all; };
}

# ルーターとのBGP接続を設定
protocol bgp upstream_router {
    local as 65001;
    neighbor 192.168.1.1 as 65000;
    
    ipv4 {
        import none; 
        export filter {
            if net = 10.10.10.10/32 then accept;
            reject;
        };
    };
}

Node 2については、router id を適切に変更するだけで完了です。

ステップ4:確認と運用

設定を保存した後、サービスを再起動します:

sudo systemctl restart bird

birdc show protocols コマンドを使用してステータスを確認します。Established と表示されれば、BGP接続は成功です。この時点で、ルーターはIP 10.10.10.10へのパスを2つ認識しているはずです。

フェイルオーバー(Failover)のテスト

実際にノードがダウンするシナリオをテストしました。Node 1のBIRDを停止すると、ルーターは(Keepalive設定によりますが)わずか1〜3秒でBGP信号の消失を検知します。即座にすべてのトラフィックがNode 2に切り替わります。このプロセス中、ping でのパケットロスはわずか1パケットでした。これはDNSのTTLが切れるのを待つ(通常数分かかる)のに比べ、非常に優れた数値です。

重要な注意点:ヘルスチェック

大きなリスクが1つあります。BIRDは動作しているものの、メインサービス(NginxやDNSなど)がハングアップしている場合です。この状態でもルーターは故障したノードにトラフィックを送り続け、「ブラックホール」状態が発生します。

解決策は、サービスのステータスを確認するスクリプトを使用することです。Nginxがダウンした場合、スクリプトはBIRDに即座にIPの広報を停止するよう要求します:

#!/bin/bash
if ! systemctl is-active --quiet nginx; then
    birdc down upstream_router
    echo "サービスがダウンしています。ルートを撤回中..."
fi

また、管理用のSSH接続にAnycast IPを絶対に使用しないでください。ルーティングテーブルの変更によって、あるノードにSSHしている最中に別のノードへ飛ばされてしまうのを防ぐため、常に物理IPを使用するようにしましょう。

Anycastを導入することで、システムは格段にプロフェッショナルで堅牢になります。複雑なフィルター設定や、1秒未満の収束速度を実現するためのBFDの最適化などで困ったことがあれば、ぜひ下のコメント欄で質問してください!

Share: