なぜサービスにバックアップIPアドレスが必要なのか
こんな状況を想像してみてください。本番環境でロードバランサーや重要なゲートウェイサーバーが稼働している。深夜2時、そのマシンがクラッシュする。背後にある全サービスが止まる。ユーザーにエラーが出る。電話が鳴り始める。
解決策は紙の上では単純です――引き継ぎ可能なスタンバイサーバーを用意しておけばいい。難しいのは、フェイルオーバー時にIPアドレスを自動的に追従させることです。それを解決するのが、KeepalivedとVRRPです。
VRRP(Virtual Router Redundancy Protocol)を使うと、2台以上のサーバーが単一の仮想IPアドレス(フローティングIPまたはVIPとも呼ばれる)を共有できます。1台がMASTERとしてVIPを保持し、それがダウンするとBACKUPサーバーが沈黙を検知して2〜3秒以内にVIPを引き継ぎます。手動対応不要。DNS変更不要。クライアントの再設定も不要です。
KeepalivdはLinux上で最も広く使われているオープンソースのVRRP実装です。私はHAProxyロードバランサー、Nginxリバースプロキシ、カスタムTCPサービスの保護に本番環境で使ってきました――一度セットアップしたら忘れてしまえるツールで、深夜2時に静かに救ってくれる日まで気にしなくていいものです。
このガイドはUbuntu/Debianの2台構成を対象としていますが、パッケージ名が若干異なるだけでCentOS/RHELにも同じ手順が適用できます。
インストール
ラボ構成
同一ネットワークセグメント上に2台のLinuxサーバーが必要です。今回の構成は以下の通りです:
- node1 — 192.168.1.10(初期MASTER)
- node2 — 192.168.1.11(BACKUP)
- 仮想IP(VIP) — 192.168.1.100(サービスが向き先とするフローティングアドレス)
クライアントは常に192.168.1.100に接続します。背後にどの物理ノードがいるかを知る必要はありません。Keepalivedが透過的に処理します。
両ノードにKeepalivdをインストールする
node1とnode2の両方で実行します:
# Ubuntu / Debian
sudo apt update
sudo apt install -y keepalived
# CentOS / RHEL / AlmaLinux
sudo dnf install -y keepalived
Keepalivedはまだインターフェースに存在しないIPをバインドする必要があります。両ノードでカーネル設定を有効にします:
# ローカルに存在しないIPへのバインドを許可(VIPに必要)
echo 'net.ipv4.ip_nonlocal_bind = 1' | sudo tee /etc/sysctl.d/99-keepalived.conf
sudo sysctl --system
設定
MASTERノード(node1)を設定する
node1のKeepalived設定ファイルを開きます:
sudo nano /etc/keepalived/keepalived.conf
以下の設定を貼り付けます:
global_defs {
router_id node1
}
vrrp_instance VI_1 {
state MASTER
interface eth0 # 実際のNIC名に置き換えること
virtual_router_id 51 # 両ノードで一致させること
priority 100 # 値が高いほどマスターとして優先される
advert_int 1 # 1秒ごとにVRRPアドバタイズメントを送信
authentication {
auth_type PASS
auth_pass SecretKey123 # 両ノードで一致させること
}
virtual_ipaddress {
192.168.1.100/24 # フローティングVIP
}
}
次に進む前に知っておくべき4つのポイント:
interface eth0—ip link showでNIC名を確認してください。クラウドVMではens3やenp0s3が使われることが多いです。virtual_router_id 51— グループID(1〜255)。両ノードで同じ値を使う必要があります。ネットワークセグメント上でまだ使われていない値を選んでください。priority 100— 最も優先度が高いノードがMASTERになります。node2は90に設定するので、node1が優先されます。auth_pass— 不正なVRRPパケットをブロックする共有パスワード。例外なく両ノードで同じ値にしてください。
BACKUPノード(node2)を設定する
node2の設定はほぼ同じです。変更するのはstateとpriorityの2つだけです。
global_defs {
router_id node2
}
vrrp_instance VI_1 {
state BACKUP
interface eth0
virtual_router_id 51
priority 90 # node1より低い値
advert_int 1
authentication {
auth_type PASS
auth_pass SecretKey123
}
virtual_ipaddress {
192.168.1.100/24
}
}
Keepalivedの起動と自動起動の設定
両ノードで実行します:
sudo systemctl enable keepalived
sudo systemctl start keepalived
オプション:ヘルスチェックスクリプト
サーバーレベルの障害は対処できました。しかし、OSが動いたままNginxがクラッシュした場合はどうなるでしょうか?Keepalivedのトラックスクリプトがまさにこれを処理します――プロセスを監視し、サービスが停止した場合にノードの優先度を自動的に下げます。
両ノードのvrrp_instanceブロックの前に以下のブロックを追加します:
vrrp_script check_nginx {
script "/usr/bin/pgrep nginx"
interval 2 # 2秒ごとにチェック
weight -20 # チェック失敗時に優先度から20を引く
fall 2 # トリガーまでに2回連続失敗が必要
rise 2 # 回復までに2回連続成功が必要
}
vrrp_instanceブロック内で参照します:
vrrp_instance VI_1 {
...
track_script {
check_nginx
}
}
計算式を整理すると:node1でNginxが停止した場合、実効優先度は100から80(100 − 20)に下がります。これはnode2の90を下回ります。node1のOSが正常に動いていても、KeepalivedはフェイルオーバーをトリガーしてVIPを移動させます。
設定変更後はリロードします:
sudo systemctl reload keepalived
確認とモニタリング
どのノードがVIPを保持しているか確認する
node1で実行します:
ip addr show eth0
プライマリIPと並んでVIPが表示されるはずです:
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP>
inet 192.168.1.10/24 brd 192.168.1.255 scope global eth0
inet 192.168.1.100/24 scope global secondary eth0
node2にはVIPが表示されません――BACKUP状態で待機しています。
フェイルオーバーをテストする
node1のKeepalivdを停止してクラッシュをシミュレートします:
# node1で実行
sudo systemctl stop keepalived
node2に切り替えて確認します:
# node2で実行
ip addr show eth0
2〜3秒以内に192.168.1.100がnode2に表示されるはずです。node1を再起動すると、優先度100がnode2の90を上回るためVIPはnode1に戻ります:
# node1で実行
sudo systemctl start keepalived
Keepalivedログでモニタリングする
状態遷移はsyslogに記録されます。リアルタイムで監視するには:
sudo journalctl -u keepalived -f
フェイルオーバー中は以下のようなログが出力されます:
keepalived[1234]: VRRP_Instance(VI_1) Transition to MASTER STATE
keepalived[1234]: VRRP_Instance(VI_1) Entering MASTER STATE
keepalived[1234]: VRRP_Instance(VI_1) Sending gratuitous ARP
最後の行――gratuitous ARP(無償ARP)――がフェイルオーバーを透過的にする仕組みです。Keepalivedはネットワーク上の全デバイスに対して、192.168.1.100が新しいMACアドレスに移動したことをARPブロードキャストで通知します。スイッチはARPテーブルを更新し、クライアントは再接続します。設定ファイルを触る必要はありません。ARPパケットの実際の動きを確認したい場合は、tcpdumpでパケットをキャプチャして確認するのが最も確実です。
VRRPステータスを直接確認する
ログを読まずに素早く状態を確認するには:
sudo kill -USR1 $(cat /var/run/keepalived.pid)
sudo cat /tmp/keepalived.data | grep State
新しいバージョンではstatsソケットが利用できます:
sudo keepalived --dump-conf
よくある問題と対処法
- 両ノードがMASTERになる(スプリットブレイン) — ほぼ確実にファイアウォールの問題です。VRRPはIPプロトコル112とマルチキャストアドレス
224.0.0.18を使用します。これらがブロックされると、各ノードが相手のダウンを誤検知します。修正方法:sudo iptables -A INPUT -p 112 -j ACCEPT(iptablesとnftablesの詳細はこちら) - フェイルオーバー後にVIPが応答しない — MASTERになったノードで
net.ipv4.ip_nonlocal_bind = 1が有効になっているか確認してください。 - virtual_router_idの不一致 — node1とnode2で同じ値を使う必要があります。IDが異なると、グループを形成できずに両ノードが独立してVIPを所有しようとします。
次のステップ
VIPが動作したら、次の当然の一手は実際のサービスをその背後に置くことです。両ノードにHAProxyまたはNginxをデプロイして同じポートでリッスンさせ、上流のクライアントを192.168.1.100に向けます。フェイルオーバーが発生してもトラフィックは自動的にVIPに追従し、クライアントの設定変更はゼロです。
さらに活用したい場合は、異なるvirtual_router_id値を持つ複数のVRRPインスタンスを実行し、通常時は両ノードにトラフィックを分散させましょう。node1はVIP-AのMASTER、node2はVIP-BのMASTERとなり、互いにバックアップとして機能します。これがアクティブ-アクティブ+フェイルオーバー構成であり、冗長性とスループットの両方を必要とするロードバランサーペアで実績のあるパターンです。

