先日、Apache HTTPD Server 2.2.10 がリリースされたわけですが、ChangeLog を眺めていたら、いくつか気になる変更点がありました。
それは、
- mod_proxy_balancer: Add ‘bybusyness’ load balance method. [Joel Gluth , Jim Jagielski]
- mod_proxy: Add connectiontimeout parameter for proxy workers in order to be able to set the timeout for connecting to the backend separately.PR 45445. [Ruediger Pluem, rahul ]
の二つです。
まず、最初の mod_proxy_balancer に「bybusyness」というロードバランサの振り分けメソッドが追加されたという内容です。mod_proxy_balancer といえば、Apache HTTPD Server でかかせない L7 ロードバランサのモジュールです。mod_proxy_balancer の該当ドキュメントをみると、ProxyPass ディレクティブに次のように振り分けメソッドを定義できます。
ProxyPass / balancer://hotcluster/lbmethod=bybusyness
lbmethod のデフォルト値は、byrequests となっていて、byrequests とはバックエンドのウェブサーバにリクエスト回数で重み付けする振り分け方法が選択されます。その他の選択肢には、bytraffic があります。bytraffic は、転送量で重み付けする振り分け方法です。そこに、2.2.10 から bybusyness という選択肢が追加されました。bybusyness は、ドキュメント原文では「perform pending request balancing」となっているので処理待ちのリクエスト数で重み付けする方法のようです。
ProxyPass を使っている場合は上の設定でいいですが、mod_rewrite を使っている場合は Proxy ディレクティブの中で設定することができます。
<Proxy balancer://neoad>
balancermember http://web1.example.com loadfactor=10 keepalive=off retry=2
balancermember http://web2.example.com loadfactor=10 keepalive=off retry=2
ProxySet lbmethod=bybusyness
</Proxy>
設定したら、ちゃんと lbmethod が確認されるが知るために、次のような感じで balancer-manager を設定します。
ProxyPass /balancer-manager !
<Location /balancer-manager>
SetHandler balancer-manager
Order Deny,Allow
Deny from all
Allow from 127.0.0.1
</Location>
Allow from は、自分がアクセスする接続元の IP アドレスを書くか、認証をかけるようにしてください。
設定したら、Apache HTTPD Server を再起動して、/balancer-manager にアクセスします。アクセスしたページに次のようななっていれば設定はできています。
LoadBalancer Status for balancer://example
StickySession Timeout FailoverAttempts Method
- 0 1 bybusyness
さっそく、bybusyness の動作確認をみてみる。
接続したバックエンドのサーバが分かるように s1、s2 と出力するようにしておいて、curl を 10 回連続で実行させてみる。
bybusyness の前に、byrequest で確認する。
通常稼働時
s1
s2
s1
s2
s1
s2
s1
s2
s1
s2
順番にラウンドロビンされている。
意図的に s1 をダウンさせたとき。
s2
s2
s2
s2
s2
s2
s2
s2
s2
s2
あたりまえだが、s2 のみ。s1 を復帰させて retry 時間がたったあと。
s2
s1
s2
s1
s2
s1
s2
s1
s2
s1
ちゃんと s1 が復帰した。
次に bybusyness で同じように試してみる。
s1
s2
s1
s2
s1
s2
s1
s2
s1
s2
次に s1 をダウンさせる。
s2
s2
s2
s2
s2
s2
s2
s2
s2
s2
次に s1 を復帰させて、retry 時間待つ。
s2
s2
s2
s2
s2
s2
s2
s2
s2
s2
あれ?おかしい、s1 が復帰しない。しばらく待ってみたが、プロキシを再起動しない限り、s1 は復帰しなかった。balancer-manager をみてみても Status が OK のままになっているにもかかわらず、s1 にはまったく振られない。
理由を知りたいと思ったので、ソースコードをみてみた。modules/proxy/mod_proxy_balancer.c の 1100 行目に find_best_bybusyness という static 関数がある。おそらく、この関数で行っている処理だと思うので念入りに観てみた。とても短く、シンプルな関数だが、Apache のソースコードをみるのは初めてなのでよく分からないが、関数の最後にデバッグログを出力していること、バックエンドのサーバが選ばれるところは、次のようになっていた。
if (!mycandidate
|| worker->s->busy < mycandidate->s->busy
|| (worker->s->busy == mycandidate->s->busy && worker->s->lbstatus > mycandidate->s->lbstatus))
mycandidate = worker;
この busy と lbstatus の内容はデバッグログで出力されているので、デバッグログを出力するようにしてみた。デバッグログは、LogLevel debug を追加する。LogLevel は、ErrorLog ディレクティブの前に設定したほうがよい。
通常稼働時のログでは、次のように出力されている。
[Tue Nov 18 01:42:33 2008] [debug] mod_proxy_balancer.c(1173): proxy: bybusyness selected worker “http://s1/” : busy 0 : lbstatus 0
[Tue Nov 18 01:43:03 2008] [debug] mod_proxy_balancer.c(1173): proxy: bybusyness selected worker “http://s2/” : busy 0 : lbstatus 10
busy がどちらとも 0 なのでラウンドロビンされている。
s1 を落として、デバッグログをみてみる。
[Tue Nov 18 01:44:20 2008] [debug] mod_proxy_balancer.c(1173): proxy: bybusyness selected worker “http://s2/” : busy 0 : lbstatus 10
s1 を復帰させてみる。
[Tue Nov 18 01:46:00 2008] [debug] mod_proxy_balancer.c(1173): proxy: bybusyness selected worker “http://s2/” : busy 0 : lbstatus 0
s1 を復帰させて、接続したとき s1 が復帰したデバッグログは出力されるが、lbstatus がマイナスになっていた。
[Tue Nov 18 01:51:05 2008] [debug] mod_proxy_balancer.c(1173): proxy: bybusyness selected worker “http://s2/” : busy 0 : lbstatus -30
そのあと、s1 のオフオンを繰り返すたびに s2 の lbsatus が -10 ずつ減っていって、s1 を復帰させた直後の接続しても s1 が復帰したというデバッグログが出力されない。
byrequests で試してみると、s1 が復帰した直後の接続すると、次のようなログが出力される。s1 の lbstatus は 0、s2 の lbstatus は 10 になっている。
[Tue Nov 18 01:56:51 2008] [debug] mod_proxy_balancer.c(1008): proxy: byrequests selected worker “http://s1/” : busy 0 : lbstatus 0
[Tue Nov 18 01:56:51 2008] [debug] mod_proxy_balancer.c(543): proxy: BALANCER (balancer://test) worker (http://s1/) rewritten to http://x1/
[Tue Nov 18 01:56:51 2008] [debug] mod_proxy.c(988): Running scheme balancer handler (attempt 0)
[Tue Nov 18 01:56:51 2008] [debug] mod_proxy_http.c(1924): proxy: HTTP: serving URL http://s1/
busy という変数の意味を調べ観ると、どうやらリクエストを振り分ける前に ++ されて、リクエストが終わったあとに — されるという単純な整数値のようです。lbstatus という変数は、Balancermember ディレクティブの値がそのまま使われているようです。
とここまで調べて、Apache のバグとして上がっているかなと思って調べてみたが、すでにあがっていた。
というわけで、bybusyness を使うとバックエンドのサーバが復帰しないという問題があるので使うのはやめておくことにしました。修正されるまでは、byrequests でいこうと思います。
mod_proxy_balancer のソースコードをすこし読んで、keepalived のように一定時間おきにチェックするのではなくて、HTTP リクエストがあったときに調べてどのサーバにリクエストを振り分けているということが分かりました。
振り分けを決めている find_best_XXX 関数は自分でも独自のルールで定義できそうです。
次に気になった connectiontimeout というのは、mod_proxy の ProxyPass 変数で定義できる値です。原文を要約すると、バックエンドのサーバに接続するまでのタイムアウト時間を秒単位で指定する設定です。すでに、timeout や retry という設定があって同じような意味で困惑しますが、timeout はバックエンドのサーバに / リクエストを送ったときのタイムアウト時間、retry はバックエンドのサーバが復帰させるときのリトライ時間です。
ということで、次のように設定することにしました。
<Proxy balancer://test>
balancermember http://s1/ loadfactor=10 retry=7 connectiontimeout=2
balancermember http://s2/ loadfactor=10 retry=7 connectiontimeout=2
</Proxy>
サーバの設定は、一度稼働したらメンテナンスを計画しない限りずっとそのままなので、最初に気合いを入れてしっかりと設定したいものです。
Tags: apache