LVSのDR方式の動作検証
KeepAlivedとLVSを使い負荷分散、冗長化時の動作を検証する。
はじめに
負荷分散は、ダイレクトルーティング(DR)を使う。
DR(ダイレクトルーティング)では、
という流れになる。
そのため、クライアントへの戻りのパケットは、LVSサーバを経由せず、言い換えれば、負荷をかけずにクライアントに返る。
また、LVSサーバを2台構築して、VRRPによる冗長化を行う。
後日、NAT方式も試してみたい。
構成
- LVSサーバ
- lvs1
- 192.168.1.10/24
- lvs2
- 192.168.1.20/24
- lvs1
- リアルサーバ(Nginx)
- web1
- 192.168.1.30/24
- web2
- 192.168.1.40/24
- web1
- クライアント
- 192.168.1.50/24
LVSサーバには、VIPとして、192.168.1.100をつける。
いずれも、Ubuntu 18.04.2で用意した仮想サーバ。
構成図
設定
LVSサーバ
パッケージインストール
# apt update && apt install -y ipvsadm keepalived
インストールされた以下のバージョンを使う。
# ipvsadm -v ipvsadm v1.28 2015/02/09 (compiled with popt and IPVS v1.2.1) # keepalived -v Keepalived v1.3.9 (10/21,2017) Copyright(C) 2001-2017 Alexandre Cassen, <acassen@gmail.com> Build options: PIPE2 IPV4_DEVCONF LIBNL3 RTA_ENCAP RTA_EXPIRES RTA_NEWDST RTA_PREF RTA_VIA FRA_OIFNAME FRA_SUPPRESS_PREFIXLEN FRA_SUPPRESS_IFGROUP FRA_TUN_ID RTAX_CC_ALGO RTAX_QUICKACK FRA_UID_RANGE LWTUNNEL_ENCAP_MPLS LWTUNNEL_ENCAP_ILA LIBIPTC LIBIPSET_DYNAMIC LVS LIBIPVS_NETLINK IPVS_DEST_ATTR_ADDR_FAMILY IPVS_SYNCD_ATTRIBUTES IPVS_64BIT_STATS VRRP VRRP_AUTH VRRP_VMAC SOCK_NONBLOCK SOCK_CLOEXEC GLOB_BRACE OLD_CHKSUM_COMPAT FIB_ROUTING INET6_ADDR_GEN_MODE SNMP_V3_FOR_V2 SNMP SNMP_KEEPALIVED SNMP_CHECKER SNMP_RFC SNMP_RFCV2 SNMP_RFCV3 DBUS SO_MARK
iptables
新しく接続を開始する80番の入りのTCPパケットは許可する。
# iptables -A INPUT -p tcp -m state --state NEW -m tcp --dport 80 -j ACCEPT # iptables -L -n Chain INPUT (policy ACCEPT) target prot opt source destination ACCEPT tcp -- 0.0.0.0/0 0.0.0.0/0 state NEW tcp dpt:80 Chain FORWARD (policy ACCEPT) target prot opt source destination Chain OUTPUT (policy ACCEPT) target prot opt source destination
keepalivedの設定
vrrp_instance Lvsdev { state BACKUP interface ens18 garp_master_delay 5 virtual_router_id 1 priority 100 nopreempt advert_int 3 virtual_ipaddress { 192.168.1.100 dev ens18 } } virtual_server 192.168.1.100 80 { delay_loop 3 lb_algo rr lb_kind DR protocol TCP real_server 192.168.1.20 80 { weight 1 inhibit_on_failure HTTP_GET { url { path /hello.html status_code 200 } connect_timeout 3 delay_before_retry 7 } } real_server 192.168.1.30 80 { weight 1 inhibit_on_failure HTTP_GET { url { path / status_code 200 } connect_timeout 3 delay_before_retry 7 } } }
keepalivedを起動する。
# systemctl start keepalived
仮想サーバテーブルを確認する。
# ipvsadm IP Virtual Server version 1.2.1 (size=4096) Prot LocalAddress:Port Scheduler Flags -> RemoteAddress:Port Forward Weight ActiveConn InActConn TCP lvs:http rr -> 192.168.1.20:http Route 1 0 0 -> 192.168.1.30:http Route 1 0 0
リアルサーバ
iptables
クライアントからLVSサーバに対して行われたリクエストがリアルサーバに 転送されてきたときに、そのパケットをリアルサーバ自身のIPアドレスにリダイレクトする。
そしてクライアントに結果を返す時は、リアルサーバのルーティングテーブルを参照して、 LVSサーバ自身のIPアドレスから返しているように見せかけることでDRを実現する。
ここでは、LVSサーバには、パケットは戻らない。
iptablesを使うと
# iptables -t nat -A PREROUTING -d 192.168.1.100/32 -j REDIRECT # iptables -t nat -L Chain PREROUTING (policy ACCEPT) target prot opt source destination REDIRECT all -- anywhere 192.168.1.100 Chain INPUT (policy ACCEPT) target prot opt source destination Chain OUTPUT (policy ACCEPT) target prot opt source destination Chain POSTROUTING (policy ACCEPT) target prot opt source destination
これで、LVSサーバ宛てのパケットをリアルサーバ自身にリダイレクトすることができる。
nginxのコンテンツ変更
web1
# echo hello web1 > /var/www/html/hello.html
web2
# echo hello web2 > /var/www/html/hello.html
動作検証
LVS間の冗長化
VIPが設定されているか確認する。
lvs1
$ ip -f inet addr show ens18 2: ens18: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000 inet 192.168.1.20/24 brd 192.168.1.255 scope global ens18 valid_lft forever preferred_lft forever inet 192.168.1.100/32 scope global ens18 valid_lft forever preferred_lft forever
lvs2
$ ip -f inet addr show ens18 2: ens18: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000 inet 192.168.1.30/24 brd 192.168.1.255 scope global ens18 valid_lft forever preferred_lft forever
VIPをlvs1側だけが持っていることがわかる。
この状態で、lvs1で障害が発生したと想定してkeepalivedを落とすと、
lvs1
# systemctl stop keepalived $ ip -f inet addr show ens18 2: ens18: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000 inet 192.168.1.20/24 brd 192.168.1.255 scope global ens18 valid_lft forever preferred_lft forever
lv2
$ ip -f inet addr show ens18 2: ens18: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000 inet 192.168.1.30/24 brd 192.168.1.255 scope global ens18 valid_lft forever preferred_lft forever inet 192.168.1.100/32 scope global ens18 valid_lft forever preferred_lft forever
VIPがlvs2側に設定されたので、冗長化できている。
リアルサーバ間の負荷分散
lvs1で仮想サーバテーブルを確認する。
# ipvsadm -l IP Virtual Server version 1.2.1 (size=4096) Prot LocalAddress:Port Scheduler Flags -> RemoteAddress:Port Forward Weight ActiveConn InActConn TCP lvs1:http rr -> 192.168.1.20:http Route 1 0 0 -> 192.168.1.30:http Route 1 0 0
クライアントから、VIPにアクセスする。
$ curl -s --connect-timeout 3 http://192.168.1.100/hello.html hello web1 $ curl -s --connect-timeout 3 http://192.168.1.100/hello.html hello web2 $ curl -s --connect-timeout 3 http://192.168.1.100/hello.html hello web1 $ curl -s --connect-timeout 3 http://192.168.1.100/hello.html hello web2
ラウンドロビンできてる。
web1のnginx落としてから、仮想サーバテーブルを確認すると 重みが0になり、振り分けの対象から除外されていることがわかる。
# ipvsadm -l IP Virtual Server version 1.2.1 (size=4096) Prot LocalAddress:Port Scheduler Flags -> RemoteAddress:Port Forward Weight ActiveConn InActConn TCP lvs1:http rr -> 192.168.1.20:http Route 0 0 0 -> 192.168.1.30:http Route 1 0 0
VIPにアクセスする。
$ curl -s --connect-timeout 3 http://192.168.1.100/hello.html hello web2 $ curl -s --connect-timeout 3 http://192.168.1.100/hello.html hello web2 $ curl -s --connect-timeout 3 http://192.168.1.100/hello.html hello web2 $ curl -s --connect-timeout 3 http://192.168.1.100/hello.html hello web2
冗長化もできてる。
参考
2.5. パケット転送をオンにする - Red Hat Customer Portal