Multiple Tomcat instances on Ubuntu

I’m spent about 6 hours total working on getting multiple instance of Tomcat to run on Ubuntu and finally wrote some scripts that I’d like to share with folks. These scripts do the following:

1. Setup the box to host multiple instances (JVMs) running tomcat

This process cleans the old layout and sets up a new layout that allows for multiple instances. This includes a number of “instances” directories in /var/lib/tomcat5.5, /etc/defaults/tomat5.5 (a new directory) and /var/log/tomcat5.5.

2. Create new instances

This allows you to setup a new instance quickly. It creates all the necessary directories and also installs the new instance into the init scripts an run levels.

Things the scripts don’t do:

– Setup multiple mod_jk configurations and apache virtual hosts
– Any SSL or security management

Here’s how to use the scripts:

1. First make sure you have Java and Tomcat installed!

bash$ sudo apt-get install sun-java6-jdk tomcat5.5

2. Grab ALL of the scripts and place them in some directory (anywhere is fine)

3. Run the multiple-instances.sh script as root from the directory you put it in

bash$ sudo ./multiple-instances.sh

4. Run the new-instance.sh script to create a new instance

bash$ new-instance.sh <instance-name>

5. Configure the new server by editing the files in /var/lib/tomcat5.5/instances/<instance-name> (mainly conf/server.xml)

6. Make sure each instance has a unique port for everything including the AJP, all listeners and the container

7. Fire it up!

bash$ sudo /etc/init.d/tomcat5.5_<instance-name> start

8. Check the logs in /var/log/tomcat5.5/instances/<instance-name>/catalina.out

Due to security considerations, I’ve included the scripts inline. Any comments are welcome:

multiple-instances.sh

#!/bin/bash

# Verify we are root
if [ "$USER" != "root" ]; then
  echo "You must run this script as root"
  exit 1
fi

# Shutdown any running instace
if ! /etc/init.d/tomcat5.5 stop; then
  echo "Unable to stop the running tomcat instance. Please stop it before running this script."
  exit 1
fi

# Copy over the tomcat.sh file before we change directories
if [ ! -f tomcat.sh ]; then
  echo "The tomcat.sh file is missing. It must be in the current directory so it can be placed"
  echo "into the new layout"
  exit 1
fi
cp tomcat.sh /etc/tomcat5.5
chmod go-wx /etc/tomcat5.5/tomcat.sh

# Now, head out and clean up
if ! cd /var/lib/tomcat5.5; then
  echo "You must first install tomcat from the apt repository"
  exit 1
fi

# Make the instances layout
mkdir instances
chown -R tomcat55:nogroup instances
chmod -R o-rwx instances
chmod -R g+w instances

# Clean up old layout
rm work
rm -rf webapps
rmdir temp
rmdir shared/classes
rmdir shared/lib
rmdir shared
rm logs
rm -rf conf

cd /usr/share/tomcat5.5
rm conf
rm logs
rm shared
rm temp
rm work

# Clean up defaults for the template script
cd /etc/default/
mv tomcat5.5 tomcat-bak
mkdir -p tomcat5.5/instances tomcat5.5/template
mv tomcat-bak tomcat5.5/template/tomcat5.5
chown -R root:root tomcat5.5
chmod -R go-w tomcat5.5
chmod -R g+r tomcat5.5

# Clean up the logs
cd /var/log/tomcat5.5
mkdir old
mv * old
mkdir instances
chown -R tomcat55:nogroup *
chmod -R o-rwx old instances
chmod -R g+w old instances

# Remove the old init script and turn off all script links for the run levels
rm /etc/init.d/tomcat5.5
update-rc.d -f tomcat5.5 remove

echo "Successfully setup the machine for multiple tomcat instances and cleaned up the single instance layout"

new-instance.sh

#!/bin/bash

if [ "$USER" != "root" ]; then
  echo "You must run this script as root"
  exit 1
fi

if [ $# != 1 ]; then
  echo "Usage: new-instance.sh "
  exit 1
fi

# Setup the instance
cd /var/lib/tomcat5.5/instances
if [ -d $1 ]; then
  echo "Instance $1 already created."
  exit 1
fi
mkdir $1

cd $1
mkdir conf webapps bin temp
if ! cp /etc/tomcat5.5/web.xml conf; then
  echo "Unable to create new instance $1 because /etc/tomcat5.5/web.xml doesn't appear to exist"
  exit 1
fi

if ! cp /etc/tomcat5.5/server.xml conf; then
  echo "Unable to create new instance $1 because /etc/tomcat5.5/server.xml doesn't appear to exist"
  exit 1
fi

if ! sed "s/@INSTANCE_NAME@/$1/g" /etc/tomcat5.5/tomcat.sh > bin/tomcat.sh; then
  echo "Unable to find the custom tomcat.sh file in /etc/tomcat5.5. This file must exist."
  exit 1
fi
chown -R tomcat55:nogroup /var/lib/tomcat5.5/instances/$1
chmod -R o-rwx /var/lib/tomcat5.5/instances/$1
chmod -R g+w /var/lib/tomcat5.5/instances/$1
chmod ug+rx bin/tomcat.sh

# Setup the logs
mkdir /var/log/tomcat5.5/instances/$1
chown -R tomcat55:nogroup /var/log/tomcat5.5/instances/$1
chmod -R o-rwx /var/log/tomcat5.5/instances/$1
chmod -R g+w /var/log/tomcat5.5/instances/$1
ln -s /var/log/tomcat5.5/instances/$1 logs

# Create catalina.policy (for the security manager)
echo "// This file is an example of a policy file. You can edit this file for the specific instance" > conf/catalina.policy.example
echo ""  >> conf/catalina.policy.example
cat /etc/tomcat5.5/policy.d/*.policy >> conf/catalina.policy.example
chown tomcat55:nogroup conf/catalina.policy.example
chmod o-rwx conf/catalina.policy.example
chmod g+w conf/catalina.policy.example

# Setup auto start
ln -s /var/lib/tomcat5.5/instances/$1/bin/tomcat.sh /etc/init.d/tomcat5.5_$1
update-rc.d tomcat5.5_$1 defaults 90

tomcat.sh

#!/bin/sh
#
# /etc/init.d/tomcat5.5 -- startup script for the Tomcat 5 servlet engine
#
# Written by Miquel van Smoorenburg .
# Modified for Debian GNU/Linux	by Ian Murdock .
# Modified for Tomcat by Stefan Gybas .
#
### BEGIN INIT INFO
# Provides:          tomcat
# Required-Start:    $local_fs $remote_fs $network
# Required-Stop:     $local_fs $remote_fs $network
# Should-Start:      $named
# Should-Stop:       $named
# Default-Start:     2 3 4 5
# Default-Stop:      0 1 6
# Short-Description: Start Tomcat.
# Description:       Start the Tomcat servlet engine.
### END INIT INFO

set -e

PATH=/bin:/usr/bin:/sbin:/usr/sbin
NAME=tomcat5.5
DESC="Tomcat servlet engine"
DAEMON=/usr/bin/jsvc
CATALINA_HOME=/usr/share/$NAME
INSTANCE_NAME=@INSTANCE_NAME@
DEFAULT=/etc/default/$NAME/instances/$INSTANCE_NAME

. /lib/lsb/init-functions
. /etc/default/rcS

# The following variables can be overwritten in $DEFAULT

# Run Tomcat 5 as this user ID
TOMCAT5_USER=tomcat55

# The first existing directory is used for JAVA_HOME (if JAVA_HOME is not
# defined in $DEFAULT)
JDK_DIRS="/usr/lib/jvm/java-6-sun /usr/lib/jvm/java-1.5.0-sun /usr/lib/j2sdk1.4-sun /usr/lib/j2sdk1.4-blackdown /usr/lib/j2se/1.4 /usr/lib/j2sdk1.5-sun /usr/lib/j2sdk1.3-sun /usr/lib/j2sdk1.3-blackdown /usr/lib/j2sdk1.5-ibm /usr/lib/j2sdk1.4-ibm /usr/lib/jvm/java-gcj /usr/lib/kaffe"

# Directory for per-instance configuration files and webapps
CATALINA_BASE=/var/lib/tomcat5.5/instances/$INSTANCE_NAME

# Use the Java security manager? (yes/no)
TOMCAT5_SECURITY=no

# Timeout in seconds for the shutdown of all webapps
TOMCAT5_SHUTDOWN=30

# End of variables that can be overwritten in $DEFAULT

# overwrite settings from default file
if [ -f "$DEFAULT" ]; then
	. "$DEFAULT"
fi

test -f $DAEMON || exit 0

[ -z "$TOMCAT5_USER" ] && TOMCAT5_USER=tomcat55

# Look for the right JVM to use
for jdir in $JDK_DIRS; do
	if [ -r "$jdir/bin/java" -a -z "${JAVA_HOME}" ]; then
		JAVA_HOME_TMP="$jdir"
		# checks for a real JDK like environment, needed to check if
		# really the java-gcj-compat-dev package is installed
		if [ -r "$jdir/bin/jdb" ]; then
			JAVA_HOME="$JAVA_HOME_TMP"
		fi
	fi
done
export JAVA_HOME

# Set java.awt.headless=true if JAVA_OPTS is not set so the
# Xalan XSL transformer can work without X11 display on JDK 1.4+
# It also looks like the default heap size of 64M is not enough for most cases
# se the maximum heap size is set to 128M
if [ -z "$JAVA_OPTS" ]; then
	JAVA_OPTS="-Djava.awt.headless=true -Xmx128M"
fi

JAVA_OPTS="$JAVA_OPTS -Djava.endorsed.dirs=$CATALINA_HOME/common/endorsed -Dcatalina.base=$CATALINA_BASE -Dcatalina.home=$CATALINA_HOME -Djava.io.tmpdir=$CATALINA_BASE/temp"

# Set the JSP compiler if set in the tomcat5.5.default file
if [ -n "$JSP_COMPILER" ]; then
	JAVA_OPTS="$JAVA_OPTS -Dbuild.compiler=$JSP_COMPILER"
fi

if [ "$TOMCAT5_SECURITY" = "yes" ]; then
	JAVA_OPTS="$JAVA_OPTS -Djava.security.manager -Djava.security.policy=$CATALINA_BASE/conf/catalina.policy"
fi

# juli LogManager disabled if running under libgcj (see bug #395167)
gcj=no
"$JAVA_HOME/bin/java" -version 2>&1 | grep -q "^gij (GNU libgcj)" && gcj=yes
if [ "$gcj" != "yes" ]; then
  JAVA_OPTS="$JAVA_OPTS -Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager -Djava.util.logging.config.file=$CATALINA_BASE/conf/logging.properties"
fi

# Define other required variables
CATALINA_PID="/var/run/$NAME-$INSTANCE_NAME.pid"
LOGFILE="$CATALINA_BASE/logs/catalina.out"
BOOTSTRAP_CLASS=org.apache.catalina.startup.Bootstrap
JSVC_CLASSPATH="/usr/share/java/commons-daemon.jar:$CATALINA_HOME/bin/bootstrap.jar"

# Look for Java Secure Sockets Extension (JSSE) JARs
if [ -z "${JSSE_HOME}" -a -r "${JAVA_HOME}/jre/lib/jsse.jar" ]; then
    JSSE_HOME="${JAVA_HOME}/jre/"
fi
export JSSE_HOME

case "$1" in
  start)
	if [ -z "$JAVA_HOME" ]; then
		log_failure_msg "no JDK found - please set JAVA_HOME"
		exit 1
	fi

	if [ ! -d "$CATALINA_BASE/conf" ]; then
		log_failure_msg "invalid CATALINA_BASE specified"
		exit 1
	fi

	log_daemon_msg "Starting $DESC" "$NAME"
	if start-stop-daemon --test --start --pidfile "$CATALINA_PID" 
		--user $TOMCAT5_USER --startas "$JAVA_HOME/bin/java" 
		>/dev/null; then

		# Clean up and set permissions on required files
		rm -rf "$CATALINA_BASE"/temp/*
		chown --dereference "$TOMCAT5_USER" "$CATALINA_BASE/conf" 
			"$CATALINA_BASE/conf/tomcat-users.xml" 
			"$CATALINA_BASE/logs" "$CATALINA_BASE/temp" 
			"$CATALINA_BASE/webapps" "$CATALINA_BASE/work" 
			"$CATALINA_BASE/logs/catalina.out" || true

		$DAEMON -user "$TOMCAT5_USER" -cp "$JSVC_CLASSPATH" 
		    -outfile "$LOGFILE"  -errfile '&1' 
		    -pidfile "$CATALINA_PID" $JAVA_OPTS "$BOOTSTRAP_CLASS"
	else
	        log_progress_msg "(already running)"
	fi
	log_end_msg 0
	;;
  stop)
	log_daemon_msg "Stopping $DESC" "$NAME"
        if start-stop-daemon --test --start --pidfile "$CATALINA_PID" 
		--user "$TOMCAT5_USER" --startas "$JAVA_HOME/bin/java" 
		>/dev/null; then
		log_progress_msg "(not running)"
	else
		$DAEMON -cp "$JSVC_CLASSPATH" -pidfile "$CATALINA_PID" 
		     -stop "$BOOTSTRAP_CLASS"
	fi
	log_end_msg 0
	;;
   status)
        if start-stop-daemon --test --start --pidfile "$CATALINA_PID" 
		--user $TOMCAT5_USER --startas "$JAVA_HOME/bin/java" 
		>/dev/null; then

		if [ -f "$CATALINA_PID" ]; then
		    log_success_msg "$DESC is not running, but pid file exists."
		    exit 1
		else
		    log_success_msg "$DESC is not running."
		    exit 3
		fi
	else
		log_success_msg "$DESC is running with pid `cat $CATALINA_PID`"
		exit 0
	fi
        ;;
  restart|force-reload)
        if start-stop-daemon --test --stop --pidfile "$CATALINA_PID" 
		--user $TOMCAT5_USER --startas "$JAVA_HOME/bin/java" 
		>/dev/null; then
		$0 stop
		sleep 1
	fi
	$0 start
	;;
  try-restart)
        if start-stop-daemon --test --start --pidfile "$CATALINA_PID" 
		--user $TOMCAT5_USER --startas "$JAVA_HOME/bin/java" 
		>/dev/null; then
		$0 start
	fi
        ;;
  *)
	log_success_msg "Usage: $0 {start|stop|restart|try-restart|force-reload|status}"
	exit 1
	;;
esac

exit 0

17 thoughts on “Multiple Tomcat instances on Ubuntu

  1. Great scripts, thanks. Found one small error in multiple-instances.sh:

    # Clean up defaults for the template script
    cd /etc/defaults/

    Should be /etc/default.

    . m

    Like

  2. These are great. I would have had to spend more than 6 hours creating the same thing.

    I only had issues with copying from the web. Maybe its just me but I get bad “, ‘, and ` characters. I also get line breaks so a continuation character ( ) would be useful. Downloadable scripts could fix these issues but I’m not concerned with the security issues.

    Like

  3. Dear Sir,

    That’s good you have access two instances on same machine of tomcat, but you access them on Linux or I think SUSE, but I have problem to access two liferay tomcat on same machine, I have changed port & java_home in server.xml & catalina.bat. Should I need to change another files too for it.

    Like

  4. Nice scripts, thanks 🙂
    Could you provide a download link though? As mentioned your blog is adding bad characters and stripping the line continuation characters.

    Like

  5. Jack,

    I’d recommend checking out the latest work I’ve done on Tomcat on Linux. I posted a DEB for running multiple instances of Tomcat 6 on Linux and it completely replaces the standard Tomcat. It also cleans up many of the messes that Debian has created with their package and is a 100% valid Tomcat installation.

    Here’s the post from the Ubuntu Forums on it:

    http://ubuntuforums.org/showthread.php?t=1211517

    I’ll put up another blog post here about it as well.

    Like

  6. Hi Brian,

    Your scripts are using the older JK workers style to connect apache to tomcat. With Apache 2.2, you can also use the (easier) mod_proxy_ajp.
    Is it a good suggestion if you update your scripts to this?

    Marcel

    Like

  7. Hi Brain,

    I can see if i can help you with that :)… though my DEB knowledge is newby!

    Some ideas:
    – would be great to have the possibility to turn a manual instance into a init.d’ed instance and vice versa
    – how about a t6eninstance and t6disinstance command (like a2ensite and a2dissite) which places symlinks from instances-available to a instances-enabled folder. That way, init.d/tomcat6instances can take one argument (stop/start/restart) and does that on all instances.. or takes two arguments, where 2nd is the instance name from the dir “instances-enabled”

    Like

  8. Perhaps. The main difference here is that Apache is a single process where the way I have Tomcat setup is that each instance is a separate process. This was intentional in order to prevent one webapp from crashing others.

    Any solution would need the ability to control single instances in cases of failures where a single instance needs to be restarted.

    Like

  9. Nice work!!!
    And this makes me think!!!
    What if we want to run two different versions of tomcat on the same server, say 5x and 6x.
    How could we manage that in case we have two different applications that one can run only with 5x and the other only with 6x?

    Like

  10. It’s the best time to make a few plans for the long run and it is time to be happy. I have read this submit and if I could I desire to suggest you some attention-grabbing issues or advice. Perhaps you can write subsequent articles relating to this article. I desire to read more issues approximately it!

    Like

  11. Pretty section of content. I simply stumbled upon your website and in accession capital to say that I acquire in fact enjoyed account your weblog posts. Any way I will be subscribing in your feeds or even I achievement you access constantly fast.

    Like

Leave a comment