アシアルブログ

アシアルの中の人が技術と想いのたけをつづるブログです

mod_proxy_balancerで特定のレスポンスコードを検知させる方法

お久しぶりです!田中です。

今回は自分が関わっている件で少し調査を行う必要があった件を紹介します。具体的には、mod_proxy_balancerでワーカーから特定のレスポンスコードが帰ってきた際、バランサーからワーカーを外す方法です。

Apache 2.2になりmod_proxy_balancerを使うと簡単にリバースプロキシを介した負荷分散+冗長化が行えるようになりました。ワーカーサーバーが停止した(応答が返ってこない)場合には自動的に分散先から外すなど便利な機能も有しており、便利に使いこなしている方も多いことでしょう。

一応mod_proxy_balancerの紹介を行っておくと、mod_proxyおよびmod_proxy_httpを利用したリバースプロキシ構成に対し、ロードバランサー機能を提供するものです。実績も豊富にあり、簡単な構成ならApacheの設定ディレクティブも最小限の追加で済むため、アシアルでも非常に良く使っています。

ロードバランサー構成をとる場合に注意が必要なのは、ヘルスチェックの方法です。ヘルスチェックとはワーカー(バランサーメンバー)が生きていることを確認する仕組みのことで、mod_proxy_balancerでは応答が返ってこない(ポートが空いていない)場合に死んだと見なします。この場合、例えば特定のワーカーがプログラムエラー等で終了した場合には応答は返ってくる(たとえば500)ことになるため、mod_proxy_balancerはそのサーバーをワーカープールから外しません。データベースに接続できない場合はワーカープールから取り除く方法がないかなと調べていたら、ちょうど最近そのようなパッチがApacheチームに提供されていたため、早速試してみました。

紹介するパッチは、ID 48939としてBugzillaに登録されています。このパッチを適用すると、ProxySetディレクティブに対してfailonstatusという設定項目が追加され、特定のレスポンスコードに対してワーカーをエラー状態と扱うものとなっちます。

このパッチを使うと、例えばアプリ側でレスポンスコード「599」を定義し、データベースやmemcachedサーバーへの接続失敗時にはこのレスポンスコードを返すことで、ユーザーにエラーページを出すことなくサービスを継続させることが可能となります。

早速試したので、その紹介をいたします。
まずはファイルをダウンロード



$ wget -O failonstatus.patch https://issues.apache.org/bugzilla/attachment.cgi?id=25923
$ wget http://ftp.riken.jp/net/apache/httpd/httpd-2.2.16.tar.gz


展開を行い、パッチを当てます



$ tar vxzf httpd-2.2.16.tar.gz
$ cd httpd-2.2.16
$ patch -p1 < ../failonstatus.patch
can't find file to patch at input line 5
Perhaps you used the wrong -p or --strip option?
The text leading up to this was:
--------------------------
|Index: httpd-2.2.x/docs/manual/mod/mod_proxy.xml
|===================================================================
|--- httpd-2.2.x/docs/manual/mod/mod_proxy.xml  (revision 965843)
|+++ httpd-2.2.x/docs/manual/mod/mod_proxy.xml  (working copy)
--------------------------
File to patch:
Skip this patch? [y] y
Skipping patch.
1 out of 1 hunk ignored
patching file modules/proxy/mod_proxy_balancer.c
patching file modules/proxy/mod_proxy.c
patching file modules/proxy/mod_proxy.h


マニュアルの形式がXML化されていたため、こちらの適応は見送りました。
後はおきまりのconfigure & make install。configure時には、proxy-balancerは別途指定する必要があることに注意しましょう。



$ ./configure --enable-modules=all --enable-mods-shared=all --enable-proxy-balancer --enable-proxy --enable-proxy-http
$ make
# make install


あとは、3台分のサーバーを定義して起動させます。まずはロードバランサー



$ vi conf/httpd.conf

Listen 80
ProxyRequests Off
ProxyPass / balancer://cluster lbmethod=byrequests timeout=1
<Proxy balancer://cluster>
   BalancerMember http://127.0.0.1:81 route=1
   BalancerMember http://127.0.0.1:82 route=2
   # 下記のように、レスポンスコードをカンマでつなげて指定します
   ProxySet lbmethod=byrequests nofailover=Off failonstatus=500,599
</Proxy>

$ httpd -f conf/httpd.conf


1台目のワーカー



$ vi conf/httpd-worker1.conf
# 変更点はListen、DocumentRoot、そしてログのパス
Listen 81

$ httpd -f conf/httpd-worker1.conf


2台目のワーカー



$ vi conf/httpd-worker2.conf
# 変更点はListen、DocumentRoot、そしてログのパス
Listen 82

$ httpd -f conf/httpd-worker2.conf


これで3台分のサーバーが起動しました。

あとは、実際に一台のApacheでエラーコードを返してみます。エラーログでは、下記の通り表示され、問題なく動作していることが分かります。



[error] proxy: BALANCER: (balancer://cluster).  Forcing recovery for worker (http://127.0.0.1:82), failonstatus 500


このパッチ、TRUNKには組み込まれているものの、バージョン2.2のブランチには反映されていませんでした。ただ、パッチの分量も多くないことから、そう困難なくソースコードへのパッチ適用は可能そうです。

大規模サイトでない限りニーズが無いかも知れませんが、頭の隅に覚えておくと便利な機能だと思います。