仕事柄BIG-IPをはじめとする負荷分散装置を取り扱っている。確かに高機能でよい製品なのだが最下位機種でも定価で400万円以上もするし、他社の安価な品でも売値で100万を切るものは少ない。また安いものは作りもそれなりなのである。負荷分散装置は冗長化で2台いれるのが常識なので、費用負担も倍になる。
ところが意外と知られていないが、簡単な負荷分散装置(L4スイッチ)はすべてオープンソースで作ることができる。高度な制御機能はないが、大半のユーザは単純なL4負荷分散くらいしか使わないので十分である。設定や管理が面倒な部分はあるが、CentOS5.0ではソフトウェア自体はすべてyumでインストールができるので、試してみる価値はあるとおもう。
以前の作業でXenを使って1台の物理サーバ上にデータセンタ構成を作った。これを利用してDMZ上にLVSによる負荷分散装置を利用したWebシステムを作る。
LVSの動作にはNATモードとDSR(Direct Server Return)モードがある。NATモードはVIPあてに来たパケットのあて先IPアドレスを実サーバに変換して実サーバに送る。そのため返信パケットも必ずLVSを通り、IPアドレスを元に戻さなければいけない。DSRモードはパケットのIPアドレスではなく、あて先MACアドレスのみを実サーバに変換する。よって戻りパケットはLVSサーバを通る必要がないが、複数の実サーバが同じIPアドレスを持つ必要があり、すこし設定が複雑になる。両者を比べるとDSRモードのほうがLVSの負荷が低くなり、大規模なシステムに向くといわれている。
NATモードとDSRモードでは物理的な繋ぎ方も違ってくる。NATモードではLVSサーバはファイアウォールのようにクライアントと実サーバの間にいなければいけないが、DSRモードではLVSサーバは実サーバと同じセグメントにいなければいけない。
はじめは概念や設定がシンプルなNATモードで試していたのだが、なぜか返信パケットにTCPチェックサムエラーが出てしまい通信ができない。LVSが返信パケットのアドレスを書き戻すときにバグがあるのかもしれない。いろいろ調べても解決できなかったが、多くの人が何も問題なく使えているようなので、もしかしたらXenに特有の問題なのかもしれない。
よって、DSRモードで設定することにする。構成は下図のようになる。
peth1 eth1 eth0 peth0
[物理I/F]-----(xenbr1)----------[Domain0]----------(xenbr0)-----[物理I/F]
|dummy0 | | | | eth0
| | | | +-----[ns1]
| | | | eth0
| | | +-------[ns2]
| | | eth0
| | +---------[storage1]
| | eth0
(xenbr2) +-----------[manager1]
| | | |
| | | |eth0 eth1
| | | +--[lvs1]---(xenbr4)
| | | eth0 eth1 |
| | +----[lvs1]------+
| | eth0
| +------[www1]
| eth0
+--------[www2]
【lvsサーバの設定】
①以下のパッケージをインストールする。
[root@lvs1 ~]# yum install ipvsadm [root@lvs1 ~]# yum install heartbeat [root@lvs1 ~]# yum install heartbeat-ldirectord
ipvsadmは、サーバ負荷分散動作の管理をする。実際の負荷分散処理はカーネルモジュールとして実装されいている。
heartbeatは、VIPを作り、2台のLVSサーバ間で冗長化を実現する。
heartbeat-ldirectordは、実サーバの監視を行い、内部でipvsadmを使って自動的に負荷分散対象サーバの追加や削除をする。
②heartbeatを設定する。
/etc/ha.d/ha.cfはlvs1とlvs2で少しだけ設定が違う。
[root@lvs1 ~]# vi /etc/ha.d/ha.cf logfile /var/log/ha-log keepalive 2 deadtime 30 warntime 10 initdead 120 udpport 694 ucast eth2 192.168.4.2 auto_failback on node lvs1 lvs2 ping 192.168.2.254 respawn hacluster /usr/lib64/heartbeat/ipfail apiauth ipfail gid=haclient uid=hacluster
[root@lvs2 ~]# vi /etc/ha.d/ha.cf logfile /var/log/ha-log keepalive 2 deadtime 30 warntime 10 initdead 120 udpport 694 ucast eth2 192.168.4.1 auto_failback on node lvs1 lvs2 ping 192.168.2.254
あとの2つの設定ファイルはlvs1とlvs2で同じ。
[root@lvs1 ~]# vi haresources lvs1 IPaddr::192.168.2.1/24/eth0 ldirectord
[root@lvs1 ~]# vi authkeys auth 1 1 crc
[root@lvs1 ~]# chmod 0600 authkeys
heartbeatを自動起動するようにする。
[root@lvs1 ~]# chkconfig heartbeat on [root@lvs1 ~]# service heartbeat start
heartbeatを起動すると、アクティブ系のサーバではVIPができている。
[root@lvs1 ~]# ifconfig
...
eth0 Link encap:Ethernet HWaddr 00:16:3E:2B:02:02
inet addr:192.168.2.2 Bcast:192.168.2.255 Mask:255.255.255.0
inet6 addr: fe80::216:3eff:fe2b:202/64 Scope:Link
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:69881 errors:0 dropped:0 overruns:0 frame:0
TX packets:67564 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:7525114 (7.1 MiB) TX bytes:6827200 (6.5 MiB)
eth0:0 Link encap:Ethernet HWaddr 00:16:3E:2B:02:02
inet addr:192.168.2.1 Bcast:192.168.2.255 Mask:255.255.255.0
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
...
③ldirectordを設定する。
[root@lvs1 ha.d]# vi /etc/ha.d/ldirectord.cf
checktimeout=5
checkinterval=60
autoreload=yes
logfile="/var/log/ldirectord.log"
virtual=192.168.2.1:80
real=192.168.2.4:80 gate
real=192.168.2.5:80 gate
fallback=192.168.2.6:80 gate
checktype=negotiate
protocol=tcp
service=http
request="test.html"
receive="i am alive"
virtualhost="www.grandarbre.net"
scheduler=rr
persistent=600
netmask=255.255.255.255
ここでautoreload=yesとしているので、今後ldirectord.cfの設定を変更しても自動的に反映される。
ldirectordはheartbeatに呼び出されて起動されるので、chkconfig ldirectord offとしておく。
また負荷分散装置としての設定はipvsadmが管理をするが、ldirectordが自動的にコマンドを発行してipvsadmを設定してくれる。よって別途ipvsadmを設定する必要はなく、chkconfig ipvsadm offとしておく。
上記の設定では、実サーバがダウンした場合にもpersistentテーブルに載っている通信は、そのままダウンしたサーバに送り続けられる。これを防ぐために、以下のカーネルパラメータを設定する。
[root@lvs1 ha.d]#echo 1 > /proc/sys/net/ipv4/vs/expire_quiescent_template
これを恒久的にする場合は/etc/sysctl.confに以下の行を追加する。
[root@lvs1 ha.d]# vi /etc/sysctl.conf ... net.ipv4.vs.expire_quiescent_template = 1 ...
【Webサーバの設定】
①ARPの設定
Webサーバでは、VIPをループバックインターフェースに設定する。また同じVIPを複数のWebサーバで共有するため、このVIPに関するARPを投げたり受けたりしないようにする。
[root@www1 ~]# vi /etc/sysctl.conf ... net.ipv4.conf.all.arp_ignore = 1 net.ipv4.conf.all.arp_announce = 2 ... [root@www1 ~]# sysctl -p
arp_ignore=1は、ARPリクエストのIPが、それを受診したインターフェースに設定されたIPであった場合にのみARPリプライを投げる。よってeth0でVIPあてのARPリクエストを受診してもリプライを返さない。
arp_announce=2は、ARPリクエストを送信する際の送信元IPとして、通信相手に一番近いインターフェースのIPを使う。つまりVIPを送信元アドレスとした通信を始める際に送信されるARPリクエストの送信元IPとして、eth0のIPを使う。
②ループバックインターフェースの設定
[root@www1 ~]# vi /etc/sysconfig/network-scripts/ifcfg-lo:0
DEVICE=lo:0
IPADDR=192.168.2.1
NETMASK=255.255.255.255
ONBOOT=yes
[root@www2 ~]# ifup lo:0
[root@www1 ~]# ifconfig lo:0
lo:0 Link encap:Local Loopback
inet addr:192.168.2.1 Mask:255.255.255.255
UP LOOPBACK RUNNING MTU:16436 Metric:1
www2でも全く同じIPを設定する。
③ヘルスチェック用のHTMLを用意。
ldirectordの設定で下記のように設定しているので、Webサーバにこの内容のHTMLファイルを用意する必要がある。
...
request="test.html"
receive="i am alive"
...
[root@www1 ~]# vi /var/www/html/test.html i am alive
【動作確認】
アクティブ側のLVSサーバで動作状態を確認する。
[root@lvs1 ~]# ipvsadm -L -n IP Virtual Server version 1.2.1 (size=4096) Prot LocalAddress:Port Scheduler Flags -> RemoteAddress:Port Forward Weight ActiveConn InActConn TCP 192.168.2.1:80 rr persistent 1 -> 192.168.2.4:80 Route 1 0 0 -> 192.168.2.5:80 Route 1 0 0
Weightが1以上のサーバが稼動状態で、0のサーバはhttpdが落ちている状態。
クライアントからwgetを使ってVIPにアクセスしてみる。
[ishii@storage1 ~]$ wget -O - --quiet 192.168.2.1 <html> <body> <font color=red>This is www3</font> </body> <html>
