CentOS 7のtomcatパッケージでcatalina.base分離

 article  Comments Off on CentOS 7のtomcatパッケージでcatalina.base分離
Oct 302016
 

以前「CATALINA_BASEを分けてTomcatを複数サービス化」のような記事を書いたのですが、CentOS 7やRed Hat 7のtomcatパッケージでこちらを実装してみます。

CentOS 7/Red Hat 7のtomcatパッケージには2種類のsystemd.execファイルが用意されています。

[root@localhost ~]# ls /usr/lib/systemd/system/tomcat*
/usr/lib/systemd/system/tomcat.service
/usr/lib/systemd/system/tomcat@.service
[root@localhost ~]#

tomcat@.serviceの先頭に以下の記述があり、こちらの手順を模倣するとcatalina.baseの分離ができそうなので早速やってみます。

# Systemd unit file for tomcat instances.
#
# To create clones of this service:
# 0. systemctl enable tomcat@name.service
# 1. create catalina.base directory structure in
# /var/lib/tomcats/name
# 2. profit.

まずはnameにinst1を指定してenableにしてみます。

[root@localhost ~]# systemctl enable tomcat@inst1.service
Created symlink from /etc/systemd/system/multi-user.target.wants/tomcat@inst1.service to /usr/lib/systemd/system/tomcat@.service.
[root@localhost ~]#

次に/var/lib/tomcats以下にcatalina.baseディレクトリを用意してパッケージデフォルトの内容をコピーします。

[root@localhost ~]# mkdir /var/lib/tomcats/inst1 && cd /var/lib/tomcats/inst1
[root@localhost inst1]# cp -pr /etc/tomcat conf
[root@localhost inst1]# cp -pr /var/log/tomcat logs
[root@localhost inst1]# cp -pr /var/cache/tomcat/temp .
[root@localhost inst1]# cp -pr /var/cache/tomcat/work .
[root@localhost inst1]# cp -pr /var/lib/tomcat/webapps .

webapps以下の不要なアプリケーションは削除します。

catalina.baseを指定する設定ファイルは/etc/sysconfig/tomcat@inst1として用意します。
これは/etc/tomcat/tomcat.confの先頭に説明があります。

# System-wide configuration file for tomcat services
# This will be loaded by systemd as an environment file,
# so please keep the syntax.
#
# There are 2 “classes” of startup behavior in this package.
# The old one, the default service named tomcat.service.
# The new named instances are called tomcat@instance.service.
#
# Use this file to change default values for all services.
# Change the service specific ones to affect only one service.
# For tomcat.service it’s /etc/sysconfig/tomcat, for
# tomcat@instance it’s /etc/sysconfig/tomcat@instance.

ファイルを作成してCATALINA_BASEとCATALINA_TMPDIRを設定します。

[root@localhost ~]# cp -p /etc/sysconfig/tomcat /etc/sysconfig/tomcat\@inst1
[root@localhost ~]# echo "CATALINA_BASE=\"/var/lib/tomcats/inst1\"" >> /etc/sysconfig/tomcat\@inst1
[root@localhost ~]# echo "CATALINA_TMPDIR=\"/var/lib/tomcats/inst1/temp\"" >> /etc/sysconfig/tomcat\@inst1

サービスを起動してみます。

[root@localhost ~]# systemctl start tomcat@inst1

catalina.baseやjava.io.tmpdirが分離された状態で起動されていることが確認できます。

/usr/lib/jvm/jre/bin/java -classpath /usr/share/tomcat/bin/bootstrap.jar:/usr/share/tomcat/bin/tomcat-juli.jar:/usr/share/java/commons-daemon.jar -Dcatalina.base=/var/lib/tomcats/inst1 -Dcatalina.home=/usr/share/tomcat -Djava.endorsed.dirs= -Djava.io.tmpdir=/var/lib/tomcats/inst1/temp -Djava.util.logging.config.file=/var/lib/tomcats/inst1/conf/logging.properties -Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager org.apache.catalina.startup.Bootstrap start

利用するポート番号変更などインスタンス個別の設定は、/var/lib/tomcats/inst1/conf以下を編集することで対応できます。

Tomcat起動スクリプト

 article  Comments Off on Tomcat起動スクリプト
Feb 182013
 

Apache Tomcat 7 (7.0.37) – Tomcat SetupのUnix daemonによりますと、Tomcarデーモンの起動はcommons-daemonプロジェクトのjsvcツールを使うのがよしとされています。
あわせて$CATALINA_HOME/bin/daemon.shが起動用rcスクリプトのテンプレートとして使えるとの情報があるのですが、コピーして加工するのも面倒なので、daemon.shを呼び出す形式での起動用rcスクリプトを用意してみました。CentOS 6で動作することを確認しています。

#!/bin/sh
#
# tomcat       Startup script for the Apache Tomcat Servlet/JSP container.
#
# chkconfig: - 80 10
# description: The Apache Tomcat Servlet/JSP container.
#

# Source function library.
. /etc/init.d/functions

RETVAL=0

JAVA_HOME=/opt/java
CATALINA_HOME=/opt/tomcat
DAEMON=$CATALINA_HOME/bin/daemon.sh
LOCKFILE=/var/lock/subsys/tomcat

start() {
        echo -n $"Starting tomcat: "
        $DAEMON --java-home $JAVA_HOME --catalina-home $CATALINA_HOME start
        RETVAL=$?
        if [ $RETVAL -eq 0 ]; then
                echo_success
                touch $LOCKFILE
        else
                echo failure
        fi
        echo
        return $RETVAL
}

stop() {
        echo -n $"Shutting down tomcat: "
        $DAEMON --java-home $JAVA_HOME --catalina-home $CATALINA_HOME stop
        RETVAL=$?
        if [ $RETVAL -eq 0 ]; then
                echo_success
                rm -f $LOCKFILE
        else
                echo failure
        fi
        echo
        return $RETVAL
}

case "$1" in
  start)
        start
        ;;
  stop)
        stop
        ;;
  restart)
        stop
        start
        ;;
  status)
        status $CATALINA_HOME/bin/jsvc
        ;;
  *)
        echo $"Usage: $0 {start|stop|restart|status}"
        exit 3
esac

exit $?

daemon.shの引数で起動ユーザーを指定することもできますが、デフォルトtomcatなので未指定にしています。
変更したい場合は–tomcat-userをdaemon.sh呼び出し時の引数に追加してみてください。他にdaemon.sh呼び出し時に指定できる引数は–catalina-baseと–catalina-pidがあります。

daemon.shを追いかけてみるとわかるのですが、catalina.shを使うときと同様にsetenv.shが呼び出されるので、CATALINA_OPTSやLANG等、他に必要な変数をそちらに記述していくことができるので便利かと思っています。

mod_jkでのsticky_session

 article  Comments Off on mod_jkでのsticky_session
Nov 042008
 

mod_jkでlbワーカを使う場合、セッションによって振り分け先を固定化するためにsticky_sessionという属性が用意されている(デフォルトtrue)。実際のところ、どういう動きになるのかソースを追ってみた。

sticky_sessionパラメータを参照しているのは、jk/native/common/jk_lb_worker.cのJK_METHOD service()部分のここらへん。

    if (p->worker->sticky_session) {
        /* Use sessionid only if sticky_session is
         * defined for this load balancer
         */
        sessionid = get_sessionid(s, p->worker, l);
    }

get_sessionid()もjk_lb_worker.cで実装されていて、以下のようになっている。

    val = get_path_param(s, p->session_path);
    if (!val) {
        val = get_cookie(s, p->session_cookie);
    }

URLパスからsession_path入手を試み、ダメならsession_cookieから値を設定している。
この時点でURLパスに含まれるsession_path値のほうがsession_cookieよりも優先されることが判明。

ちなみにget_path_param()もget_cookie()もjk_lb_worker.cで実装されていて、get_path_param()のほうはURLパスからp->session_pathで始まる値の”=”以降、終端(?か;か\0)までを返すようになっている。get_cookie()も似たような処理なので割愛。

次にget_sessionid()で入手したsessionidは

rec = get_most_suitable_worker(s, p->worker, sessionid, p->states, l);

で一致するsessionidを探し、なければfind_best_worker()でワーカを決定するようになっている。

なんとなく動きはわかったものの、JSESSIONIDという文字列がどこにも出現しないので、今度はそちらを追いかけてみることに。
JSESSIONIDはjk/native/common/jk_global.hの中でJK_SESSION_IDENTIFIERとして定義されている。

JK_SESSION_IDENTIFIERを参照してるのはjk_lb_worker.cのjk_get_lb_session_cookie()呼び出し部分。

    strncpy(p->session_cookie,
            jk_get_lb_session_cookie(props, p->name, JK_SESSION_IDENTIFIER),
            JK_SHM_STR_SIZ);
    strncpy(p->session_path,
            jk_get_lb_session_path(props, p->name, JK_PATH_SESSION_IDENTIFIER),
            JK_SHM_STR_SIZ);

jk_get_lb_session_cookie()はjk/native/common/jk_util.cで定義されていて、JK_SESSION_IDENTIFIERはjk_map_get_string()にそのまま渡している。

jk_map_get_string()は、結局MAKE_WORKER_PARAM(SESSION_COOKIE_OF_WORKER)マクロでworker.ワーカ名.session_cookieの設定値(デフォルト”JSESSIONID”)を返すようになっていた。

jk_get_lb_session_path()もほぼ同じでMAKE_WORKER_PARAM(SESSION_PATH_OF_WORKER)によりworker.ワーカ名.session_path設定値(デフォルト”;jsessionid”)を返す。

結局のところ、こちらは両方ともworker.propertiesでの設定値かデフォルト値かを設定しているだけ。

ここで設定されたキー(p->session_path=”;jsessionid”かp->session_cookie=”JSESSIONID”)をベースに最初に出てきたget_most_suitable_worker()で振り分け先を決定している、ということになる。

CATALINA_BASEを分けてTomcatを複数サービス化

 article  Comments Off on CATALINA_BASEを分けてTomcatを複数サービス化
Sep 202008
 

Tomcatのバイナリ1個で、複数のインスタンスをサービス登録してみました。
インスタンス別にCATALINA_BASE配下ディレクトリ(conf,logs,temp,webapps,work)を分けて、Tomcatが使うポート番号も変えてあげれば、バイナリは共通のまま複数インスタンス化することができます。

設定項目 デフォルト インスタンス1 インスタンス2
サービス名 Tomcat6 Tomcat6Instance1 Tomcat6Instance2
JAVA_HOME C:\Program Files\Java\jdk1.6.0_07
CATALINA_HOME C:\apache\tomcat-6.0.18
CATALINA_BASE C:\apache\tomcat-6.0.18 C:\apache\tomcat-instance1 C:\apache\tomcat-instance2
Server Componentの
port番号
8005 18005 28005
HTTP Connectorの
port番号
8080 18080 28080
AJP Connectorの
port番号
8009 18009 28009

まずは以下の手順で各種ディレクトリと設定ファイルを準備。

  • apache-tomcat-6.0.18.zipを入手し、C:\apache\tomcat-6.0.18に展開
  • C:\apache\tomcat-instance1を作成し、C:\apache\tomcat-6.0.18からconf,logs,temp,webapps,workディレクトリをコピー
  • C:\apache\tomcat-instance1\conf\server.xmlのポート番号を変更
  • C:\apache\tomcat-instance2も同様に作成

デイレクトリと設定ファイルが準備できたら、service.batコマンドで登録。

C:\apache\tomcat-6.0.18\bin>set JAVA_HOME="C:\Program Files\Java\jdk1.6.0_07"
C:\apache\tomcat-6.0.18\bin>set CATALINA_BASE=C:\apache\tomcat-instance1
C:\apache\tomcat-6.0.18\bin>service.bat install Tomcat6Instance1
Installing the service 'Tomcat6Instance1' ...
Using CATALINA_HOME:    C:\apache\tomcat-6.0.18
Using CATALINA_BASE:    C:\apache\tomcat-instance1
Using JAVA_HOME:        "C:\Program Files\Java\jdk1.6.0_07"
Using JVM:              auto
The service 'Tomcat6Instance1' has been installed.

CATALINA_BASE環境変数を変えて、2個めのインスタンスも同様に登録します。

C:\apache\tomcat-6.0.18\bin>set CATALINA_BASE=C:\apache\tomcat-instance2
C:\apache\tomcat-6.0.18\bin>service.bat install Tomcat6Instance2
Installing the service 'Tomcat6Instance2' ...
Using CATALINA_HOME:    C:\apache\tomcat-6.0.18
Using CATALINA_BASE:    C:\apache\tomcat-Instance2
Using JAVA_HOME:        "C:\Program Files\Java\jdk1.6.0_07"
Using JVM:              auto
The service 'Tomcat6Instance2' has been installed.

今回は簡単に済ませるためにservice.batコマンドを使いましたが、tomcat6w.exeを使うともっと細かいパラメータを指定して登録することができます。登録したサービスの設定変更をおこなうことも可能です。
詳しくはApache Tomcat 6.0 – Windows service HOW-TOをご覧ください。

Nov 182007
 

Session Persistenceといっても、LoadBalancerなどで設定するCookie Persistenceのハナシではありません。

Tomcatは停止時にセッション状態をファイルに保存して、起動時に読み込んでセッションを永続化するようになってます(Session Persistence)。

場合によっては便利ですが、セッションに積むインスタンスがシリアライズに対応していないとExceptionが発生したり、作りによっては都合がよくありません。

Session PersistenceはManager Componentでおこなってますので、こちらの設定で無効化することができるようになってます。
Apache Tomcat Configuration Reference – The Manager Component

手っ取り早くTomcat全体で無効化してしまうには、$CATALINA_HOME/conf/context.xmlに

<Manager pathname="" />

と書かれた部分がありますのでこちらを有効にします。デフォルトではSESSIONS.serというファイル名なのですが、それをnullにしているので保存しないようになります。

Manager Conponentでは、このほかにセッションIDの長さ(sessionIdLength)や、最大セッション数(maxActiveSessions)などを設定することができるようになってます。

なお、Managerの設定はContext単位になりますので、バーチャルホストやアプリケーション毎に設定することも可能です。
詳しくはApache Tomcat Configuration Reference – The Context Containerを。

ちなみにTomcat 4の頃からずばりsaveOnRestart="false"と設定できるPersistent Managerがあったのですが、当時はExperimentalと注意書きされていました。Tomcat 6になってもExperimentalのままなので開発止まっちゃったんですかね 😕

Oct 122007
 

Tomcat起動時のJava引数でJMXエージェントを有効化するオプションを追加してやればサーバ側はOK。
たとえばbin/setenv.shでCATALINA_OPTSに追加してやる。

CATALINA_OPTS="$CATALINA_OPTS \
-Dcom.sun.management.jmxremote \
-Dcom.sun.management.jmxremote.port=8686 \
-Dcom.sun.management.jmxremote.ssl=false \
-Dcom.sun.management.jmxremote.authenticate=false \
"

あとはJConsoleを実行する側でサーバ名とポート番号(上の例では8686)を指定して接続すればよい。
ちなみに上の指定はSSLも認証もなしの状態なので、クローズドなN/W経由で利用しないと危険。

またJMXエージェントとのやりとりはRMIでおこなわれるので、jmxremote.portで指定した番号以外のポートも使われる。
Firewallを経由する場合は注意が必要。

JMX を使用するプラットフォームの監視と管理

Sep 292007
 

今頃気がついたのですが、Tomcat 5.5以降はJSPコンパイラにEclipse JDTを使うようになったそうです。つまりjavacが不要になったのでJDKではなくJREでも動かすことができると。
ただJREに含まれるのはHotSpot Client VMだけなので、HotSpot Server VM使いたければJDKが必要になります。

Tomcat 5.0まではコンパイラの変更もweb.xml設定でOKだったのですが、それが少しややこしくなったようです。
以下のスレッドでコンパイラをjikesに変更する方法が話題になってます。

JSPが出回り始めた頃は「なぜ本番サーバに開発キット(JDK=Java Development Kit)を入れなきゃいけないの?」と思ってたのですが、やっとRuntimeだけでもいけるようになったということですね。

Jul 262007
 

アプリケーションの実装としてではなく、Tomcatレベルで変更する方法です。
@ITのフォーラムには以下のQAがあります。
JSESSIONIDを保持したCookieをsecure属性にする方法 – Java Solution
こちらによれば、Tomcatは「セキュアな通信の場合CookieにSecureを付与してくれる」ことになります。

ところがApacheやTomcatでSSLしてる場合はよいのですが、SSLアクセラレータやロードバランサ、stunnelなどでSSLを解除しているとsecureと認識されなくなってしまい、Secure属性が付与されなくなってしまいます。

で、TomcatのAJP13コネクタ定義にはsecureという属性があり、こちらをtrueに設定するとresponse.isSecure()にtrueを返してくれるようになります。

Apache Tomcat Configuration Reference – The AJP Connectorより
Attribute Description
secure Set this attribute to true if you wish to have calls to request.isSecure() to return true for requests received by this Connector (you would want this on an SSL Connector). The default value is false.

ですのでserver.xmlのコネクタ定義で以下の設定をおこなえば、毎回cookie.setSecure(true)が実行されて、CookieにSecureが付与されるようになります。

<Connector port="8009" secure="true"
           enableLookups="false" redirectPort="8443" protocol="AJP/1.3" />
Jul 102007
 

厳密な方法ではありません。禁止ではなく防止できるかも、というレベルです。
rcスクリプト等でtomcatの起動ユーザをroot以外にしていたとします。

case "$1" in
  start)
    su - tomcat -c "$CATALINA_HOME/bin/startup.sh"
    ;;
  stop)
    su - tomcat -c "$CATALINA_HOME/bin/shutdown.sh"
    ;;

ですが、$CATALINA_HOME/bin/startup.shを直接実行されるとtomcat以外のユーザで起動されてしまう可能性があります。そこでsetenv.shを使い、tomcatユーザ以外で実行した場合にメッセージを出して終了するようにしてみました。

if [ "`/usr/bin/whoami`" != "tomcat" ]; then
  echo
  echo "tomcatユーザ以外では起動しないでください。"
  echo "/etc/rc.d/init.d/tomcat {start|stop}を使ってね。"
  echo
  exit
fi
:

こうしておけば、startup.shレベルであれば起動ユーザをチェックできると思います。

Jul 062007
 

Tomcat 5.5における404 Not Foundなどのデフォルトエラーページ出力は、org.apache.catalina.valves.ErrorReportValveクラスで実装されている。
このクラスはHostコンテナのerrorReportValveClass属性で変更可能なので、errorReportValveClass=””としてしまえば、Tomcat標準のエラーページは出力されず、HTTPステータスコードだけが返るようにできるようだ。
Apache Tomcat Configuration Reference – The Host Container

このクラスのソースを見ると出力メッセージはLocalStrings.propertiesで定義されている文字列が出力されていることがわかる。

まぁ、このクラスやプロパティファイルを置き換えるよりweb.xmlでerror-page定義したほうが楽ですけど。

ApacheがTomcatからのHTTPステータスコードにしたがってエラーページを返すようになってくれれば、Apache側で定義したErrorDocumentに統一できて楽なのではないかと思う今日この頃。