技術的雑談-複数のTomcatを立ち上げてmod_jk経由でロードバランスさせる
環境
- 使用OS:CentOS 3.5
- httpd:Apache 2.0.46(多分2.0.xだったら大丈夫)
- JDK 1.5.0_10
- Apache tomcat 5.5.17
- mod_jk(tomcat-connector) 1.2.20
- mod_jkを使ってTomcatとhttpdを連携するが完了していること。
目的
- 1つのサーバ(OSインスタンス)上で複数のTomcatインスタンスを立ち上げる。
- mod_jkの機能を使い、複数のTomcat(JSP/Servletエンジン)を1つのhttpdから公開する。
今回の完成版の構成図は以下のようになります。
┌――――┐ httpリクエスト→ │ Apache ├―――┐ 処理を依頼 ┌――――┐ | |mod_jk|―――――→| Tomcat1| httpレスポンス← | | |←―――――| | | | | 結果を返す └――――┘ | | | | | | 処理を依頼 ┌――――┐ | | |―――――→| Tomcat2| | | |←―――――| | | | | 結果を返す └――――┘ └――――┴―――┘ ※ 2つのTomcatは同一OSインスタンス上で稼動
例えば、
「どうしてもあるWeb ApplicationでTomcatインスタンスを1つ占領したい」
「Tomcatの処理能力を増やしたい」
「実験をしたい!」(ぉ
などの目的に使用できます。
また、別記の複数のTomcat間でSessionの途切れないClusterを作るの基礎技術にもなります。
手順
Tomcatの複数インスタンスとは?
普通、Tomcatは1つのJava-VM上でマルチスレッド動作をし、複数のWeb Applicationを単一のTomcatで処理する事ができます。
server.xmlを読むとわかるのですが、Tomcatのインスタンス(メモリー上で実行されている実体)1つの中にも複数のEngineを定義したり、ContextというWeb Applicationの単位を追加したりできます。
(この辺の話は…そのうち書くかもしれない。とりあえず外部参照!(爆))
ただ、試験や特別の理由でサーバ1台(≒OSインスタンス1つ。最近はXenとかVMWareとかあるのでハード的なサーバ1台=OS1つではなくなってきつつある…(汗))に複数台のTomcatを乗せて動かしてみたりしたくなることもあります。
ここでは単純に、
「startup.shを叩いて起動されるTomcatのプロセス一つ=Tomcat 1つ」と考えます。
しかし、TomcatはディレクトリやSocketなど諸々のOSリソースを占有するServerなので、単純に「startup.sh」を2回叩いてもTomcatは2つにはなりません。(後から起動したTomcatがエラーになり立ち上がらないはずです。)
Tomcatが使用するリソース
現状、Tomcatが使用するCPUとHDDとMemory以外のリソースは以下のもののようです。
(いずれもDefaultのserver.xmlの状態で。)
- TCP Socket 8005番
- TomcatのShutdownなどのコマンドを投入する為に使われています。
- TCP Socket 8009番
- Tomcatがmod_jkと通信する為に使用されます。(全くDefaultのserver.xmlではコメントアウトされているかもしれません。)
- TCP Socket 8080番
- TomcatがHTTPプロトコルを受け付けるために使用されます。
- (Tomcatの元Directoryの)/temp/ Directory
- TomcatのSessionなどの情報がShutdown時に一時記憶されたりするらしいです。
- (Tomcatの元Directoryの)/work/ Directory
- JSPが一時的にコンパイルされたり、Session情報が入れられたりします。server.xmlの<Engine>タグのname要素で指定されたEngine名前でサブDirectoryが作られ、その下にWeb Application名でDirectoryが作られ、その下にファイルが置かれます。
- (Tomcatの元Directoryの)/log/ Directory
- Tomcat実行時のLogファイルが作成されます。
- (Tomcatの元Directoryの)/conf/ Directory
- Tomcatの設定ファイルが置かれます。
- (Tomcatの元Directoryの)/webapp/ Directory
- Tomcatで処理されるWeb ApplicationのJSPやServletが置かれます。
準備
今回、「複数のTomcatを1つのhttpdで束ねる」という全体なので、mod_jkが既にInstallされて稼動可能になっていないとなりません。
Install
/usr/java/apache-tomcat-5.5 Directoryを同じOS内でコピーします。
ここでは仮に/usr/java/apache-tomcat-5.5-2とします。
設定
まず、apache-tomcat-5.5-2側のserver.xml内のTCP Portをapache-tomcat-5.5とぶつからないように修正します。
- <Server port="8005" shutdown="SHUTDOWN">
- この"8005"を空いているTCP Port番号にします。(ここでは9005にします。)
- <Connector port="8009" enableLookups="false" protocol="AJP/1.3" />
- この8009を空いているTCP Port番号にします。(ここでは9009にします。)
- <Engine name="Catalina" defaultHost="localhost">
- 念のため、ここのname="Catalina"を"Catalina2"にします。これによってwork/下の内容がwork/Catalina2/に作られるようになります。
次に、これはapache-tomcat-5.5側でもapache-tomcat-5.5-2側でも行わなくてはならないのですが、それぞれのEngineにmod_jkの振り分けに使う名前を付けます。
これは何かと言うと、Tomcat上でのSession情報とTomcatインスタンスの齟齬が起きないようにする為のものです。
例を挙げると、
- Clientがhttpdにリクエストを投げる
- httpdはそれがJSP/Servletが処理するべきものだと判断したらそれをmod_jkに回す
- mod_jkはTomcat1にそのリクエストを処理させたとする。この時、Tomcat1ではSession ID「AAAA」を発行するとする。
- 処理結果をmod_jk経由でhttpd→Clientに返す。ClientはSession ID 「AAAA」を今回のSessionと信じる。
- ClientがSession 「AAAA」の次のリクエストとしてまたhttpdに要求を投げる。
- httpdはそれがJSP/Servletが処理するべきものだと判断したらそれをmod_jkに回す
- mod_jkは(振り分け方法が指定されていなかったら)負荷分散のために今度はTomcat2に処理を回す。
- Tomcat2はSession ID 「AAAA」なるものは知らないので、Session ID 「AAAA」を無効とし、新しいSession ID 「BBBB」を発行する。
- 処理結果をmod_jk経由でhttpd→Clientに返す。ClientはSession ID 「BBBB」を今回のSessionと信じる。
- 以下繰り返し。
つまり、Session IDがリクエストの度に発行され、Sessionの意味がなくなってしまう。
これを防ぐには、一旦作成されたSessionを、Sessionを発行したTomcatで処理し続ければよい。
(※違ったアプローチで「Session IDと内容を共有する」という方法も当然ある。それが別記のTomcat Cluster化のこと。)
TomcatのEngineに対してmod_jk用の振り分け名を付けるには、server.xmlの<Engine>タグに「jvmRoute」というPropertyを追加する。
# apache-tomcat-5.5側のserver.xml <Engine name="Catalina" defaultHost="localhost" jvmRoute="tomcat1"> # apache-tomcat-5.5-2側のserver.xml <Engine name="Catalina" defaultHost="localhost" jvmRoute="tomcat2">
この時のjvmRouteの文字列は後でworkers.propertiesにも記載するので覚えておく。
次に、workers.propertiesファイルを編集する。
worker.list=wlb, jkstatus # tomcat1(apache-tomcat-5.5)用の設定 worker.tomcat1.port=8009 worker.tomcat1.host=localhost worker.tomcat1.type=ajp13 # tomcat2(apache-tomcat-5.5-2)用の設定 worker.tomcat2.port=9009 worker.tomcat2.host=localhost worker.tomcat2.type=ajp13 # 割り振り比率の設定(ここでは両Tomcatに均等になるようにリクエストを分散する) worker.tomcat1.lbfactor=10 worker.tomcat2.lbfactor=10 # ロードバランサーの設定 worker.wlb.type=lb worker.wlb.balance_workers=tomcat1, tomcat2 worker.wlb.sticky_session=True worker.wlb.method=Session
- 実worker名
- 実worker名は必ずjvmRouteの名前とあわせる。そうしないと正常にSessionが振り分けられない。文献によっては「任意の値でOK」みたいに書かれているが、「jvmRouteにあわせる」が正解。(確認済み。)
- worker.list
- 後述するロードバランサーの仮想workerを含める。逆にtomcat1などの実workerは含めてはいけない。
- port、localhost、type
- それぞれTomcatの実際の設定に沿った定義をする
- lbfactor
- 小さいほうがより多くの仕事を振られる。同じロードバランサーに所属する実Workerのlbfactor値は相対的に比較され、比率が決定される。
- worker.wlb.type=lb
- 「type=lb」はロードバランサーを意味する特別なworker種類
- balance_workers
- このロードバランサーの振り分け対象となる実Workerをカンマ区切りで列挙する。
- sticky_session
- Sessionごとに使用するTomcatを固定する、という意味。(書かなくても=TrueがDefaultらしいが…。)
- method=Session
- 振り分け単位を設定する。他にはRequest、Trafficなどの設定があるらしいけど試してない。何も指定しないとRequestが指定されたことになるらしい。
最後にhttpd.conf、又はmod_jk.confを修正し、worker「wlb」でJSP/Servletの処理を行うように指定する。
JkMount /jsp-examples/* wlb JkMount /jkstatus jkstatus
起動・終了
前記の設定を終えたら両Tomcat、httpdを再起動する。
エラーが出なければひとまずOK。
確認
下記のような.jspファイルを作り、配置する。
<%@ page language="java" %> <html> <body> <h1><font color="red">Session Servered by Tomcat</font></h1> <table align="center" border="1"> <tr> <td>Session ID</td> <td><%= session.getId() %></td> </tr> <tr> <td>Created on</td> <td><%= session.getCreationTime() %></td> </tr> </table> </body> </html>
単にSession IDと作成時刻を表示するだけの簡単なJSPです。
これを
/usr/java/apache-tomcat-5.5/webapps/jsp-examples/
と
/usr/java/apache-tomcat-5.5-2/webapps/jsp-examples/
にindex2.jspという名前で作成する。
(Tomcat、httpdを再起動しなくてもアクセス可能になるはず。)
ブラウザで「http://〜/jsp-examples/index2.jsp」にアクセスする。
この時、表示されるSession IDが(SessionのTimeout以内の間隔で)ブラウザからページをリロードしても変わらなければ成功。
もしSession IDがコロコロ変わるようだったらjvmRouteとworker名の関係がずれている。
次に、ブラウザで「http://〜/jkstatus」を開いて、mod_jkの稼動状況を表示させる。
まずでっかく、
「Listing Load Balancing Worker (1 Worker)」と表示されていない時はworkers.propertiesの設定が間違っているか、workers.propertiesの修正後にhttpdを再起動していない。
次にその下の部分に、ロードバランサー配下の実WorkerのStstusが表示されている。
Balancer Members [Hide] Name Type Host Addr Act Stat D F M V Acc Err CE Wr Rd Busy Max Route RR Cd Rs [E|R] tomcat1 ajp13 localhost:8009 127.0.0.1:8009 ACT N/A 0 10 1 0 81 0 0 23K 56K 0 2 tomcat1 0 [E|R] tomcat2 ajp13 localhost:9009 127.0.0.1:9009 ACT N/A 0 10 1 0 25 0 0 7.6K 14K 0 1 tomcat2 0
ここではまず両方Workerが「ACT」であること。
これが「ERR」とかなっている場合はそちらのTomcatが起動していないか、server.xmlとworkers.propertiesのPort番号がずれていることが考えられる。
次に、先程のindex2.jspを何度か叩いてみたあとで、jkstatusの画面をリロードする。
Session IDが変わっていなくて、かつ片方の「Acc」だけが増えているのが正解。
(ちゃんとSticky Sessionが機能している。)
これが両方増えてしまう時は、誰か他にアクセスしている人がいるか、Sticky Sessionが効いていないかのどちらか。
後者の場合は
- 両server.xmlのjvmRouteがちゃんとユニークであること。
- server.xmlのjvmRouteとworkers.propertiesのworker名が同じになっていること。
- jvmRouteとTomcatのPortがちゃんと正しく(テレコになったりせずに)対応している事。
を確認する事。
ちなみに
言うまでもない事ですが、Tomcatを別々のOSインスタンスや別サーバに立ち上げ、そのサーバのIPアドレスをworkers.propertiesでlocalhostの代わりに指定すれば、(本当の意味での)ロードバランシングされたJSP/Servletエンジンになります。
関連項目
履歴
2007/01/18 -- 初版
技術的雑談へ戻る