mod_proxy_balancerで中〜大規模サーバー運用するときの勘所 – (4) mod_deflateと組み合わせる際の注意点編

Apache2.2から、ロードバランシングをしてくれるmod_proxy_balancer というモジュールが標準添付になりました。

このモジュール、その名前の通り、ApacheレベルでHTTPリクエストをバックエンドのサーバーに振り分けることでロードバランシングをしてくれるモジュールです。

Apacheの公式ドキュメントや試しに入れてみた人のBlogなどは散見されますが、実際の現場で運用している事例というのはまだ無いようです。

そこで、実際にピーク時にover 500 request/secでmod_proxy_balancerなサーバーを運用している経験をふまえ、つまずいた点などを公開していきたいと思います。

今回は、mod_deflateと組み合わせる際の注意点です。前回までの設定ではhttpd.confに以下のように書いていたかと思います。

ProxyRequests Off
ProxyPass / balancer://cluster/ timeout=2

<Proxy balancer://cluster/>
BalancerMember http://192.168.1.1/ loadfactor=10 keepalive=On
BalancerMember http://192.168.1.2/ loadfactor=10 keepalive=On
</Proxy>

さて、mod_proxy_balancerようなモジュールを使うサイトというのはApacheレベルで300 Request/secを超えるようなサイトだと思います。このようなサイトで気になってくるのは帯域の問題。基本的に流せるデータ量によって回線使用料が決まってくるのであり、圧縮効率のよいHTMLやCSSやJavaScriptはなるべくmod_deflateで圧縮して転送し、またブラウザにキャッシュされた後から変更のないファイルは「304 Not Modified」をHTTPレスポンスとして送ることで、データの転送量を少しでも削減することが重要になります。

普通に圧縮すればいいだけだろうと思って、以下のように設定すると、意外と帯域が節約できてないことに気づくと思います。

ProxyRequests Off
ProxyPass / balancer://cluster/ timeout=2

<Proxy balancer://cluster/>
AddOutputFilterByType DEFLATE text/html text/css application/x-javascript
BalancerMember http://192.168.1.1/ loadfactor=10 keepalive=On
BalancerMember http://192.168.1.2/ loadfactor=10 keepalive=On
</Proxy>

これで帯域が節約できない原因は意外なところにありました。

BalancerMemberで指定したバックエンドのサーバーが返すHTTPレスポンスには普通ETagという、コンテンツが更新されているかどうかを判定するためのヘッダが含まれるのですが、そのETagの生成に、ファイルのinode番号が使われていたのでした。

inode番号はサーバーごとにほぼ固有の値になってしまうのであり、192.168.1.1と192.168.1.2にアクセスが振り分けられた際には、それぞれ違うETagが返されてしまいます。違うEtagが返ってくると、ブラウザはコンテンツが更新されたものとして、新たに取得してしまうので、その分の帯域を使ってしまうと言うことでした。

これの解決策として、mod_headersモジュールを使ってブラウザへETagヘッダを返さないようにするのと、ブラウザからのIf-None-MatchヘッダをBalancerMemberに渡さないようにする方法をとりました。

ProxyRequests Off
ProxyPass / balancer://cluster/ timeout=2

<Proxy balancer://cluster/>
AddOutputFilterByType DEFLATE text/html text/css application/x-javascript
RequestHeader unset If-None-Match
Header unset ETag
BalancerMember http://192.168.1.1/ loadfactor=10 keepalive=On
BalancerMember http://192.168.1.2/ loadfactor=10 keepalive=On
</Proxy>

FileETag ディレクティブでETagの生成にinode番号を使わないようにする(FileETag MTime Size)とすることでも回避可能ですが、IEでは一旦ETagがキャッシュされてしまうと新しいETagに置き換えてくれないという問題があり、この回避策は見送りました。

さて、これでもまだ帯域が節約できていないことに気づくと思います。

原因はブラウザシェア8〜9割を占めるIEの実装にありました。

mod_deflateを使うと、レスポンスヘッダにVary: Accept-Encodingが追加されるのですが、これがあると常にIEはIf-Modified-Sinceヘッダを送らずにコンテンツを取得しようとするのでした。(See also: IE ぁぃぃ〜 – にぽたん研究所

これの回避策としてはレスポンスヘッダからVaryヘッダを取り除くことが挙げられます。副作用としてAccept-Encoding: deflateを送っていないブラウザにもdelateなコンテンツをデリバリしてしまうことになりますが、 deflateに対応していないブラウザというのはごく少数だと思われるので、問題ないと言うことにしました。

ProxyRequests Off
ProxyPass / balancer://cluster/ timeout=2

<Proxy balancer://cluster/>
AddOutputFilterByType DEFLATE text/html text/css application/x-javascript
RequestHeader unset If-None-Match
Header unset ETag
Header unset Vary
BalancerMember http://192.168.1.1/ loadfactor=10 keepalive=On
BalancerMember http://192.168.1.2/ loadfactor=10 keepalive=On
</Proxy>

Leave a Reply

Your email address will not be published. Required fields are marked *