CentOS5.0とLVS(Linux Virtual Server)でWeb負荷分散装置を作る

仕事柄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>