GeekFactory

int128.hatenablog.com

ロードバランサの内側でApache Tomcatを使うときに気を付けること

多くのWebサービスでは、ロードバランサやApache httpdアプリケーションサーバとの通信を中継しています。冗長化とアプリケーションレイヤの隠蔽が目的です。このような環境でApache Tomcatを使う場合、開発環境との違いに気を付ける必要があります。

例えば、下記の構成を考えます。

ロードバランサ(LB) Webサーバ APサーバ
https://www.example.com/   http://10.0.0.15/   ajp://172.16.0.31/

まず最初に、LBより内側で発信元のIPアドレス(ソースIP)を取得できるよう設定すべきです。これがないと内側でログが取れません。大抵はデフォルトでアドレス変換しない設定になっていると思いますが、LBの内側IPアドレスに変換する設定もあるので注意が必要です。

Apache httpdのmod_proxy_ajpを使う場合、HttpServletRequest#getRemoteAddr()でソースIPを取得できません。mod_jkだと取得できるんですけどね。IPアドレスでのアクセス制御はApache httpdで行う必要があります。どうしてもアプリで必要な場合は、WebサーバでHTTPヘッダにソースIPを載せます。

Tomcatの発行するHTTPリダイレクトが内側のURLになってしまうことがあります。これはHttpServletResponse#sendRedirect()を使っている場合に発生します。ajpだとWebサーバのホスト名などからURLを組み立てられますが、httpプロキシが間に入ると本来のURLを組み立てることができなくなり、間違ったURLがブラウザに送信されます。リダイレクトの問題はTomcatの設定変更で解決できます。

Configure your copy of Tomcat 6 to include a special element, with appropriate proxy settings, for example:

<Connector port="8081" ...
              proxyName="www.mycompany.com"
              proxyPort="80"/>

which will cause servlets inside this web application to think that all proxied requests were directed to www.mycompany.com on port 80.

http://tomcat.apache.org/tomcat-6.0-doc/proxy-howto.html

今回の構成ではSSLを使っているため、次のように設定しましょう。私の環境(Apache Tomcat 5.5.23)だと、ajpコネクタではうまくいきましたが、httpコネクタではエラーが出ました。

<Connector ...
           proxyName="www.example.com"
           proxyPort="443"
           scheme="https"
           secure="true"/>

アプリから外部に通信する場合、外部への通信を通過させるようLBを設定しておく必要があります。今回の構成だとさらに複雑で、Webサーバが外部への通信を中継する必要があります。私の場合はmod_proxy_httpを使っています。

開発環境はTomcatがむき出しの状態になっており、ブラウザからのアクセスや外部への通信でつまづくことは少ないですが、実環境では色々なレイヤが間に入るので何かと手間取ることが多いと思います。あらかじめ実環境での動作を考えながら実装していくようにしましょう。アプリ屋とインフラ屋はもっと仲良くすべし。