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 の使用量は減らない?詳しい人教えてプリーズ)
まとめ
- Tomcat 7 から導入された Parallel Deployment を利用すれば、ゼロダウンタイムデプロイが実現できる
- VirtualWebappLoader を利用すれば追加のクラスパスも使うことができる
- ただし、メモリは2倍使うので十分に確保しておく必要がある