- Overview
- Module Description - What the module does and why it is useful
- Setup - The basics of getting started with tomcat
- Usage - Configuration options and additional functionality
- Reference - An under-the-hood peek at what the module is doing and how
- Limitations - OS compatibility, etc.
- Development - Guide for contributing to the module
Support for running multiple intances of apache tomcat.
Currently supports RedHat boxes and Apache Tomcat 7.x or 8.x
Module features:
- Multiple Tomcat instances
- Choose the user to run each Tomcat instance as
- Installation/removal of endorsed Java libraries (eg XML parsers)
- Installation/removal of shared Java libraries (eg database drivers)
- JNDI/JDBC support (edit server.xml and context.xml)
- Choose the port(s) tomcat runs on
- HTTPs connector support (won't work out of the box as you must setup keystore and tell tomcat about it)
- Choose your own file permissions
- Supply and use your own templates for server.xml, setenv.sh, etc
- Support controlling auto deployment/unpacking of .war files
- Side-by-side installation of multiple apache-tomcat package (assuming you have access to a correctly packaged version)
- Choose to run a specific Java version for each Tomcat instance
- Choose to run a specific Tomcat version for each Tomcat instance
- Remove specified versions of tomcat from the system
- System init script for each tomcat instance
- Tomcats that don't shut down correctly after 60 seconds will be killed
- Restart Tomcat automatically after altering shared/endorsed libraries, Java or Tomcat
- JMX support
- AJP connector support
Packages:
- libxml2 for XML configuration file validation
- Tomcat package(s) as instructed by user
Services:
- Installs one service per tomcat instance as instructed by user
Files:
- Tomcat instances under
/var/tomcat
(configurable) - Shared Java libraries under
/usr/local/lib/tomcat_shared
(configurable) - Endorsed Java libraries under
/usr/local/lib/tomcat_endorsed
(configurable) - System init script(s) at
/etc/init.d/tomcat_*
- Symlink to the default tomcat at
/usr/local/apache-tomcat
(configurable)
This module requires the future parser
You should have access to a yum repository or HTTP server hosting the apache-tomcat packages you wish to install. A sample .spec file you can use to build an RPM file is available here
Your system must have Java installed. Puppet Labs supports a Java module or if you need to have side-by-side Java installations from RPMs this alternate (unsupported) Java module can install it.
You must create the user(s) to run any required tomcat instances yourself. By
default, this module expects a user called tomcat
which you could create like
this:
user { "tomcat":
ensure => present,
home => "/var/tomcat",
gid => "tomcat",
}
group { "tomcat":
ensure => present,
}
If wanting to use this module to install shared/endorsed libraries, you must put the .jar files somewhere Puppet can download them from. An HTTP server is ideal for this purpose.
The code snippits below will give you a functional tomcat instance. Don't forget to have the Setup Requirements in place first
class { "::tomcat":}
Setup directories for shared and endorsed libraries, load variables used by other manifests into memory
::tomcat::install { "myvendor-apache-tomcat-7.0.56":
symlink_target => "/usr/local/apache-tomcat-7.0.56",
}
Use yum to install a copy of "myvendor-apache-tomcat-7.0.56" and create a
symlink from /usr/local/apache-tomcat
to /usr/local/apache-tomcat-7.0.56
The file at /usr/local/apache-tomcat-7.0.56
is created by the installation of
the RPM package.
::tomcat::install { "myvendor-apache-tomcat-7.0.56-1-1.x86_64.rpm":
download_site => "http://myrpms.myvendor.com",
}
Download the file myvendor-apache-tomcat-7.0.56-1-1.x86_64.rpm
from
http://myrpms.myvendor.com
and install it.
::tomcat::instance { "main":
http_port => 8080,
shutdown_port => 8009,
}
Download the file apache-tomcat-8.0.24.tar.gz
from http://mirror.ventraip.net.au/apache/tomcat/tomcat-8/v8.0.24/bin
and install it to /usr/local/apache-tomcat-8.0.24
. After unpacking, symlink /usr/local/apache-tomcat
back to this directory.
::tomcat::install { "apache-tomcat-8.0.24.tar.gz":
ensure => present,
symlink_target => "/usr/local/apache-tomcat-8.0.24",
download_site => "http://mirror.ventraip.net.au/apache/tomcat/tomcat-8/v8.0.24/bin",
}
Create a tomcat instance called main
running on port 8080 under the tomcat
user.
The above setup will:
- Run as the user
tomcat
- Be controlled by an init script at
/etc/init.d/tomcat-main
- Run the Tomcat HTTP connector on port 8080
- Shutdown via port 8009
- Store all instance files under /var/tomcat/main (name taken from resource
title) with the sub-directories:
- bin - shell script(s) to run when starting/stopping the instance
- conf - configuration files
- lib - instance wide .class and .jar files to load
- logs - all logs for this instance will be created here
- run - contains the PID (process ID) of this tomcat instance
- temp - temp data
- webapps - deployment directory (automatic by default)
- work - tomcat persistent state
- Use the JDK provided at
/usr/java/default
- Use the apache tomcat installation at
/usr/local/apache-tomcat
You can have as many Tomcat instances as you like running on each node simultaneously, you just have to remember to choose different ports to run them on. If you mess up the port assignments by allocating the same port more then once, you will get an error from Puppet that looks like this:
Error: Could not retrieve catalog from remote server: Error 400 on SERVER: Duplicate declaration: Tomcat::Port[8080] is already declared in file /etc/puppetlabs/puppet/modules/tomcat/manifests/instance.pp:310; cannot redeclare at /etc/puppetlabs/puppet/modules/tomcat/manifests/instance.pp:321 on node agent-0.puppetlabs.vm
Warning: Not using cache on failed catalog
Error: Could not retrieve catalog; skipping run
This protects you from destroying working Tomcat installations by trying to re-allocate the port. To resolve this error, simply make sure that each port assigned only once per host.
Using multiple instances protects you against the "bad apple" problem of a single badly behaved webapp crashing your entire tomcat installation, as often happens when organisations run huge shared instances of tomcat with lots of webapps running. By isolating each webapp (or group of webapps...) to its own container, you both limit the risk of complete failure and gain visibility of the webapp(s) causing problems - because now they only kill themselves, not everything. See for comprehensive information.
You can run each tomcat instance as a different user if you want to provide better protection and separation of sensitive data.
::tomcat::instance { "myapp1":
http_port => 8080,
shutdown_port => 8009,
instance_user => "myapp1",
}
::tomcat::instance { "myapp2":
http_port => 8081,
shutdown_port => 8010,
instance_user => "myapp2"
}
In the above example, tomcat will run the first instance as the user myapp1
and the second instance as myapp2
. Note that you must create any users and
groups your instance uses yourself. See Setup Requirements
for an example.
Put the classes, types, and resources for customizing, configuring, and doing the fancy stuff with your module here.
The module supports the installation of shared/endorsed libraries to grant access
to libraries such as XML parsers (must be put in 'endorsed') and database drivers.
Libraries are preserved across upgrades and once installed are available to all
Tomcat intances managed by this module automatically
::tomcat::library { "postgresql-9.3-1102.jdbc41.jar":
download_site => "http://172.16.1.101",
}
The above example would attempt to download the postgres database driver from a
web server running at http://172.16.1.101
. Once installed, all running tomcat
instances managed by this module will be restarted so they can use the new library.
Libraries can also be removed by setting the ensure
parameter to absent
:
::tomcat::library { "postgresql-9.3-1102.jdbc41.jar":
ensure => absent,
}
Tomcat will also be restarted after libraries are removed.
Endorsed libraries are installed/removed in exactly the same way by setting the
parameter lib_type
to endorsed
, for example:
::tomcat::library { "jaxb-impl-2.2.3.jar":
download_site => "http://172.16.1.101",
lib_type => "endorsed",
}
JNDI/JDBC support in tomcat is normally achieved by editing server.xml, context.xml and copying a database driver into the lib directory. You can use this puppet module to do all of these tasks.
Once a suitable database driver has been installed (see above example), you can
pass fragments of XML to the ::tomcat::instance
resource and it will include
them in it's configuration file. This gives you the full flexibility of XML to
specify all required database parameters.
Rather then keeping these XML fragments in the puppet manifests, its a good idea to keep them separate using hiera, which could look something like this for an Oracle data source:
---
tomcat:instance:main:server_xml_jdbc: |
<Resource name="jdbc/application"
auth="Container"
factory="oracle.ucp.jdbc.PoolDataSourceImpl"
type="oracle.ucp.jdbc.PoolDataSource"
description="Application"
connectionFactoryClassName="oracle.jdbc.pool.OracleDataSource"
minPoolSize="2"
maxPoolSize="30"
inactiveConnectionTimeout="20"
user="tomcat"
password="t0ps3cr3t :)"
url="jdbc:oracle:thin:@(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=db.bigcorp.com)(PORT=1521))(CONNECT_DATA=(SERVICE_NAME=PROD)))"
connectionPoolName="applicationConnectionPool"
validateConnectionOnBorrow="true"
sqlForValidateConnection="select 1 from DUAL" />
tomcat:instance:main:context_xml_jdbc: |
<ResourceLink name="jdbc/application"
global="jdbc/application"
type="javax.sql.DataSource"/>
The manifest (for your tomcat role...?) would then lookup pass the XML
fragments to the tomcat instance and the relevant section will be included in
the server.xml
and context.xml
files.
$server_xml_jdbc = hiera("tomcat:instance:main:server_xml_jdbc")
$context_xml_jdbc = hiera("tomcat:instance:main:context_xml_jdbc")
::tomcat::instance { "main":
http_port => 8080,
shutdown_port => 8009,
server_xml_jdbc => $server_xml_jdbc,
context_xml_jdbc => $context_xml_jdbc,
}
Don't forget to install the database driver!
The HTTPs connector is configured for a tomcat::instance
by allocating a port
wit the https_port
parameter and then supplying any additional require XML in
the https_attributes
parameter.
The user is responsible for setting up any required key stores, etc. There is comprehensive documentation in the tomcat SSL HOW-TO.
It's a good idea to put the required SSL attributes in your node's hiera file like this:
---
# all of the attributes from the SSL-HOWTO *except* the port
:tomcat:instance:main:ssl_attributes: |
protocol="HTTP/1.1"
maxThreads="200"
scheme="https" secure="true" SSLEnabled="true"
keystoreFile="${user.home}/.keystore" keystorePass="changeit"
clientAuth="false" sslProtocol="TLS"
So that you have easy access to them in your manifests:
$https_attributes = hiera(":tomcat:instance:main:ssl_attributes:")
::tomcat::instance { "main":
http_port => 8080,
shutdown_port => 8009,
https_port => 8081,
https_attributes => $https_attributes,
}
If it doesn't work, make sure your keystore is created and readable and that you haven't specified the connector port more then once
By default, the module creates files and directories with the following owners, groups and permissions:
Item | Owner | Group | Mode |
---|---|---|---|
init script | always root | file_group |
file_mode_init |
setenv.sh | file_owner |
file_group |
file_mode_script |
instance RO dirs | file_owner |
file_group |
file_mode_regular |
instance RW dirs | instance_user |
instance_user |
file_mode_regular |
regular files | file_owner |
file_group |
file_mode_regular |
- The init script is always owned by root. Be careful with the permissions on
this file - recall that init scripts are run as
root
at boot time setenv.sh
has additional execute permission- Instance RO dirs contain files that tomcat needs to read but not alter and the tomcat process itself shouldn't be able to write to these directories
- Instance RW dirs need to be writable by the owner of the Java process running tomcat for the server to work properly as they contain items such as log files and temporary storage
- Regular files are all other files that puppet installs regarding tomcat
Default values
The authorative default values of these fields are set in the params.pp
file
and are currently:
$file_mode_regular = "0640"
$file_mode_script = "0750"
$file_mode_init = "0755"
$file_owner = "root"
$file_group = "tomcat"
$instance_user = "tomcat"
Don't forget that when the Puppet agent runs, it will "add one" to directories
which have the read bit set, so directories created with a $file_mode_regular
of 0640
will end up with a final mode of 0750
so that you can cd
into
them.
If you wish to change any of these values from the defaults, refer to the following table. If you need to make changes, be sure to set the parameter in for all applicable defined resources at the same time to avoid getting an unpredictable mix of explict and default permissions.
Parameter | tomcat | tomcat::install | tomcat::instance | tomcat::library |
---|---|---|---|---|
file_owner |
YES | YES | YES | YES |
file_group |
YES | YES | YES | YES |
file_mode_regular |
YES | YES | YES | |
file_mode_script |
YES | |||
file_mode_init |
YES | |||
instance_user |
YES |
file_mode
andfile_group
only change the ownership of the symlink to the default tomcat version when used with tomcat::install. It is up to the RPM package to set suitable permissions for the files under$CATALINA_HOME
when packaging tomcat.
By default, the module installs to the following main directories:
/var/tomcat
-- instances/usr/local/lib/tomcat_shared
-- shared Java libraries/usr/local/lib/tomcat_endorsed
-- endorsed Java libraries
Its possible to override each of these locations, however you must do so for each of the resource types that manages tomcat on the system.
Say we wanted to install a tomcat on a node at the following locations instead:
/home/tomcat
-- instances/home/tomcat/lib
-- shared Java libraries/home/tomcat/endorsed
-- endorsed Java libraries
We could do that like this:
# set the system-wide locations for this node
class { "::tomcat":
instance_root_dir => "/home/tomcat",
shared_lib_dir => "/home/tomcat/lib",
endorsed_lib_dir => "/home/tomcat/endorsed",
}
# Install a version of tomcat
tomcat { "apache-tomcat-7.0.56": }
# create a tomcat instance using the directories created when the tomcat
# class was declared
::tomcat::instance { "main":
http_port => 8080,
shutdown_port => 8009,
shared_lib_dir => "/home/tomcat/lib",
endorsed_lib_dir => "/home/tomcat/endorsed",
}
It's also possible to disable system-wide shared/endorsed libraries by setting
the shared_lib_dir
or endorsed_lib_dir
parameter to false on the tomcat
class declaration and all ::tomcat::instance
resources on the node.
If you do so, note that you still have access to an individual /lib
directory
within each tomcat instance.
If you find the supplied templates don't meet your needs, you are able to
substitute any or all of them via the ::tomcat::instance
resource
eg:
::tomcat::instance { "main":
http_port => 8080,
shutdown_port => 8009,
init_script_template => "myorg_tomcat/init_script.erb",
setenv_sh_template => "myorg_tomcat/setenv.sh.erb",
server_xml_template => "myorg_tomcat/server.xml.erb",
catalina_properties_template => "myorg_tomcat/catalina.properties.erb",
context_xml_template => "myorg_tomcat/context.xml.erb",
logging_properties_template => "myorg_tomcat/logging.properties.erb",
tomcat_users_xml_template => "myorg_tomcat/tomcat-users.xml.erb",
web_xml_template => "myorg_tomcat/web.xml.erb"
In this imaginary scenario, we have replaced the default templates with our own
from a module we have made ourselves called myorg_tomcat
. Recall that
templates are expected to be found in a sub-directory of the module called
templates
By default, any .war
files found in the webapps
directory of an instance
will be unpacked and deployed. If you don't want this to happen, you can turn
this behavour off:
::tomcat::instance { "main":
http_port => 8080,
shutdown_port => 8009,
unpack_wars => false,
auto_deploy => flase,
You can install as many side-by-side version of apache tomcat on the node as you like as long as your packaging allows it!.
To install multiple tomcat versions, simply specify the tomcat packages to install.
eg:
::tomcat::install { "myorg-apache-tomcat-7.0.55":
symlink_target => "/usr/local/apache-tomcat-7.0.55"
}
::tomcat::install { "myorg-apache-tomcat-7.0.56": }
In this example, we installed two tomcat versions and set the older version
7.0.55 to be the default by supplying the symlink_target
parameter. We have
to tell Puppet where to point the symlink it creates because it doesn't know
what files are in the package
To make a tomcat instance use a specific version of Java, set it's java_home
parameter.
eg:
::tomcat::instance { "main":
http_port => 8080,
shutdown_port => 8009,
java_home => "/usr/java/jdk1.7.0_67",
}
Would force this Tomcat instance to run Java from /usr/java/jdk1.7.0_67
Don't forget to install Java at the location specified!
To make a tomcat instance use a specific version of Apache Tomcat, set it's
catalina_home
parameter.
eg:
::tomcat::instance { "main":
http_port => 8080,
shutdown_port => 8009,
catalina_home => "/usr/local/apache-tomcat-7.0.56",
}
Would force this tomcat instance to run Tomcat from /usr/local/apache-tomcat-7.0.56
To remove old versions of Tomcat from a node, simply set the ensure
parameter
to absent
on the ::tomcat::install
resource you want to get rid of.
eg:
::tomcat::install { "myorg-apache-tomcat-7.0.40":
ensure => absent
}
Would remove tomcat 7.0.40 from the system
JMX allows you to monitor and control your tomcat instance which can be useful if you need to debug a problem. Be aware that some monitoring programs could trigger excessive slowdowns depending on what they are doing and also be aware of the security implications of granting access.
To enable JMX, set the jmx_port
parameter to the TCP port you wish to listen
on.
eg:
::tomcat::instance { "main":
http_port => 8080,
shutdown_port => 8009,
jmx_port => 6666,
}
Would start an un-authenticated listener on port 6666. If you need
authentication and/or SSL, read the ::tomcat::instance documentation inside
the manifest and look at the jmx_authenticate
, jmx_ssl
, jmx_password_file
and jmx_access_file
parameters.
AJP (mod_jk
)
can be enabled by setting the ajp_port
parameter on an instance to be the TCP
port you want to listen on.
eg:
::tomcat::instance { "main":
http_port => 8080,
shutdown_port => 8009,
ajp_port => 8005,
}
Would start an AJP listener on port 8005.
Note that in a lot of cases, it's quicker and easier to skip AJP altogether and just use the HTTP/1.1 connector.
tomcat
Sets up shared directories (if required) and loads the tomcat::params
class
into scope for use by the defined types.
tomcat::install
Installs the tomcat RPM and optionally symlinks it to a default location
tomcat::instance
Create a tomcat instance on a node
tomcat::library
Installs or removes a shared/endorsed library from a node
tomcat::params
Shared class parameters
tomcat::port
Dummy class used to enforce the rule of not being able to allocate the same
port more then once on a node
Comprehensive tests are available in the spec
directory.
rake spec
To run tests
Only supports RedHat OS family and Tomcat 7
Patches welcome if accompanied by relevant spec test(s)
Geoff Williams