KeepalivedとLVSで構築するL4ロードバランサー

「SAVACAN」担当のMKです。

今回は、Keepalivedを活用してLinux環境でL4ロードバランサーを構築したいと思います。

ロードバランサーというと、クラウドサービスでは AWSのELB(Elastic Load Balancing)、ハードウェア製品では F5ネットワークス社のBIG-IPシリーズなど有名ですが、実はOSSを活用する事で、Linux環境で簡単に無料の自前ロードバランサーを構築することができます。総合的な管理機能や拡張面では劣りますが処理性能ではが遜色なく、低コストで導入でき、カスタマイズ性の面から、特に中小規模のシステム環境においては有効な選択肢となります。

それでは、まずロードバランサーの基本的な仕組みについて簡単に説明していきます。

目次

ロードバランサーの仕組みと主な用途について

ロードバランサーは、アクセスを受け取り、それを複数のサーバーに振り分ける役割を持ちます。

具体的には、一般的にユーザーからのアクセスはWEBサーバーが受け取りますが、間にロードバランサーを置くことでアクセスをロードバランサ―で受け取り、複数のWEBサーバーへ振り分けます。これにより、単一のWEBサーバーでは処理しきれなかった量のアクセスも、複数のサーバーへ処理を分散することで捌くことが可能となります。また、振り分け先のサーバーがダウンした場合、ロードバランサ―の死活監視機能によってダウンサーバーを振り分け対象から除外し、可用性を維持することができます。

一方で、データベースシステムやストレージシステムなど、複数個所でデータを更新するとデータの不整合が発生してしまうシステムでは、今回のL4ロードバランサーによる分散は適切ではありません。このような場合、それぞれのアプリケーションに特化した分散ソリューションを検討する必要があります。

今回のブログでは、L4ロードバランサーの構築方法に焦点を当てて解説しますが、ロードバランサーには主にL4とL7の2種類が存在します。次にその違いについてもご説明いたします。

L4とL7の違い

L4とL7のロードバランサーは、分散制御に用いるプロトコルが異なります。環境に応じて最適なロードバランサーを選択することで、効率的で安定したサービス運用が実現できます。

●L4ロードバランサーとは
L4ロードバランサーは、OSI参照モデルのレイヤー4(トランスポート層)に基づいて負荷分散を行います。具体的には、IPアドレスとポート番号の組み合わせを用いて、TCP/UDPプロトコルに基づいてトラフィックを分散制御します。
パケットヘッダーに基づいて比較的単純なルールに沿って制御を行うため、高速に動作する点が特徴です。単純な振り分けロジックで良い場合や、高スループットが必要な環境に適しています。

●L7ロードバランサーとは
L7ロードバランサーは、OSI参照モデルのレイヤー7(アプリケーション層)に基づいて負荷分散を行います。L7では、HTTPリクエストのURLやCookie、HTTPヘッダーなどのアプリケーション層の情報を利用して、トラフィックの振り分けを行います。トラフィック内容を詳細に解析するため、より複雑な振り分けルールでの制御が可能です。
ウェブアプリケーションやAPIトラフィックの詳細な制御が可能な反面、L4ロードバランサーと比較すると高負荷で低速です。また、SSL/TLSを終端する事が可能な点も特徴です。

今回の構成

今回は以下の構成でのご説明をさせて頂きます。

・サーバーOSは「Almalinux8」
・構築するサーバーは、Keepalived2台、webサーバー2台
・Keepalived2台でアクティブ/スタンバイ構成
・webサーバーの80、443ポートへのロードバランス
・ロードバランスの方式はDSR形式(サーバーからの戻りパケットを直接ユーザーへ返す形式)
・80ポートの振り分けは、アクティブ/スタンバイ構成
・443ポートの振り分けは、最小接続ロードバランス構成

インストール

前提モジュールのインストール

dnf install gcc openssl-devel kernel-devel

ロードバランスモジュールのインストール

dnf install ipvsadm keepalived

以上でインストールは完了です。

Keepalivedの設定

設定対象となるファイルは以下の2つとなります。
/etc/keepalived/keepalived.conf : keepalived の振る舞いに関する設定
/etc/sysconfig/keepalived : keepalived を起動する時のパラメータ設定

アクティブ側のkeepalived.conf設定

global_defs {
	notification_email {
		 down@example.com # ダウン検知時の通知先
	}
	notification_email_from mailfrom@example.com # 送信元メールアドレス
	smtp_server localhost
	smtp_connect_timeout 30
}

vrrp_instance vip_httpd {
	interface ethxx
	virtual_router_id 9
	lvs_sync_daemon_interface ethxx
	state BACKUP
	garp_master_delay 5
	priority 100 # プライオリティ(値が高い方がマスター)
	advert_int 1

	authentication {
		auth_type PASS
		auth_pass lb-test01_http # 対向先ロードバランサ認証password
	}

	virtual_ipaddress {
		xxx.xxx.xxx.ccc/24 dev ethxx # ロードバランサのサービス待ち受けアドレスと設定先のNIC指定
	}

 unicast_src_ip xxx.xxx.xxx.aaa # 自身のIPアドレス
 unicast_peer {
  xxx.xxx.xxx.bbb # 対向先ロードバランサIPアドレス
 }

 notify_master /root/bin/mail_master.sh # (任意)マスター昇格時に実行するスクリプト
 notify_backup /root/bin/mail_backup.sh # (任意)スレーブ降格時に実行するスクリプト
}

virtual_server xxx.xxx.xxx.ccc 80 { # 80ポートの待ち受け設定
    delay_loop 10
    lvs_sched lc
    lvs_method DR
    protocol TCP
    sorry_server xxx.xxx.xxx.eee 80 # メインサーバダウン時の振り分け先IPアドレス
    real_server xxx.xxx.xxx.ddd 80 { # 振り分け先となるメインサーバのIPアドレス
        weight 1
        TCP_CHECK { 
            connect_port 80
            connect_timeout 3
        }
    }
}

virtual_server xxx.xxx.xxx.ccc 443 { # 443ポートの待ち受け設定
    delay_loop 10
    lvs_sched lc
    lvs_method DR
    protocol TCP
    real_server xxx.xxx.xxx.ddd 443 { # 振り分け先サーバー設定
        weight 1
        TCP_CHECK { 
            connect_port 443
            connect_timeout 3
        }
    }
    real_server xxx.xxx.xxx.eee 443 { # 振り分け先サーバー設定
        weight 1
        TCP_CHECK { 
            connect_port 443
            connect_timeout 3
        }
    }
}

スタンバイ側のkeepalived.conf設定

global_defs {
	notification_email {
		 down@example.com # ダウン検知時の通知先
	}
	notification_email_from mailfrom@example.com # 送信元メールアドレス
	smtp_server localhost
	smtp_connect_timeout 30
}

vrrp_instance vip_httpd {
	interface ethxx
	virtual_router_id 9
	lvs_sync_daemon_interface ethxx
	state BACKUP
	garp_master_delay 5
	priority 50 # プライオリティ(値が高い方がマスター)
	advert_int 1

	authentication {
		auth_type PASS
		auth_pass lb-test01_http # 対向先ロードバランサ認証password
	}

	virtual_ipaddress {
  xxx.xxx.xxx.ccc/24 dev ethxx # ロードバランサのサービス待ち受けIPと設定先のNIC指定
	}

 unicast_src_ip xxx.xxx.xxx.bbb # 自身のIPアドレス
 unicast_peer {
  xxx.xxx.xxx.aaa # 対向先ロードバランサIPアドレス
 }

 notify_master /root/bin/mail_master.sh # (任意)マスター昇格時に実行するスクリプト
 notify_backup /root/bin/mail_backup.sh # (任意)スレーブ降格時に実行するスクリプト
}

virtual_server xxx.xxx.xxx.ccc 80 { # 80ポートの待ち受け設定
    delay_loop 10
    lvs_sched lc
    lvs_method DR
    protocol TCP
    sorry_server xxx.xxx.xxx.eee 80 # メインサーバダウン時の振り分け先IPアドレス
    real_server xxx.xxx.xxx.ddd 80 { # 振り分け先となるメインサーバのIPアドレス
        weight 1
        TCP_CHECK { 
            connect_port 80
            connect_timeout 3
        }
    }
}

virtual_server xxx.xxx.xxx.ccc 443 { # 443ポートの待ち受け設定
    delay_loop 10
    lvs_sched lc
    lvs_method DR
    protocol TCP
    real_server xxx.xxx.xxx.ddd 443 { # 振り分け先サーバー設定
        weight 1
        TCP_CHECK { 
            connect_port 443
            connect_timeout 3
        }
    }
    real_server xxx.xxx.xxx.eee 443 { # 振り分け先サーバー設定
        weight 1
        TCP_CHECK { 
            connect_port 443
            connect_timeout 3
        }
    }
}

稼働機予備機のconfigの違いはunicastで指定するIPアドレスとプライオリティのみです。

/etc/sysconfig/keepalivedの設定は、アクティブ/スタンバイともに同様の設定となります。

# --vrrp               -P    Only run with VRRP subsystem.
# --check              -C    Only run with Health-checker subsystem.
# --dont-release-vrrp  -V    Dont remove VRRP VIPs & VROUTEs on daemon stop.
# --dont-release-ipvs  -I    Dont remove IPVS topology on daemon stop.
# --dump-conf          -d    Dump the configuration data.
# --log-detail         -D    Detailed log messages.
# --log-facility       -S    0-7 Set local syslog facility (default=LOG_DAEMON)

KEEPALIVED_OPTIONS="-D -P -C"

使用オプション解説

  • -P

VRRPは複数のKeepalived間で仮想IPアドレスを共有し、高可用性を実現するためのプロトコルです。ネットワーク障害発生時にスタンバイ側が自動的に仮想IPアドレスを引き継ぎ、サービスの中断を最小限に抑えます。

  • -C

リアルサーバーの健康状態を監視し、その結果に基づいて負荷分散の設定を動的に調整する機能です。

  • -D

ログに詳細なメッセージを出力します。

フェイルオーバー/フェイルバック時に実行するスクリプトを作成

任意ですが、フェイルオーバー/フェイルバックが発生した際にスクリプトを実行する事が出来ます。
検知用の通知設定を仕込んだり、一時対応用の処理を仕込んでおくと便利です。

ファイル名と設置場所はkeepalived.confの内容に合わせています。
パーミッションは755を指定します。

マスターになった時に実行するスクリプト(/root/bin/mail_master.sh)
マスター昇格時にメールを送信します。

#!/bin/bash

cat <<EOM | mail -s "[`hostname`] - Entering MASTER state" 通知先メールアドレス -- -f 送信元メールアドレス
=> Message Code 001: Device is now owning VIPs <=

EOM

バックアップになった時に実行するスクリプト(/root/bin/mail_backup.sh)
スレーブ降格時にメールを送信します。

#!/bin/bash

cat <<EOM | mail -s "[`hostname`] - Entering BACKUP state" 通知先メールアドレス -- -f 送信元メールアドレス
=> Message Code 002: Device is nolonger owning VIPs <=

EOM

firewalledの設定

Keepalived同士で冗長性を制御するための通信に、112ポートを使用するため以下を追加します。

firewall-cmd --zone=public --add-rich-rule='rule family=ipv4 source address="unicastの項にて設定したネットワークアドレス" protocol value=vrrp accept' --permanent

以上で、KeepalivedとLVSで構築するL4ロードバランサーの設定は完了です。
次に、振り分け先となるWEBサーバー側でfirewalledの設定をします。

WEBサーバー側でのfirewalled設定

ロードバランサー経由で届いた仮想IPアドレス宛のパケットをDNAT変換して受け取れるように設定します。

firewall-cmd --permanent --direct --add-rule ipv4 nat PREROUTING_direct 0 -d (待ち受けIP) -j DNAT --to (WEBサーバーIP)

以上でWEBサーバー側での設定が完了です。

Keepalivedの動作状況チェック

仮想IPアドレスの割り当て状況確認します。

ip addr list

<アクティブ時は、自身のIPとvrrpで共有している仮想IPが表示される>
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast qlen 1000
  link/ether (mac address)
  inet xxx.xxx.xxx.aaa scope brd xxx.xxx.xxx.xxx global eth0
  inet xxx.xxx.xxx.ccc scope global secondary eth0
   valid_lft forever preferred_lft forever

<スタンバイ時は、自身のIPのみ表示される>
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast qlen 1000
  link/ether (mac address)
  inet xxx.xxx.xxx.aaa brd xxx.xxx.xxx.xxx scope global eth0
   valid_lft forever preferred_lft forever

ノードの接続状況を確認します。
アクティブ機、スタンバイ機ともに表示フォーマットは同様です。

ipvsadm -Ln

IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
 -> RemoteAddress:Port     Forward Weight ActiveConn InActConn
TCP  xxx.xxx.xxx.ccc:80 lc
  -> xxx.xxx.xxx.aaa:80              Route   1      1          1
TCP  xxx.xxx.xxx.ccc:443 lc
  -> xxx.xxx.xxx.aaa:443             Route   1      446        1
  -> xxx.xxx.xxx.bbb:443             Route   1      447        1

80番ポートはアクティブ/スタンバイ構成のため、アクティブ側のみ表示されます。
443番ポートは最小接続ロードバランス構成なので、両振り分け先が表示されています。

keepalivedのconfig解説

設定と動作状況の確認方法を説明しましたので、最後にkeepalivedのconfigの設定値を補足させて頂きます。

global_defs {
    notification_email {
       down@example.com                # ノードダウン時のメール送信先、複数設定可能
    }
    notification_email_from mailfrom@example.com   # メール送信元アドレス
    smtp_server localhost               # メール送信に使用するSMTP先
    smtp_connect_timeout 30              # SMTPのTime out待ち設定
}

vrrp_instance vip_httpd {
    interface ethxx                   # 使用するインターフェイス
    virtual_router_id 9                # 仮想ルーターのID、冗長構成の機器は同じIDを持たなければならない。
    lvs_sync_daemon_interface ethxx         # フェイルオーバー時に接続を維持する
    state BACKUP                    # 起動時の自身の振る舞い。
    garp_master_delay 5                # 自身がマスターに昇格した時に送信するARPの遅延させる時間
    priority 100                    # 優先度の設定。数値が大きい方が優先される。
    advert_int 1                    # 冗長構成時の確認間隔

    authentication {                  # 冗長構成時の対向先認証方式とPASSの設定
        auth_type PASS
        auth_pass lb-test01_http
    }

    virtual_ipaddress {                # 作成する仮想サービス用IP、複数設定可能。
        xxx.xxx.xxx.ccc/xx dev ethxx
    }
    notify_master /root/bin/mail_master.sh      # マスター昇格時に実行するスクリプトの指定
    notify_backup /root/bin/mail_backup.sh      # バックアップ降格時に実行するスクリプトの指定
}

virtual_server xxx.xxx.xxx.ccc 80 {          # 動作を設定する仮想IPのポート設定
    delay_loop 10                                # 振り分け先死活監視のポーリング間隔
    lvs_sched lc                    # ロードバランシング方式の設定
    lvs_method DR                   # パケット転送方式の設定
    protocol TCP
    sorry_server xxx.xxx.xxx.eee 80         # アクティブ/スタンバイ時のスタンバイ側IP
    real_server xxx.xxx.xxx.ddd 80 {        # アクティブ/スタンバイ時のアクティブ側IP
        weight 1                   # ノードの優先度設定
        TCP_CHECK {                 # ノードの死活チェック設定
            connect_port 80                 # 死活監視時のポート設定
            connect_timeout 3               # 死活監視ポーリングのタイムアウト設定
        }
    }
}

AWSのElastic Load Balancing(ELB)サービスについて

AWSのマネージドサービスとして提供されているElastic Load Balancing(ELB)について少し解説します。
ELBには、以下の4種類のロードバランサープランが用意されており、それぞれ異なる用途に最適化されています。

Network Load Balancer(NLB)
Network Load Balancerは、OSI参照モデルのレイヤー4(L4)で動作するロードバランサーです。高いパフォーマンスと低レイテンシを提供し、TCPおよびUDPトラフィックを効率的に処理するため、リアルタイムなアプリケーションや高スループットを要求するサービスに最適です。

Application Load Balancer(ALB)
Application Load Balancerは、OSI参照モデルのレイヤー7(L7)で動作します。HTTPおよびHTTPSのトラフィックを対象としており、リクエストの内容(URLパスやヘッダー)に基づいてトラフィックを分散することができます。ALBは、マイクロサービスアーキテクチャやコンテナベースのアプリケーションに特に有効です。

Gateway Load Balancer(GWLB)
Gateway Load Balancerは、OSI参照モデルのレイヤー3(L3、ネットワーク層)で動作します。このロードバランサーは、GENEVE(UDPポート6081)を使用し、セキュリティアプライアンスやネットワークサービスを効率的にスケーリングおよび管理するために使用されます。ネットワークトラフィックの流れを最適化するために、特にセキュリティインフラの負荷分散に役立ちます。

Classic Load Balancer(CLB)
Classic Load Balancerは、従来型のL4ロードバランサーで、主にEC2-Classic環境で使用されます。現在では、VPC(Virtual Private Cloud)環境での使用は推奨されていません。VPC環境では、Network Load Balancerがより優れたパフォーマンスを提供します。

マネージドサービスのためメンテナンスフリーでインストール操作も不要、いつでもすぐに利用する事が出来ます。また、いずれも従量課金制でAWSに対する初期導入コストは必要ありません。
ロードバランサーそのもののチューニングは出来ませんが、管理コンソールから簡単に設定することが可能で、AWSの巨大なエコシステムと連動する事で様々な用途に活用できる点が非常に強力です。

まとめ

弊社では、KeepalivedとNginxを利用したL7ロードバランサを組み合わせて、1日あたり約2,500万PV規模の巨大ウェブサイトの継続した安定運用を実現しています。この実績から、Keepalivedを活用した自前ロードバランサーは、高トラフィックな環境でも十分に活用可能であることが証明されました。

また、Keepalivedはオンプレミス環境だけでなく、クラウド内で構築する事も可能です。(環境による)
弊社のお客様にて、コスト面からクラウド内サービスの採用が難しいとの事で、サーバーをロードバランサとして構築する形でご提案したこともあります。こちらは現在も安定して運用頂いております。

高価なロードバランサー機器を購入することなく、コストを抑えながら高可用性も確保したい場合には、Keepalivedの導入を検討してみてはいかがでしょうか。
OSSの活用は保守メンテナンス面が不安要素ですが、弊社は、様々なOSSを15年以上活用してきた実績とノウハウがあります。ご検討の際はいつでもご相談ください。

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

この記事を書いた人

目次