Tomcat5.5.15のクラスタ信頼性評価に関する考察
(株)日立システムアンドサービス
Tomcatクラスタに対して、「基本動作検証」および「高負荷状態検証」という2つの観点から実機評価を行った。
デフォルトの設定値では正常な処理が行われなかった検証項目も、パラメータを適切に設定することで、すべての検証項目において正常な処理が行われた。
IPA 日本OSS推進フォーラム公開のアプリケーションサーバ クラスタシステム信頼性評価のチェックリストに従い実施したTomcat5.5.15の評価結果に関する報告である。
◆ Tomcatクラスタの概要
クラスタの実現には、動的なノード発見、ノードの生存確認、レプリケーションのデータ転送などのノード間通信の実現が必要である。
Tomcatは動的なノード発見、ノードの生存確認、レプリケーションのデータ転送などのノード間通信をcatalina-clusterと呼ばれるライブラリを用いて実現している。Tomcatは、マルチキャスト通信を用いてクラスタ(メンバシップ)を形成する。
TomcatクラスタではTCPソケットを用いてレプリケーションのデータ転送を行う。
Tomcatはレプリケーション方式(ReplicationMode)として、「同期(synchronous)」、「非同期(asynchronous)」、「プール(pooled)」、「非同期キュー(fastasyncqueue)」の4つを提供している。
以下に4方式の処理の違いを示す。
(1) 同期(synchronous)
同期(synchronous)方式は、1つのスレッドでリクエスト処理・レプリケーション処理を行う。
(2) 非同期(asynchronous)
非同期(asynchronous)方式は、リクエスト処理用スレッドの他にレプリケーション処理用スレッドを生成する。両スレッドは非同期で動作する。レプリケーション情報をレプリケーション処理用スレッドのキューに格納した後、クライアントへレスポンスを返す。
(3) プール(pooled)
プール(pooled)方式は同期(synchronous)方式の一種である。リクエスト処理・レプリケーション処理に用いるスレッドを複数使用できる点が、同期(synchronous)方式と異なる。
(4) 非同期キュー(fastasyncqueue)
非同期キュー(fastasyncqueue)方式は非同期(asynchronous)方式の一種である。キュースレッドが非同期で動作する点が、非同期(asynchronous)方式と異なる。
パラメータwaitForAckの値はレプリケーション処理の動作に影響する。waitForAckの設定値がtrueのとき、他ノードへのレプリケーションデータ転送が完了するまでクライアントへレスポンスを返さない。これは1つのスレッドでリクエスト処理・レプリケーション処理を行う、同期(synchronous)方式およびプール(pooled)方式に対して有効である。
◆ チェックリストの結果
| |
チェックリスト |
waitForAck="true"
| waitForAck="false"
| コメント |
| 同期 |
非同期 |
プール |
非同期キュー |
同期 |
非同期 |
プール |
非同期キュー |
| 基本動作検証 |
レプリケーションとフェイルオーバの確認 |
○ |
- ノードの障害停止時には他のノードに処理が引き継がれることを確認した。
- 検証アプリケーションのCounter値が連番になっていることからHttpSessionオブジェクトに格納された値が正常に複製されることを確認した。
|
| 運用中のノード追加 |
○ |
- 運用中に追加したノードがクラスタに参加し、他のノードの障害停止時には処理を引き継ぐことを確認した。
- 検証アプリケーションのCounter値が障害停止前の値の連番になっていることからHttpSessionオブジェクトに格納された値が正常に複製されることを確認した。
|
| APサーバプロセス障害と復帰 |
○ |
- Tomcat再起動後、再起動したTomcatがクラスタに再び参加し、他のノードの障害停止時に処理を引き継ぐことを確認した。
- 検証アプリケーションのCounter値が障害停止前の値の連番になっていることからHttpSessionオブジェクトに格納された値が正常に複製されることを確認した。
- Apache HTTP Server(mod_jk)は、ノード障害を検知してからrecover_time経過するまでの間、障害停止したノードが復旧しているかどうかに関わらずそのノードはエラー状態であると認識する。あるノードが障害停止したとき、recover_time以内にそのノード以外のノードがすべて障害停止した場合はrecover_time経過するまでクライアントからのリクエストを処理できなくなってしまうので注意が必要である。
|
| ネットワーク障害と復帰 |
○ |
- ネットワーク障害時にフェイルオーバが機能しなかったため他のノードに切り替わらず、クライアント(Webブラウザ)ではレスポンス待ち状態が続くことを確認した。
- ネットワーク障害時にフェイルオーバが実施されなかった原因は、Apache HTTP Server(mod_jk)とTomcat間のネットワーク障害(断線)がmod_jkによって検知されなかったためである。対策として、各ノードのワーカ属性を次のように設定した。
socket_timeout:3(sec)、prepost_timeout:500(msec)、reply_timeout:10000(msec)
これらの設定値で追試した結果、ネットワーク障害時にはフェイルオーバにより約9秒で他のノードへ切り替わり、処理が継続された。ネットワーク障害修復後は分離されたノードがクラスタに復帰することを確認した。
- socket_timeoutは、Apache HTTP Server(WEBサーバ)からAPサーバへ接続要求を出した後の、APサーバからの応答待ちタイムアウト時間である。このタイムアウトがretries(デフォルト値は3)回発生すると、該当ノードはエラー状態であると認識される。socket_timeoutを設定する際はWEBサーバとAPサーバ間の通信速度およびトラフィックを考慮する必要がある。
- prepost_timeoutは、確立した接続を用いてWEBサーバからAPサーバへリクエストを送信する際、APサーバへあらかじめPINGを送信した後の応答待ちタイムアウト時間である。prepost_timeoutの設定値以内にAPサーバから応答が返ってきた場合にのみ引き続きリクエストを送信する。このタイムアウトがretries(デフォルト値は3)回発生すると、該当ノードはエラー状態であると認識される。prepost_timeoutを設定する際はWEBサーバとAPサーバ間の通信速度およびトラフィックを考慮する必要がある。
- reply_timeoutは、WEBサーバからAPサーバへリクエストを送信した後の、APサーバからのレスポンス待ちタイムアウト時間である。このタイムアウトがretries(デフォルト値は3)回発生すると、該当ノードはエラー状態であると認識される。reply_timeoutを設定する際はWEBサーバとAPサーバ間の通信速度およびトラフィックに加えて、APサーバが処理するアプリケーションの処理時間を考慮する必要がある。特にロングトランザクションが発生するようなアプリケーションに対しては、注意して設定する必要がある。
|
| 連鎖障害 |
○ |
- リクエストを処理したノードに連鎖的に障害が発生した場合でも、残りのノードで順々に遷移して処理が引き継がれることを確認した。
- 検証アプリケーションのCounter値が障害停止前の値の連番になっていることからHttpSessionオブジェクトに格納された値が正常に複製されることを確認した。
|
| 同時障害 |
○ |
- 同時障害発生時に残りの1ノードが処理を継続できることを確認した。
- 検証アプリケーションのCounter値が障害停止前の値の連番になっていることからHttpSessionオブジェクトに格納された値が正常に複製されることを確認した。
-
|
| アプリケーション処理中の障害 |
○ |
- APサーバに致命的な障害を発生させるアプリケーション処理が行われた場合、クラスタを構成するすべてのノードが順々に障害停止することを確認した。
- クラスタを構成するすべてのノードが順々に障害停止した原因は、ロードバランサのApache HTTP Server(mod_jk)が、あるノードが致命的なエラーによって障害停止した後、他のノードに切り替えて同様の処理を行わせた(リカバリ処理)ためである。
- 1つのノードの障害停止を受けたリカバリ処理による、クラスタ全体の障害停止を回避するにはApache HTTP Server(mod_jk)のワーカ属性recovery_optionsに適切な値を設定する。ワーカ属性recovery_optionsに適切な値を設定することで、1つのノードがアプリケーション処理中の致命的障害により障害停止した場合でも他のノードに処理を切り替えず、クライアントへエラーレスポンスを返すことができる。
- ワーカ属性recovery_optionsに3(mod_jkによるリカバリ処理を行わない)を設定して追試したところ、全ノードの連鎖的な障害停止は起こらず、クライアントにはエラーレスポンス(Bad Gateway, HTTPステータス502)が返った。その後、他のノードがセッション情報を引き継いで処理が継続されることを確認した。
- 今回はアプリケーション処理中の障害であったため、recovery_optionsに1(Tomcatがリクエスト受信後に障害停止してもmod_jkによるリカバリ処理を行わない)を設定して追試しても、recovery_optionsが3のときと同様の結果が得られた。
|
| アプリケーション例外 |
○ |
- アプリケーションで例外が発生した場合でもセッション情報のレプリケーションが行われた。このときレプリケーションが行われたセッション情報は、例外発生前にHttpSessionオブジェクトのsetAttribute()メソッドにより更新されたものであり、例外発生によるセッション情報の自動ロールバックは行われていなかった。
- アプリケーション例外などの障害発生時でもセッション情報の一貫性を確保するためには、アプリケーションの設計段階において例外キャッチ時のセッション情報ロールバック処理を組み込んでおく必要がある。
|
| インターコネクト障害 |
○ |
- waitForAckの値が"false"のときの同期(synchronous)、非同期(asynchronous)、プール(pooled)、非同期キュー(fastasyncqueue)、およびwairForAckの値が"true"のときの非同期(asynchronous)、非同期キュー(fastasyncqueue)の場合、レプリケーションが失敗しているにも関わらずクライアントには正常なレスポンスを返し、APサーバの障害発生時にはセッション情報の不整合を確認した。これは、リクエストを処理したノードが、セッション情報のレプリケーション先からの応答(ACK)が返ってくるかどうかに関わらずクライアントにレスポンスを返したためである。
- 一方、waitForAckの値が"true"のときの同期(synchronous)、プール(pooled)の場合、Webブラウザから最初のリクエストを送信して長時間(約5分)経過しても処理結果画面が表示されなかった。これは、リクエストを処理したノードが、セッション情報のレプリケーション先から応答(ACK)が返ってくるまではクライアントにレスポンスを返さないようになっていたためである。
- インターコネクト回線の断線のシミュレートに関して、今回採用した手法は"ifconfig"コマンドであった。インターコネクト回線上でレプリケーション通信を行うためにルーティングを設定しているが、"ifconfig"コマンドによりインタフェースをダウンさせるとそのインタフェースに関するルーティング設定が削除される。実際の断線では、ルーティング設定は何も変化しない。"ifconfig"コマンドによるインタフェースダウンの代わりにネットワークケーブルの抜去を行うことで、断線により近い状態をシミュレートして追試を行った。追試の結果、すべてのレプリケーションモードおよびwaitForAck値の組み合わせにおいてセッション情報の不整合が生じたが、クライアントにレスポンスが返された。これは期待していた結果に合致する。以上の結果から、実際にインターコネクト回線の断線が発生したときは常にセッション情報の不整合が生じ、当初期待した結果通りになることが分かった。
- インターコネクト障害が原因で起こるセッション情報不整合問題の回避策として、リクエスト用回線とレプリケーション用回線を同一回線にする方法がある。リクエスト用回線とレプリケーション用回線が同一であれば、レプリケーションを行えないノードはリクエストを受け付けないのでセッション情報の不一致は発生しない。この方法は、セッション情報のサイズが小さい場合に有効であるが、1つの回線でリクエスト・レスポンス・レプリケーションの3種類のデータ転送を行うため、回線が飽和状態にならないよう帯域の大きい回線を用いる必要がある。
- インターコネクト障害の検知には、Tomcatのクラスタメンバシップメッセージが利用できる。Tomcatがクラスタを構成するとき、各ノードはそれぞれマルチキャスト通信を行い、クラスタメンバシップを形成する。あるノードからのマルチキャストパケットが一定時間届かないとき、そのノードはクラスタメンバシップから外れたと認識され、他ノードのログに以下のメッセージが出力される。
YYYY/MM/DD HH:MM:SS org.apache.catalina.cluster.tcp.SimpleTcpCluster memberDisappeared
情報: Received member disappeared:org.apache.catalina.cluster.mcast.McastMember[tcp://xxx.xxx.xxx.xxx:4001,catalina,xxx.xxx.xxx.xxx,4001, alive=48451]
ログ監視ツールなどを用いてこのメッセージを検索することにより、インターコネクト障害を検知することができる。
|
| Apache HTTP Server(mod_jk)障害 |
○ |
- Apache HTTP Server(mod_jk)の再起動後もアプリケーションのレスポンスで返される「Counter値」が連番になっており、Apache HTTP Server(mod_jk)の障害停止・再起動がセッションの継続に影響を与えないことを確認した。
|
| DBサーバ障害 |
○ |
- DBサーバの障害復帰後も(DB接続プーリングの初期プーリング数(initialSize="5") - 1)回のアクセスにおいてDBサーバとのコネクション取得失敗を示すエラーレスポンスが返った。その後アクセスするとDBアクセス検証画面が表示され、以後のアクセスにおいても正常に動作した。
- DBサーバ再起動後もエラーレスポンスが返った原因は、DBサーバの再起動時にコネクションプールの接続がリフレッシュされずに残っていたためである。Tomcatが最初にDBサーバへアクセスしたとき、initialSizeの設定値分のコネクションを確立し、プールする。DBサーバの再起動後、プールに残っていたコネクションを利用するとエラーとなり、そのコネクションは消滅する。プールに残っていたコネクションがすべて消滅するまで、クライアントにはエラーレスポンスが返る。
- DBサーバ再起動時のように、一度TomcatとDBサーバ間のコネクションが切断されたときにコネクションプールをリフレッシュさせるには、validationQueryを使用する。validationQueryは、TomcatとDBサーバとの接続時、値として設定されたSQL文を事前に実行してDBサーバとのコネクションが有効かどうかを検証するので、DBサーバとのコネクションが切断された場合でもコネクションプールを自動でリフレッシュすることができる。server.xmlのResource設定に、次のようにvalidationQueryを設定したところ、DBサーバ再起動後にTomcatとDBサーバ間で正常に接続されることを確認した。
<Resource
:
validationQuery="select * from oss_stuff"
:
/>
|
| 高負荷時動作検証 |
連鎖障害 |
○ |
- リクエストを処理したノードに連鎖的に障害が発生した場合でも、残りのノードで順々に遷移して処理が引き継がれることを確認した。
- 検証アプリケーションのCounter値が障害停止前の値の連番になっていることからHttpSessionオブジェクトに格納された値が正常に複製されることを確認した。
|
| 同時障害 |
○ |
- 同時障害発生時に残りの1ノードが処理を継続できることを確認した。
- 検証アプリケーションのCounter値が障害停止前の値の連番になっていることからHttpSessionオブジェクトに格納された値が正常に複製されることを確認した。
|
◆ まとめ
Tomcatクラスタに対して、「基本動作検証」および「高負荷状態検証」という2つの観点から実機評価を行った。
デフォルトの設定値では正常な処理が行われなかった検証項目も、パラメータを適切に設定することで、すべての検証項目において正常な処理が行われた。
Tomcatに限らずAPサーバによるクラスタはプロダクト毎に機能拡張や改善が盛んである。従って、今回作成したチェックリストもクラスタ機能の向上を反映し継続的な更新・改良を行うことが望ましい。
◆ 関連情報
| |
|
|
|
このデータの性能データ
関連する考察データ