犬でも分かるhaproxy: LinuxサーバのHA構成

IT

Linuxサーバの運用をするとき、重要なサービスはHA(High Availability=高可用性)構成にして、1台のサーバがダウンした場合でも、もう1台のサーバでサービスを継続できるようにする、という機能が必要とされることがあります。今回は、RHEL9の標準PKGで、2台のサーバをHA構成にして運用する方法を解説します。

haproxy+keepalivedを使う

HAを実現する方法はいろいろあります。外部のロードバランシング装置を使う例もありますが、今回はLinuxサーバだけで実現できる方法を考えます。
著名なところでは、haproxy+corosync+pacemakerという組み合わせがあります。高度な機能である一方で設定も複雑なので、今回はより簡易に実現できる組み合わせとして、haproxy+keepalivedを試してみます。

ユースケース

今回、題材となるユースケースは以下の通りとします。

① 2台のLinuxサーバに対して代表のIPアドレス(VIP=仮想IPアドレス)を付与する。
② ユーザがVIPに対してsshログインをするとき、2台のLinuxサーバにラウンドロビン(交互)に接続するものとする。
③ ユーザが実IPアドレスに対してsshログインをする場合は、実IPアドレスに対応したLinuxサーバに接続するものとする。
④ 1台のLinuxサーバがダウンした場合、VIPに対するsshログインは、他方のLinuxサーバに接続するものとする。

haproxy+keepalivedによるHA構成

keepalivedの機能

keepalivedは、VRRP を使って VIP(仮想IPアドレス)を複数ノード間で切り替える機能を有します。VRRP は Virtual Router Redundancy Protocol(仮想ルータ冗長化プロトコル)の略で、
1つのVIPを複数のノードで共有し、自動的に引き継ぐ仕組みです。
keepalivedはActive/Standby 構成で待機し、通常時はマスター側がVIPを持っています(Active)。マスター側がダウンしていることを検知すると、フェイルオーバーが働き、バックアップ側がActiveとなってVIPを持つようになります。
VIPは、実IPアドレスとは別に、ネットワークIFがもうひとつIPアドレスを持つ形で実装されます。keepalivedを実行した後に、ip aコマンドを実行すると、対象となるネットワークIFにおいて、実IPアドレスとVIPの2つのIPアドレスを持つことが確認できます。

haproxyの機能

haproxyは、クライアントからの接続を複数のサーバに振り分ける機能を提供します。この振り分けのときに、ダウンしているサーバを除外することができます。
振り分けのルール(balanceディレクティブ)の代表的なものは以下の通りです。

balanceディレクティブ機能
roundrobinクライアントからの接続を交互に振り分け
leastconn接続数が最も少ないサーバへ振り分け
sourceクライアントIPのハッシュでサーバを選択。サービスの継続性が必要なものに向いている。
keepalivedとhaproxyの連携

ユーザがアクセスする代表のIPアドレス、すなわちVIPは、keepalivedが管理しています。今回のケースでは、2台のkeepaliveがお互いに連携し、最もpriorityの高いものがマスターとなってVIPを作成します。keepaliveはあくまでも、複数のサーバの中で誰がVIPを持つのか、という管理しているだけです。keepaliveがhaproxyと明示的に連携をしているという訳ではありません。

haproxyは、VIP宛に接続してきたクライアントを、指定されたルールに従って、どのサーバのサービス(今回の例ではssh)に振り分けるのかを決定します。なるほど名前の通り、HA対応かつ分散機能付きのプロキシサービスということですね。

マスターとなっているサーバ(以下の例ではha0)がダウンすると、バックアップだったサーバha1のkeepaliveがマスターに昇格して、VIPを作成します。ha1がVIPを持つので、クライアントがVIPを指定してssh接続してきた場合は、ha1のhaproxyがsshの接続を受け付けて、接続可能なha1のsshdに振り分けます。

サーバha0が復旧すると、priorityの高いha0が再度マスターに昇格して、2台による自動振り分けが復活することになります。

インストール

インストールは簡単。対象となるサーバで、haproxyとkeepalivedをインストールするだけです。

# dnf -y install haproxy keepalived

設定

keepalivedの設定

keepalivedの設定ファイルは、/etc/keepalived/keepalived.conf です。今回の設定は以下の通りとしました。

vrrp_script chk_haproxy {
script "pidof haproxy >/dev/null"
interval 2
weight 10
}

vrrp_instance VI_1 {
state MASTER ★1
interface enp2s0 ★2
virtual_router_id 51
priority 100 ★3
advert_int 1

authentication {
    auth_type PASS
    auth_pass MySecretPass
}

virtual_ipaddress {
    192.168.2.232/24  ★4
}

track_script {
    chk_haproxy
}

}

環境に合わせて設定すべき点は★を付けた箇所です。マスターとなるサーバとバックアップとなるサーバでは設定が異なる箇所があるので注意しましょう。

ディレクティブ指定する値と意味
★1 stateマスターとなるサーバではMASTER, それ以外はBACKUP と記述します。
★2 interfaceVIPを持つネットワークIFの名前を定義します。
★3 priorityマスターとバックアップを決めるプライオリティ値です。マスターで100を指定したら、バックアップのほうでは、それよりも小さい値(例えば90)を指定します。
★4 virtual_ipaddress生成するVIPを指定します。
haproxyの設定

haproxyの設定ファイルは、/etc/haproxy/haproxy.cfg です。haproxy.cfgの内容は、HAを構成する全てのサーバで同じです。

global
daemon
log /dev/log local0 ★1
maxconn 4096
user haproxy
group haproxy

defaults
mode tcp
log global
option tcplog
timeout connect 5s
timeout client 1h
timeout server 1h

frontend ssh_front
bind 192.168.2.232:22 ★2
default_backend ssh_back

backend ssh_back
balance roundrobin 3
option tcp-check
tcp-check connect port 22 ★4
tcp-check expect rstring ^SSH-

server ha0 192.168.2.230:22 check ★5
server ha1 192.168.2.231:22 check
★6

解説すべき点は★を付けた箇所です。

ディレクティブ指定する値と意味
★1 log /dev/log local0/dev/logに書き込むことによって、システムのシスログ(rsylog)が受信します。local0はシスログのfacility(分類)で、haproxyではlocal0を指定するのが一般的です。
★2 bind 192.168.2.232:22フロントエンドで受け付けるIPアドレスとportを指定します。今回の例では、VIPのport=22が対象です。
★3 balance roundrobin振り分けルールを指定します。この例ではroundrobinを指定しています。
★4 tcp-check connect port 22バックエンドの死活監視を行うport番号を指定します。今回はsshが対象なので22とします。
★5 server ha0 192.168.2.230:22 check
★6 server ha1 192.168.2.231:22 check
振り分け先のIPアドレスとport番号のリストを定義します。ha0,ha1はホスト名と同じである必要はありません。この定義の中で一意であればOKです。

注意すべき設定

keepalivedとhaprfoxyの設定を定義して、keepalivedとhaproxyを起動すれば、うまく動くんじゃないかなーと思いきや、そんな単純ではありません。

sshdの設定

今回のユースケースでは、VIP宛のssh接続はhaproxyで受け付けして、実IPアドレス宛のssh接続はsshdが直接受け付けするように設定する必要があります。

sshdのデフォルト設定では、ホストが保有する全てのIPアドレスでsshを受け付けするようになっています。ssコマンドで確認すると「0.0.0.0:22」となっているので、全てのIPアドレスで受け付けするようになっていることが確認できます。

# ss -ltnp 'sport = :22'
State    Recv-Q      Send-Q         Local Address:Port  Peer Address:Port   Process

LISTEN       0        128            0.0.0.0:22          0.0.0.0:*        users:(("sshd",pid=713,fd=3))
LISTEN       0        128            [::]:22             [::]:*             users:(("sshd",pid=713,fd=4))

この状態では、VIPを持ったときに、haproxyとsshdが接続を取り合ってしまうので、sshdのほうでは実IPアドレスのport=22だけをListenするように以下の設定を/etc/ssh/sshd_config に追加します。
以下はha0に対する追加設定、

AddressFamily inet
ListenAddress 192.168.2.230

以下はha1における追加設定です。

AddressFamily inet
ListenAddress 192.168.2.23
1

/etc/ssh/sshd_configを直接修正せずに、この内容を記載したファイル、例えば /etc/ssh/sshd_config.d/01-haproxy.conf を作成することでも良いです。

修正をしたら、sshdを再起動して下さい。。

# systemctl restart sshd

VIPを持たない場合でもhaproxyがエラーにならないためのおまじない

VIPはkeepaliveが実行されてactiveにならないと作成されません。そして、haproxyはVIPが存在しないとエラーになるので、バックアップ側ではこのままではhaproxyが起動できません。
また、マスター側でもシステム起動後のkeepalivedとhaproxyの実行順序を考慮しないとなりません。

でもこれらの課題を解決するおまじないがあるので安心です。そのおまじないは以下のコマンドを実行することです。

# sysctl -w net.ipv4.ip_nonlocal_bind=1

このおまじないの意味は、またネットワークIFに存在しないIPアドレスにもbindできるようになる、という設定です。デフォルトの値は0で、bindするときにIPアドレスが存在していないとエラーになります。1に設定しておくことで、バックアップ側でも、また、keepalived起動前でもhaproxyをエラーなく起動することができるようになります。

上記のコマンドは一時的な設定でしかありません。この設定を永続化するのであれば、以下の内容を 例えば /etc/sysctl.d/98-nonlocalbind.conf というファイルに記載しておくのが良いでしょう。

起動設定

keepalivedとhaproxyの自動起動設定と起動を以下の通り行います。

# systemctl enable keepalived
# systemctl restart keepalived
# systemctl enable haproxy
# systemctl restart haproxy

haproxyのログをrsyslogに転送してみる

/etc/haproxy/haproxy.cfgの設定のログは、local0 の分類でrsyslogに転送する設定となっているので、rsyslog側でそれを受信する設定が必要です。
例えば、/etc/rsyslog.d/01-haproxy.conf というファイルを作成して、以下の内容を定義します。

local0.* /var/log/haproxy.log

その後、rsyslogを再起動します。

# systemctl restart rsyslog

テストしてみる

テストは、192.168.2.0/24のネットワークに接続されている別のホストからssh接続をして行います。

・実IPアドレスによる接続では、それぞれの実IPアドレスを有するホストにssh接続をすることができました。
・VIPによる接続では、ホストha0とha1に交互にssh接続をすることができました。
・ホストha0をshutdownした状態では、VIPによる接続でホストha1にssh接続をすることができました。

マスター側の /var/log/haproxy.logを確認すると、以下のような接続がラウンドロビンで振り分けされていることが確認できます。

Aug 11 15:46:12 ha0 haproxy[763]: 192.168.2.210:49998 [11/Aug/2025:15:46:08.975] ssh_front ssh_back/ha1 1/0/3755 5009 -- 1/1/0/0/0 0/0
Aug 11 15:46:25 ha0 haproxy[763]: 192.168.2.210:38154 [11/Aug/2025:15:46:13.988] ssh_front ssh_back/ha0 1/0/11019 5425 -- 1/1/0/0/0 0/0
Aug 11 15:46:29 ha0 haproxy[763]: 192.168.2.210:36664 [11/Aug/2025:15:46:27.497] ssh_front ssh_back/ha1 1/0/1960 5009 -- 1/1/0/0/0 0/

広告主へのリンク

おうちでLinuxやるなら、ミニPCがちょうど良いです(私はこの3台を使っています)。
RHEL10をインストールするなら、CPUのISAレベルはx86_64-v3以上でないとならないので、NucBox M7がお勧め、RHEL9までならx86_64-v2のCPUにも対応しているので、GK41とNucBox G3でもOKです。
今回のユースケースは、NucBox G3上で仮想マシンを作成してテストしています。

コメント

タイトルとURLをコピーしました