2006年08月28日
こんなに簡単! Linuxでロードバランサ (2)
前回までで、
というところまではできました。
これでリアルサーバへ負荷分散することができたのですが、冗長性がありませんでした。つまり、リアルサーバがダウンしても、ロードバランサはそれを認識できず、ダウンしているリアルサーバなのにパケットを送ってしまっていました。
このとき、クライアントから見ると、たまにサーバから応答がないように見えてしまいます。
というわけで今回は冗長化のお話、
を紹介したいと思います。
- 複数のWebサーバにロードバランスする
というところまではできました。
これでリアルサーバへ負荷分散することができたのですが、冗長性がありませんでした。つまり、リアルサーバがダウンしても、ロードバランサはそれを認識できず、ダウンしているリアルサーバなのにパケットを送ってしまっていました。
このとき、クライアントから見ると、たまにサーバから応答がないように見えてしまいます。
というわけで今回は冗長化のお話、
- リアルサーバのヘルスチェック
を紹介したいと思います。
keepalivedの紹介
今回はkeepalivedを使います。
おおざっぱにいうと、keepalivedは2つの機能を提供します。
- 1. ヘルスチェック機構と連携したIPVSでのリアルサーバの管理 (--check)
- 前回ipvsadmコマンドを使って行ったような、バーチャルIPアドレス (VIP) やリアルサーバの管理を設定ファイルに記述することにより行えます。
また、いくつかのヘルスチェック機能をもっていて、定期的にリアルサーバにヘルスチェックして、失敗した場合は割り当てから外してくれます。
今回のテーマはこの機能です。
- 2. VRRPによる死活監視 (--vrrp)
- VRRPという機構を使って、ロードバランサ自身をアクティブ/バックアップ構成で冗長化できます。
VRRPはルータやロードバランサに限った機構ではないので、アクティブ/バックアップ構成ならどんなシステムにも適用できます。例えば、DRBDでディスクをサーバ間で冗長化しているNFSサーバのフェイルオーバなどにも使えます。
VRRPによるロードバランサの冗長化についてはまた別の回で説明したいと思います。
keepalivedのインストール
keepalivedをインストールします。
前回のipvsadmと同じように、keepalivedもkernelのIPVSのバージョンと食い違わないようにしなければなりません。
そこでipvsadmと同様、keepalivedもディストリビューションのパッケージを使わず、ソースからコンパイルしてインストールします。
configureの--prefixはお好みに合わせて変更してください。
ここでは複数バージョンを簡単に切り替えられるように、--prefix=/usr/klab/app/keepalived-1.1.12として、いくつかのシンボリックリンクをはっていますが、こういった必要がないならば、--prefix=/usrや-prefixを省略しても構いません。
ここでは複数バージョンを簡単に切り替えられるように、--prefix=/usr/klab/app/keepalived-1.1.12として、いくつかのシンボリックリンクをはっていますが、こういった必要がないならば、--prefix=/usrや-prefixを省略しても構いません。
$ wget http://www.keepalived.org/software/keepalived-1.1.12.tar.gz
$ tar zxf keepalived-1.1.12.tar.gz
$ cd keepalived-1.1.12
$ ./configure --prefix=/usr/klab/app/keepalived-1.1.12
$ make
# make install
# ln -s keepalived-1.1.12 /usr/klab/app/keepalived
# ln -s ../app/keepalived/bin/genhash /usr/klab/bin/
# ln -s ../app/keepalived/sbin/keepalived /usr/klab/sbin/
keepalivedの設定
keepalivedを動かす前に、何点か環境をチェックします。基本的に前回の通りに構成されていれば大丈夫です。
- ipvsadm -CでIPVSの設定をクリアしておく。念のためipvsadm -Lnで空になっているか確認。
- lv1の外側に10.10.31.100のVIPがついているか確認。
- lv1がIP転送が有効になっているか確認。
cat /proc/sys/net/ipv4/ip_forwardが1になっていればOK。
- リアルサーバ(w101、w102)のデフォルトゲートウェイがlv1の内側のアドレス (192.168.31.11) を向いているか確認。
あとは、後述するヘルスチェック用に、リアルサーバw101、w102で/health.cgiを作って、CGIとして動作するようにHTTPサーバの設定をします。今回はあとのヘルスチェックの実験のためにCGIにしていますが、CGIに限らず、静的なファイルでもPHPでもJSPでも何でも構いません。
#!/bin/sh
echo 'Content-Type: text/plain'
echo
printf "%s OK\n" $(hostname)
lv1$ curl -i -H 'Host example.org' http://w101/health.cgi
HTTP/1.1 200 OK
Date: Wed, 23 Aug 2006 13:01:37 GMT
Server: Apache/2.2.3 (Unix)
Transfer-Encoding: chunked
Content-Type: text/plain
w101 OK
ではまずはkeepalivedの設定ファイルを作ります。
# 「HTTP100」という名前のバーチャルサーバグループを定義。
virtual_server_group HTTP100 {
# サービス用の (バーチャル) IPアドレスとポート番号を指定
# ipvsadmコマンドの-tの引数に対応する。
10.10.31.100 80
}
# 「HTTP100」に属するリアルサーバの設定。
# 「virtual_server_group」ではなく「virtual_server group」であることに
# ちょっと注意。(「_」じゃなくてスペース)
virtual_server group HTTP100 {
delay_loop 3 # ヘルスチェックの間隔 [秒]
lvs_sched lc # ロードバランスの方法 (以前はlb_algoでした)
lvs_method NAT # パケット転送の方法 (以前はlb_kindでした)
protocol TCP
# ヘルスチェック (HTTP_GETやSSL_GET) のときに使われる
# バーチャルホスト名。HTTPリクエストのHostヘッダに使われる。
virtualhost example.org
# リアルサーバが全滅したときにパケットが転送されるサーバ
sorry_server 192.168.31.11 80
# リアルサーバの設定 (w101)
real_server 192.168.31.101 80 {
weight 1
# ヘルスチェックに失敗したときに、設定から削除するのではなく
# weightをゼロにする。
inhibit_on_failure
# ヘルスチェックの設定
HTTP_GET {
url {
path /health.cgi # ヘルスチェックURLのパス部
status_code 200 # 期待するステータスコード
}
connect_timeout 3 # 応答が返ってくるまでのタイムアウト [秒]
}
}
# リアルサーバの設定 (w101)
real_server 192.168.31.102 80 {
weight 1
inhibit_on_failure
HTTP_GET {
url {
path /health.cgi
status_code 200
}
connect_timeout 3
}
}
}
前回と同じように、lvs_schedはlc、lvs_methodはNATにします。
リアルサーバへのヘルスチェックはdelay_loopで指定した間隔ごとに実行します。
ヘルスチェック(HTTP_GET)は、HTTPのGETリクエストをリアルサーバに送ります。その際のバーチャルホスト名はvirtualhostで、パスはHTTP_GETの中のpathで指定したものになります。今回の例だと、http://example.org/health.cgiになります。
期待するHTTPのレスポンスコードはstatus_codeで指定します。今回の場合、リアルサーバがhttp://example.org/health.cgiのリクエストに対して200以外のレスポンスコードを返した場合は、ヘルスチェック失敗となります。
ヘルスチェックのHTTPアクセスのタイムアウトをconnect_timeoutで指定します。HTTPアクセスを始めてからレスポンスを受け取りきるまでconnect_timeout秒で完了しない場合はヘルスチェック失敗となります。気をつけて欲しいのは、connect_timeoutのデフォルトはゼロなので、明示的にconnect_timeoutを指定しなかった場合、ゼロ秒でリアルサーバはレスポンスを返さないといけなくなってしまいます。(^^; ですのでconnect_timeoutは必ず指定しましょう。
この設定では、ヘルスチェックが失敗すると直ちにvirtual_server group「HTTP100」から外されます。ドキュメントにはnb_get_retryとdelay_before_retryという設定項目があって、直ちにではなく数回失敗したら外すようにできそうなのですが、少なくともkeepalived-1.1.12で試した限りではHTTP_GETでこれらリトライのパラメータは期待したように動作しませんでした。
inhibit_on_failureを指定しているので、グループから外すときは、設定を削除するのではなく、weightをゼロにします。この方が、ipvsadm -Lnで見たときにどのリアルサーバが外されているか分かりやすいと思います。
sorry_serverは全てのリアルサーバがダウンした場合に、パケットが転送されるサーバです。今回はlv1を指定していますが、実運用ではほかのロードバランサではないサーバを指定した方がよいでしょう。
また、ひとつのreal_serverとsorry_sreverという構成にすれば、アクティブ/バックアップ構成を作ることができます。
今回提示した設定は必要最低限なものです。ほかにもいろいろ - 例えばヘルスチェックの SSL_GET、TCP_CHECK、SMTP_CHECK や、notify_{up,down} など - 設定項目があるのですが、それはkeepalived付属のdoc/keepalived.conf.SYNOPSISを参照してください。
keepalivedを動かしてみる
設定ファイル (/usr/klab/etc/keepalived.conf) ができたので、いよいよkeepalivedを起動してみます。
lv1# /usr/klab/sbin/keepalived -n -S 1 -f /usr/klab/etc/keepalived.conf --check -d
- -n
- daemonとしてバックグランドで動作するのではなく、フォアグランドで動作するようになります。
- -S 1
- ログをsyslogのLOCAL1に出すようにします。
- -f /usr/klab/etc/keepalived.conf
- 設定ファイルを指定します。
- --check
- IPVSとヘルスチェックの機能だけを有効にします。
今回は必要ないので--vrrpは指定しません。
- -d
- 起動時に設定をログ(syslog)にダンプします。
設定項目が意図したものになっているか確認したいときに有益です。
keepalivedが起動したら、ipvsadmコマンドでIPVSの設定を確認してみましょう。
lv1# ipvsadm -Ln --sort
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
-> RemoteAddress:Port Forward Weight ActiveConn InActConn
TCP 10.10.31.100:80 lc
-> 192.168.31.101:80 Masq 1 0 0
-> 192.168.31.102:80 Masq 1 0 0
また、リアルサーバのアクセスログを確認して、ヘルスチェック用のリクエストが来ているか確認してみましょう。
ここまで確認できれば、外部からhttp://example.orgにアクセスしてちゃんとロードバランスできると思います。
リアルサーバの管理
続いてリアルサーバを落としてみて、keepalivedがちゃんとそれを検知して管理してくれるか確認してみます。
まずはw102のHTTPサーバを終了してみましょう。
数秒後にlv1のログファイルに
lv1 Keepalived_healthcheckers: Error connecting server [192.168.31.102:80].
lv1 Keepalived_healthcheckers: Disabling service [192.168.31.102:80] from VS [HTTP100:0]
と記録されて、w102のweightがゼロになるはずです。
lv1# ipvsadm -Ln --sort
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
-> RemoteAddress:Port Forward Weight ActiveConn InActConn
TCP 10.10.31.100:80 lc
-> 192.168.31.101:80 Masq 1 0 0
-> 192.168.31.102:80 Masq 0 0 0
そして外部からhttp://example.orgにアクセスすると、w101だけにアクセスがいくはずです。
では、w102のHTTPサーバを起動しなおしてみましょう。
ほどなくしてlv1のログファイルに
lv1 Keepalived_healthcheckers: HTTP status code success to [192.168.31.102:80] url(1).
lv1 Keepalived_healthcheckers: Remote Web server [192.168.31.102:80] succeed on service.
lv1 Keepalived_healthcheckers: Enabling service [192.168.31.102:80] to VS [HTTP100:0]
と記録され、ipvsadmコマンドでもw102がサービスに復帰した(weightが1に戻った)ことが確認できると思います。
みごとにヘルスチェック機構が動作しました。
では次に、200以外のレスポンスコードを返すようにしてみましょう。
ヘルスチェック対象となるhealth.cgiを次のようにレスポンスコード500を返すようにします変更します。
#!/bin/sh
echo 'Status: 500 hmmm'
echo 'Content-Type: text/plain'
echo
echo down...
するとlv1のログに
lv1 Keepalived_healthcheckers: HTTP status code error to [192.168.31.102:80] url(/health.cgi), status_code [500].
lv1 Keepalived_healthcheckers: Disabling service [192.168.31.102:80] from VS [HTTP100:0]
と記録され、w102がサービスから外されたのが確認できると思います。
続いてw101のHTTPサーバも停止してみましょう。これで全てのリアルサーバがダウンしたこととなり、keepalived.confで設定したsorry_serverにアクセスがゆきます。
sorry_serverでは、Apacheならば
<VirtualHost *:80>
ServerName example.org
DocumentRoot /var/www/example.org/htdocs/
AliasMatch .* /var/www/example.org/htdocs/sorry
</VirtualHost>
lv1$ cat /var/www/example.org/htdocs/sorry
gomen gomen
と設定して、どんなパスのリクエストでもhtdocs/sorryを返すようにしておけばよいでしょう。先にものべましたが、今回はlv1をsorry_serverにしていますが、本番運用ではロードバランサではないほかのHTTPサーバを指定すべきでしょう。
client$ curl -H 'Host: example.org' http://10.10.31.100
gomen gomen
最後にOS起動時にkeepalivedが起動するようにします。
/etc/init.d/keepalivedを作ってもよいのですが、keepalivedが意図せずダウンするとたいへんなことになるので、daemontoolsなどを利用して起動するとよいと思います。その場合は、このようなファイル(ipvs/run)を作ればよいでしょう。
#!/bin/sh
exec 2>&1
exec /usr/klab/sbin/keepalived -n -S 1 -f /usr/klab/etc/keepalived.conf --check
まとめ
今回は、keepalivedを使って負荷分散と冗長性と兼ね備えたロードバランサを作りました。
さて、これで枕を高くして寝られるでしょうか?
ちょっと待ってください。ロードバランサ自身がダウンしたら全てのサービスが停止してしまいます。 (^^;
枕を高くして寝るにはほかにもいろいろとしなければならないのですが、まずはその第一歩として、次回はkeepalivedのVRRPの機能を使って、ロードバランサ自身の二重化をしてみたいと思います。
参考
- keepalived
- ドキュメント集
- doc/keepalived.conf.SYNOPSIS (keepalived.confのリファレンス)
- ドキュメント集
- LVS-mini-HOWTO
- LVS-HOWTO
- 20. LVS: Running a firewall on the director: Interaction between LVS and netfilter (iptables).
IPVSとNetfilter (iptables) の関係の図があります。
- 20. LVS: Running a firewall on the director: Interaction between LVS and netfilter (iptables).
- 『サーバ負荷分散技術』
- 『CDNプロトコル入門 - アクセス集中からWebを守る』
トラックバックURL
この記事へのトラックバック
1. IPVS+keepalived(1.2.1)でサービス監視 [ インフラ技術の実験室 ] 2011年05月29日 22:50
IPVSを使用してOpenblocks600をロードバランサにしましたが、障害中のリアルサーバにもアクセスを割り当ててしまうため、keepalivedでサービス監視を行うことで障害中のリアルサーバを自動的にロードバランス先か...
2. daemontoolsでkeepalivedデーモンの稼動管理 [ インフラ技術の実験室 ] 2011年06月04日 23:40
daemontoolsでkeepalivedデーモンの稼動管理を行うことで、異常終了した際に自動的にデーモンを再起動できるようにします。
この記事へのコメント
3. Posted by とっとこ 2008年04月08日 13:45

それで1点お尋ねした移転があるのですが、(1)でVIPを設定後、リアルサーバのRIPを追加するときにパケット転送方法をDirect Server Return(DSR)にしたのですが、この場合でkeepalivedを設定する場合も上記の方法で大丈夫でしょうか?それとも何か変えなければならないでしょうか?
例:lvs_schedやlvs_methodをDSRにあわせなるとか?
よろしくお願いします。
4. Posted by Yasui 2008年04月08日 14:47
とっとこさん、コメントありがとうございます。keepalivedはLVSを制御する機能をもっているため、ipvsadmを使ってVIPとRIPを設定しておく必要はありません。/etc/keepalived.confを作ってkeepalivedを起動すれば自動的にVIPとRIPを設定してくれます。DSRで使う場合には、lvs_method DRと記述しておく必要があるとおもいます。
また、DSRについてはhttp://dsas.blog.klab.org/archives/50678999.htmlでも書いていますのでご参照くださいませ。
また、DSRについてはhttp://dsas.blog.klab.org/archives/50678999.htmlでも書いていますのでご参照くださいませ。
5. Posted by とっとこ 2008年04月08日 22:08

検討していた分散化ができそうです。
6. Posted by ぽむ 2008年04月22日 14:06
大変参考になる記事をありがとうございます。
keepalived.confの「authentication」部分で、認証タイプとパスワードを設定していますが、これは何に利用されるのでしょうか?
keepalived.conf.SYNOPSISを呼んでみたのですが具体的な説明がなく、何の為の設定かわからず悩んでおります。
お分かりであれば教えてください。
keepalived.confの「authentication」部分で、認証タイプとパスワードを設定していますが、これは何に利用されるのでしょうか?
keepalived.conf.SYNOPSISを呼んでみたのですが具体的な説明がなく、何の為の設定かわからず悩んでおります。
お分かりであれば教えてください。
8. Posted by DSASの中の人 2008年05月20日 16:58
> ぽむさん、
ごめんなさい、返事が遅くなりました。
認証タイプの意味はRFC2338の5.3.6節で定義されている内容になります。
ただし、RFC3768ではこの項目は削除され、互換性のためだけに残されているので、今後はなくなるかもしれませんね。
ごめんなさい、返事が遅くなりました。
認証タイプの意味はRFC2338の5.3.6節で定義されている内容になります。
ただし、RFC3768ではこの項目は削除され、互換性のためだけに残されているので、今後はなくなるかもしれませんね。