Keepalived & VRRP:LinuxでフローティングIPを構築して高可用性を実現する

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

なぜサービスにバックアップIPアドレスが必要なのか

こんな状況を想像してみてください。本番環境でロードバランサーや重要なゲートウェイサーバーが稼働している。深夜2時、そのマシンがクラッシュする。背後にある全サービスが止まる。ユーザーにエラーが出る。電話が鳴り始める。

解決策は紙の上では単純です――引き継ぎ可能なスタンバイサーバーを用意しておけばいい。難しいのは、フェイルオーバー時にIPアドレスを自動的に追従させることです。それを解決するのが、KeepalivedVRRPです。

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 eth0ip link showでNIC名を確認してください。クラウドVMではens3enp0s3が使われることが多いです。
  • virtual_router_id 51 — グループID(1〜255)。両ノードで同じ値を使う必要があります。ネットワークセグメント上でまだ使われていない値を選んでください。
  • priority 100 — 最も優先度が高いノードがMASTERになります。node2は90に設定するので、node1が優先されます。
  • auth_pass — 不正なVRRPパケットをブロックする共有パスワード。例外なく両ノードで同じ値にしてください。

BACKUPノード(node2)を設定する

node2の設定はほぼ同じです。変更するのはstatepriorityの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 ACCEPTiptablesと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となり、互いにバックアップとして機能します。これがアクティブ-アクティブ+フェイルオーバー構成であり、冗長性とスループットの両方を必要とするロードバランサーペアで実績のあるパターンです。

Share: