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であれば、アンデプロイしてしまっても問題ない。


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

画像内のコマンドは以下

$ 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 で確認)

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

2013.03.21 追記

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

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

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

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

まとめ

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