mallowlabsの備忘録 このページをアンテナに追加 RSSフィード

2013-03-15

Tomcat7 でゼロダウンタイムデプロイ

「Web アプリバージョンアップ時に Tomcat再起動してもいいのは小学生までだよねー」
ということで、Tomcat でダウンタイム無しで Web アプリバージョンアップをする方法についてまとめてみる。

Parallel Deployment

Tomcat 7 から Parallel Deployment という機能が追加され、同一 Web アプリの複数バージョンを同時にデプロイができるようになった。

war のファイル名を somewebapp##001.war 等にしておくことで、

- $CATALINA_BASE/
  - webapps/
    - somewebapp##001.war
    - somewebapp##002.war

のように配備をすると、 http://localhost:8080/somewebapp/ でアクセスした場合に、セッションが継続している場合には古い方(001)を、そうでなければ新しい方(002)を Tomcat が勝手に使ってくれる。
この機能により、バージョンアップ時には連番をインクリメントした war ファイルを置くだけで、ダウンタイム無しにバージョンアップができるというカラクリになっている。

Tomcat の Web アプリケーションマネージャを見ると、デプロイされているバージョンや、どちらのバージョンの Web アプリセッションが生きているかなどを確認することができる。
古い方の Web アプリセッション数が0であれば、アンデプロイしてしまっても問題ない。

f:id:mallowlabs:20130315153420p:image


連番は Jenkins のビルド番号をつけると便利である。
私のチームの場合は、M2 Extra Steps Plugin でハードリンクを作成している。

f:id:mallowlabs:20130315161511p:image

画像内のコマンドは以下

$ ln "${WORKSPACE}/target/somewebapp.war" "${WORKSPACE}/target/somewebapp##${BUILD_NUMBER}.war"

VirtualWebappLoader

ここまでで、ゼロダウンタイムデプロイは実現できそうなのだが、私のチームではそのままでは使えなかった。
というのも、webapps/somewebapp/WEB-INF/lib/ の下にプラグインと称して、追加の jarデプロイ後に置く運用になっていたためだ。
また、環境による細かな設定変更も webapps/somewebapp/WEB-INF/classes/ にプロパティを置くこともしている。

この運用だと、war を置くだけではデプロイが完了しない。

そこで目をつけたのが Tomcat 7 から正しく動くようになった VirtualWebappLoader というクラスローダである。

Web アプリsrc/main/webapp/META-INF/context.xml に以下のような内容を書いて置く。

<?xml version="1.0" encoding="UTF-8"?>
<Context>
  <Loader
    className="org.apache.catalina.loader.VirtualWebappLoader"
    searchExternalFirst="true"
    virtualClasspath="${catalina.base}/extensions/somewebapp/WEB-INF/classes;
                      ${catalina.base}/extensions/somewebapp/WEB-INF/lib/*.jar"
  />
</Context>

context.xml の設定により $CATALINA_BASE/extensions というディレクトリを作成し、その下に追加で読み込ませたいファイルや jar ファイルを適切に配置すれば、デプロイ時に読みこんでくれるようになる。
重要なのは searchExternalFirst 属性で、これを true にしないと extensions 以下のファイルが優先されず、意図通りに動作しない。

注意事項

ここまでで、ゼロダウンタイムが実現できるが、メモリ周りは注意が必要である。

具体的には、内部的には二個のアプリデプロイしているのと同じなので、Permanent 領域もやはり2倍使う。
例えば、2回のデプロイをした時に PermGen の使用量の遷移は以下のようになる。(Java VisualVM で確認)

f:id:mallowlabs:20130315155811p:image

そのため、Tomcat の起動オプションの -XX:MaxPermSize を十分に大きく取っておく必要がある。
(アンデプロイしても PermGen の使用量は減らない?詳しい人教えてプリーズ)

2013.03.21 追記

id:bati11 さんからコメントを頂きました。

Tomcat の起動オプションに以下のオプションを追加すると PermGen スペースも開放されるそうです。

  • -XX:+CMSPermGenSweepingEnabled
  • -XX:+CMSClassUnloadingEnabled

ただし、メモリリークの問題が無いというのが前提です。
(私のプロジェクトで試したら開放されなかった…)

まとめ

  • Tomcat 7 から導入された Parallel Deployment を利用すれば、ゼロダウンタイムデプロイが実現できる
  • VirtualWebappLoader を利用すれば追加のクラスパスも使うことができる
  • ただし、メモリは2倍使うので十分に確保しておく必要がある

bati11bati11 2013/03/18 12:58 PermGenの問題は悩ましいですよね。これのせいで結局いつか再起動が必要になってしまいますし・・・。

私の場合は以下の2つで対応できました。
(これでもアンデプロイされない時がありますが・・・。)

■GCの設定
起動オプションに以下の2つを指定します。
-XX:+CMSPermGenSweepingEnabled
-XX:+CMSClassUnloadingEnabled

■メモリリーク対応
SystemクラスローダかContextクラスローダがロードしたクラスへの参照を保持しているオブジェクトが残っているとアンデプロイされないっぽいです。
TomcatのJreMemoryLeakPreventionListenerを使用すると問題ある箇所が特定できる(ログに出力される)ので対応します。

mallowlabsmallowlabs 2013/03/21 12:55 > bati11 さん
有益な情報ありがとうございます!

自分のプロジェクトで試してみたのですが、
PermGen は減りませんでした…。
Tomcat の Find Leaks で怒られることは認識していたので
予想通りだったのですが。

とはいえ、記事の方に追記させていただきました。

スパム対策のためのダミーです。もし見えても何も入力しないでください
ゲスト


画像認証

Connection: close