Let's say you want to configure virtual hosts in Apache, with each virtual host talking to a different Tomcat instance via the mod_jk connector. Each virtual host serves up a separate application via an URL such as http://www.myapp.com. This URL needs to be directly mapped to a Tomcat application. This is a fairly important requirement, because you don't want to go to a URL such as http://www.myapp.com/somedirectory to see your application. This means that your application will need to be running in the ROOT of the Tomcat webapps directory.
You also want Apache to serve up some static content, such as images.
Running multiple instances of Tomcat has a couple of advantages: 1) you can start/stop your Tomcat applications independently of each other, and 2) if a Tomcat instance goes down in flames, it won't take with it the other ones.
Here is a recipe that worked for me. My setup is: CentOS 4.4, Apache 2.2 and Tomcat 5.5, with mod_jk tying Apache and Tomcat together (mod_jk2 has been deprecated).
Scenario: we want www.myapp1.com to go to a Tomcat instance running on port 8080, and www.myapp2.com to go to a Tomcat instance running on port 8081. Apache will serve up www.myapp1.com/images and www.myapp2.com/images.
1) Install Apache and mod_jk. CentOS has the amazingly useful yum utility (similar to apt-get for you Debian/Ubuntu fans), which makes installing packages a snap:
# yum install httpd
# yum install mod_jk-ap20
2) Get the tar.gz for Tomcat 5.5 -- you can download it from the Apache Tomcat download site. The latest 5.5 version as of now is apache-tomcat-5.5.23.tar.gz.
3) Unpack apache-tomcat-5.5.23.tar.gz under /usr/local. Rename apache-tomcat-5.5.23 to tomcat8080. Unpack the tar.gz one more time, rename it to tomcat8081.
4) Change the ports tomcat is listening on for the instance that will run on port 8081.
# cd /usr/local/tomcat8081/conf
- edit server.xml and change following ports:
8005 (shutdown port) -> 8006
8080 (non-SSL HTTP/1.1 connector) -> 8081
8009 (AJP 1.3 connector) -> 8010
There are other ports in server.xml, but I found that just changing the 3 ports above does the trick.
I won't go into the details of getting the 2 Tomcat instances to run. You need to create a tomcat user, make sure you have a Java JDK or JRE installed, etc., etc.
The startup/shutdown scripts for Tomcat are /usr/local/tomcat808X/bin/startup.sh|shutdown.sh.
I will assume that at this point you are able to start up the 2 Tomcat instances. The first one will listen on port 8080 and will have an AJP 1.3 connector (used by mod_jk) listening on port 8009. The second one will listen on port 8081 and will have the AJP 1.3 connector listening on port 8010.
5) Deploy your applications.
Let's say you have war files called app1.war for your first application and app2.war for your second application. As I mentioned in the beginning of this post, your goal is to serve up these applications directly under URLs such as http://www.myapp1.com, as opposed to http://www.myapp1.com/app1. One solution I found for this is to rename app1.war to ROOT.war and put it in /usr/local/tomcat8080/webapps. Same thing with app2.war: rename it to ROOT.war and put it in /usr/local/tomcat8081/webapps.
You may also need to add one line to the Tomcat server.xml file, which is located in /usr/local/tomcat808X/conf. The line in question is the one starting with Context, and you need to add it to the Host section similar to this one in server.xml. I say 'you may also need' because I've seen cases where it worked without it. But better safe than sorry. What the Context element does is it specifies ROOT as the docBase of your Web application (similar if you will to the Apache DocumentRoot directory).
At this point, if you restart the 2 Tomcat instances, you should be able to go to http://www.myapp1.com:8080 and http://www.myapp2.com:8081 and see your 2 Web applications.
<Host name="localhost" appBase="webapps"
unpackWARs="true" autoDeploy="true"
xmlValidation="false" xmlNamespaceAware="false">
<Context path="" docBase="ROOT" debug="0"/>
6) Create Apache virtual hosts for www.myapp1.com and www.myapp2.com and tie them to the 2 Tomcat instances via mod_jk.
Here is the general mod_jk section in httpd.conf -- note that it needs to be OUTSIDE of the virtual host sections:
#
# Mod_jk settings
#
# Load mod_jk module
LoadModule jk_module modules/mod_jk.so
# Where to find workers.properties
JkWorkersFile conf/workers.properties
# Where to put jk logs
JkLogFile logs/mod_jk.log
# Set the jk log level [debug/error/info]
JkLogLevel emerg
# Select the log format
JkLogStampFormat "[%a %b %d %H:%M:%S %Y] "
# JkOptions indicate to send SSL KEY SIZE,
JkOptions +ForwardKeySize +ForwardURICompat -ForwardDirectories
# JkRequestLogFormat set the request format
JkRequestLogFormat "%w %V %T"
Note that the section above has an entry called JkWorkersFile, referring to a file called workers.properties, which I put in /etc/httpd/conf. This file contains information about so-called workers, which correspond to the Tomcat instances we're running on that server. Here are the contents of my workers.properties file:
#The file declares 2 workers that I named app1 and app2. The first worker corresponds to the AJP 1.3 connector running on port 8009 (which is part of the Tomcat instance running on port 8080), and the second worker corresponds to the AJP 1.3 connector running on port 8010 (which is part of the Tomcat instance running on port 8081).
# This file provides minimal jk configuration properties needed to
# connect to Tomcat.
#
# The workers that jk should create and work with
#
workers.tomcat_home=/usr/local/tomcat8080
workers.java_home=/usr/lib/jvm/java
ps=/
worker.list=app1, app2
worker.app1.port=8009
worker.app1.host=localhost
worker.app1.type=ajp13
worker.app1.lbfactor=1
worker.app2.port=8010
worker.app2.host=localhost
worker.app2.type=ajp13
worker.app2.lbfactor=1
The way Apache ties into Tomcat is that each of the VirtualHost sections configured for www.app1.com and www.app2.com declares a specific worker. Here is the VirtualHost section I have in httpd.conf for www.app1.com:
<VirtualHost *:80>
ServerName www.app1.com
DocumentRoot "/usr/local/tomcat8080/webapps/ROOT"
<Directory "usr/local/tomcat8080/webapps/ROOT">
# Options Indexes FollowSymLinks MultiViews
Options None
AllowOverride None
Order allow,deny
allow from all
</Directory>
ErrorLog logs/app1-error.log
CustomLog logs/app1-access.log combined
# Send ROOT app. to worker named app1
JkMount /* app1
JkUnMount /images/* app1
RewriteEngine On
RewriteRule ^/(images/.+);jsessionid=\w+$ /$1
</VirtualHost>
The 2 important lines as far as the Apache/mod_jk/Tomcat configuration is concerned are:
JkMount /* app1
JkUnMount /images/* app1
The line "JkMount /* app1" tells Apache to send everything to the worker app1, which then ties into the Tomcat instance on port 8080.
The line "JkUnMount /images/* app1" tells Apache to handle everything under /images itself -- which was one of our goals.
At this point, you need to restart Apache, for example via 'sudo service httpd restart'. If everything went well, you should be able to go to http://www.myapp1.com and http://www.myapp2.com and see your 2 Web applications running merrily.
You may have noticed a RewriteRule in each of the 2 VirtualHost sections in httpd.conf. What happens with many Java-based Web application is that when a user first visits a page, the application does not know yet if the user has cookies enabled or not, so the application will use a session ID mechanism fondly known as jsessionid. If the user does have cookies enabled, the application will not use jsessionid the second time a page is loaded. If cookies are not enabled, the application (Tomcat in our example) will continue generating URLs such as
http://www.myapp1.com/images/myimage.gif;jsessionid=0E45D13A0815A172BD1DC1D985793D02
In our example, we told Apache to process all URLs that start with 'images'. But those URLs have already been polluted by Tomcat with jsessionid the very first time they were hit. As a result, Apache was trying to process them, and was failing miserably, so images didn't get displayed the first time a user hit a page. If the user refreshed the page, images would get displayed properly (if the user had cookies enabled).
The solution I found for this issue was to use a RewriteRule that would get rid of the jsessionid in every URL that starts with 'images'. This seemed to do the trick.
That's about it. I hope this helps somebody. It's the result of some very intense googling :-)
If you have comments or questions, please leave them here and I'll try to answer them.
35 comments:
Very useful article.
Thanks a lot.
Hi Grig. I was very interested in using yum to install mod_jk on CentOS, but when I run the command, I get "No Match for argument: mod_jk-ap20". Any ideas?
Matt -- good point. I should have specified that I used the JPackage yum repository. Instructions here: http://www.devco.net/pubwiki/RedHat/Java
Here's what I did, in a nutshell:
1) cd /etc/yum.repos.d/
2) wget http://jpackage.org/jpackage.repo
3) Edit jpackage.repo and specify 'enabled=1' in the [jpackage-rhel] section
3) if you now do
# yum search mod_jk*
you'll get:
mod_jk-tools.i386 1.2.15-1jpp jpackage-rhel
Matched from:
mod_jk-tools
Analysis and report tools for mod_jk
Miscellaneous mod_jk analysis and report tools.
mod_jk-ap20.i386 1.2.15-1jpp jpackage-rhel
Matched from:
mod_jk-ap20
Tomcat mod_jk connector for Apache 2.0.x.
Tomcat mod_jk connector for Apache 2.0.x.
mod_jk-manual.i386 1.2.15-1jpp jpackage-rhel
Matched from:
mod_jk-manual
Tomcat mod_jk connector manual
Tomcat mod_jk connector manual.
mod_jk2.i386 2.0.4-7jpp jpackage-generic
Matched from:
mod_jk2
Tomcat mod_jk2 connector for Apache 2.0.x
HTH,
Grig
Hi Grig, it's a great article. But do you have the same instruction for installing Apache2 and Tomcat6.
Thanks,
Grig, I need to setup Apache 2.0.59 and Tomcat 5.5.25. However, Apache is on BoxA and Tomcat is on BoxB. I've googled and googled for information regarding configuring Apache to talk to a remote Tomcat worker with no luck. Any suggestions?
-Erik
Hi Grig, thank you very much for your article.
Hi Grig,
I have e'thing set up and configured Apache2, Tomcat 5.5, mod_jk..problem I am facing is strange and I could not undertstand..!
I have a following URL: http://license.changingworlds.com which is accessible only if you append port 8080 to it...!! for example URL works fine if you http://license.changingworlds.com:8080 and while http://license.changingworlds.com displays mere text in the browser..your help will be apppreciated. Thanks. Regards.
Zulfiqar Mansoor
Zulfikar -- it seems that mod_jk doesn't work as expected in your case. When you specify the port in the URL, you end up hitting tomcat directly, without going through apache. When you hit the URL with no port, you hit apache, then mod_jk is supposed to relay that request to tomcat. I'd look through the mod_jk log and the apache logs, and try to figure out what's going on.
Grig
Hi Grig. Thanks for your reply! If above is the case then when i try http://localhost:8080 why apache homepage is displayed..it should be tomcat home page which should be displayed..right?
I verified mod_jk.log and found following details:
[Thu Sep 27 12:04:40 2007] [3916:656] [debug] jk_connect.c (455): trying to connect socket 840 to 127.0.0.1:8009
[Thu Sep 27 12:04:41 2007] [3916:656] [info] jk_connect.c (473): connect to 127.0.0.1:8009 failed (errno=61)
[Thu Sep 27 12:04:41 2007] [3916:656] [info] jk_ajp_common.c (891): Failed opening socket to (127.0.0.1:8009) (errno=61)
[Thu Sep 27 12:04:41 2007] [3916:656] [info] jk_ajp_common.c (1311): (default) error connecting to the backend server (errno=61)
[Thu Sep 27 12:04:41 2007] [3916:656] [info] jk_ajp_common.c (2085): (default) sending request to tomcat failed, recoverable operation attempt=2
[Thu Sep 27 12:04:41 2007] [3916:656] [error] jk_ajp_common.c (2097): (default) Connecting to tomcat failed. Tomcat is probably not started or is listening on the wrong port
Then I verified port in httpd.conf its perfectly fine mod_jk is configured on 8009 while tomcat is running on 8080.
Any thouhgts on this?
Regards.
Zulfiqar
Zulifqar -- from the mod_jk.log entries, it looks like tomcat has not been started. mod_jk tried to connect to it and failed. Did you make sure you started up tomcat? You shouldn't see the apache home page when you hit localhost:8080, unless apache is for some reason listening on that port already. If that is the case, it would explain why tomcat can't run: when you start tomcat up, it sees that something else is already listening on port 8080, and hence it fails. Check out the catalina.out file in TOMCAT_ROOT/logs and see if it contains any errors.
Grig
Grig,
You have no idea what a life saver you are. I've been searching the internet and running in circles for days now trying to get my site setup. I read this and the previous post on Apache/Tomcat configuration and it works perfectly. Thank you so much.
Hi Grig,
Nice article.
Do you know if tomcat 6 needs to be configured differently to talk to apache?
I have tomcat 6 running on port 8100 but cannot get it to apache on a linux server. I can do it on my PC which runs XP. The difference is that the linux box has two other versions (standalone) of tomcat running. Any ideas?
Hi I am running the setup in your great article,I have an issue when I change to https:// mode, jk
url matching is no longer forwarded, apache seems to then just look in htdocs.Please does anyone have any idea why and how to solve the problem, its driving me mad. Apache 2.0.61,latest jk connector, openssl g.
Many thanks
Here is the general mod_jk section in httpd.conf -- note that it needs to be OUTSIDE of the virtual host sections:
#
# Mod_jk settings
#
# Load mod_jk module
LoadModule jk_module modules/mod_jk.so
# Where to find workers.properties
JkWorkersFile conf/workers.properties
# Where to put jk logs
JkLogFile logs/mod_jk.log
# Set the jk log level [debug/error/info]
JkLogLevel emerg
# Select the log format
JkLogStampFormat "[%a %b %d %H:%M:%S %Y] "
# JkOptions indicate to send SSL KEY SIZE,
JkOptions +ForwardKeySize +ForwardURICompat -ForwardDirectories
# JkRequestLogFormat set the request format
JkRequestLogFormat "%w %V %T"
Note that the section above has an entry called JkWorkersFile, referring to a file called workers.properties, which I put in /etc/httpd/conf. This file contains information about so-called workers, which correspond to the Tomcat instances we're running on that server. Here are the contents of my workers.properties file:
#
# This file provides minimal jk configuration properties needed to
# connect to Tomcat.
#
# The workers that jk should create and work with
#
workers.tomcat_home=/usr/local/tomcat8080
workers.java_home=/usr/lib/jvm/java
ps=/
worker.list=app1, app2
worker.app1.port=8009
worker.app1.host=localhost
worker.app1.type=ajp13
worker.app1.lbfactor=1
worker.app2.port=8010
worker.app2.host=localhost
worker.app2.type=ajp13
worker.app2.lbfactor=1
Dear All I found the answer to the problem, I can't belive it. JK-1.2.26 for me does not forward usrl matching under SSL. I used JK-1.2.25 and it all works fine.Very odd .26 Does not work propoerly for me under SSL!!!!!!
Need help.I have an application deployed on Tomcat and am connecting to it thru Apache http serevre.I need to enable Tomcat directory listing.
JkOptions +ForwardKeySize +ForwardURICompat +ForwardDirectories is given in httpd.conf but still am getting 'Page cannot be found':-(
Bibin -- I had problems too trying to do directory listing within Tomcat. The solution I found was to create another Apache virtual host with the DocumentRoot set to the directory I wanted to list within Tomcat.
Grig
Hi Grig. What a great article. Thank you very much for the information. I have a slight problem though.
I have two applications running on separate tomcat instances. I have Liferay (app1) on Tomcat8080 and Alfresco (app2) on Tomcat8081. However, with the settings that you provided, the mod_jk does not seem to be transmitting any requests to the 8081 port. That means when I have app2 connected to port 8081 and AJP port 8009, I get a serer busy error. But when I changed it to port 8080 , www.app2.com works.
On port 8081 the mod_jk log shows that the Tomcat is probably not started or is listening on the wrong port. Is there something we need to configure in mod_jk to listen to more than one port.
I am running Ubuntu 8.04 and Apache 2.2 and I have installed the mod_jk using Ubuntu's repositories. Please help me.
rb -- it sounds like your tomcat instance on port 8081 is not configured properly. You need to modify its conf/server.xml file and change 8080 to 8081, and 8009 to 8010 for example (step 4 in my post). Then restart it and make sure you can hit your app directly on port 8081 in your browser.
If this works, then you need to modify workers.properties and specify the 2 workers, one tied to port 8009 and the other to port 8010.
Grig
Grig,
I want to setup connector for multiple instances of Tomcat but without virtual hosts. Do I need to do anything with changing DocumentRoot in httpd.cong?
So far I installed mod_jk.so, edited files httpd.conf and workers.properties but nothing works.
All logs show no errors and show that mod_jk is working. But when I hit my server without port number it just goes to old apache content, it does not redirect to Tomcat content.
Thanks
Anonymous -- take a look at my other blog post on apache/tomcat/mod_jk:
http://agiletesting.blogspot.com/2005/10/configuring-apache-2-and-tomcat-55.html
Grig
Hi Grig,
I looked at your post
http://agiletesting.blogspot.com/2005/10/configuring-apache-2-and-tomcat-55.html
I followed your steps but still it does not work.
I have the following versions of software:
Tomcat 6.0.16
tomcat-connectors-1.2.26
Apache/2.2.8 (Ubuntu) PHP/5.2.4-2ubuntu5.1 with Suhosin-Patch mod_jk/1.2.26 Server
Apache reports this line above and I guess it means that mod_jk connector was installed and loaded OK.
Tomcat is installed on port 8080:
Server port =”8005” shutdown ->
Connector port = “8080” protocol=”HTTP/1.1” ->
Connector port = “8009” protocol=”AJP/1.3” ->
Tomcat is running perfectly for about 4 month already.
I want specifically test jsp files accessible OK at:
http://my.server.edu:8080/examples/jsp/jsp2/el/basic-arithmetic.jsp
Here are my configuration files:
httpd.cong:
--------
LoadModule jk_module /usr/lib/apache2/modules/mod_jk.so
JkWorkersFile /etc/apache2/workers.properties
JkShmFile /var/log/apache2/mod_jk.shm
JkLogFile /var/log/apache2/mod_jk.log
JkLogLevel debug
JkLogStampFormat "[%a %b %d %H:%M:%S %Y] "
JkOptions +ForwardKeySize +ForwardURICompat -ForwardDirectories
JkMount /examples/jsp/jsp2/el/*.jsp worker_sp
Alias /examples "/home/dspace4/tomcat/webapps/examples"
[Directory "/home/dspace4/tomcat/webapps/examples"]
Options Indexes MultiViews FollowSymLinks
AllowOverride None
Allow from all
[/Directory]
------------
(Note I changed angle brackets to [] , otherwise it is removed by this blog)
and
workers.properties:
-------------
workers.tomcat_home=/home/dspace4/tomcat
workers.java_home=/usr/java/jdk1.6.0_07
ps=/
worker.list=worker_sp
worker.worker_sp.type=ajp13
worker.worker_sp.host=localhost
worker.worker_sp.port=8009
worker.worker_sp.lbfactor=1
-------------
When I try
http://my.server.edu:8080/examples/jsp/jsp2/el/basic-arithmetic.jsp
it works OK
but
http://my.server.edu/examples/jsp/jsp2/el/basic-arithmetic.jsp
shows source code
It means Apache serves static pages OK but dynamic content is not processed by Tomcat.
Could you please advise?
Thanks!
Sergio
Sergio -- I'd look into mod_jk.log and the apache error log to see if there are any details. Also, I'd try to JkMount /examples/* instead of the full path to your jsp dir.
Grig
Thanks Grig,
I looked at logs and from there I found here:
http://marc.info/?l=tomcat-user&m=120164756115289&w=2
That from Jk version 1.2.26 "mounts are not inherited from the global server to any virtual host."
So even I didn't setup Virtual Hosts whole my server was setup by default as VirtualHost *.
So I added
JkMount /myapp/* worker_my
within VirtualHost, not globally
But even better I mounted this file there:
JkMountFile /etc/apache2/uriworkermap.properties
and defined which worker serve which application in this file
It works now.
Thanks,
Sergio
Brilliant, thanks very much!
I'm looking for a way to connect different virtual hosts to different contexts on the same tomcat instance without having to create a new tomcat instance for each java app.
Any ideas?
maxmil -- have you tried doing JkMounts to different portions of the same tomcat instance?
For example, in one vhost definition you could have
JkMount /myapp1/* tomcat_worker
In another vhost definition you could have:
JkMount /myapp2/* tomcat_worker
...etc
Grig
Thanks for the quick reply.
The thing is that i want my virtual hosts to map directly to my contexts.
For example i want
http://www.domain1.com => http://www.domain1.com:8080/context1
and
http://www.domain2.com => http://www.domain2.com:8080/context2
I think that the way to do this is to configure several different hosts within the same tomcat instance and have each context i want to map to be the root context of its host.
maxmil -- now I understand you scenario. First of all, I warmly recommend this book: "Professional Apache and Tomcat 5" from Wrox (http://www.amazon.com/Professional-Apache-Tomcat-Programmer/dp/0764559028).
In chapter 16, "Shared Tomcat Hosting", they recommend setting up one Host element in server.xml per virtual host that you need. All the Host elements are within the same Engine element, and each Host element can contain its own Logger element.
You also need to add contexts to server for each virtual host.
Here is their example of a Host element: (blogger doesn't let me use the tomcat specific tags, so I'll replace the less-than sign with LTS and greater-than sign with GTS):
LTS Host name='example.com' debug='0'
appBase='/home/websites/example.com/webapps'
autoDeploy='true'
unpackWars='true' GTS
LTS Context path='' docBase='ROOT' debug='0'/ GTS
LTS Context path='/shop' docBase='shop' debug='0'/ GTS
LTS /Host GTS
You'd have similar Host elements for each of the vhosts you need.
HTH -- please leave a comment if it does, so others can benefit from our discussion.
Grig
Hi Grig,
Thanks for the idea, i didn't use the book but setting up several hosts in tomcat was not too hard and worked like a charm.
In addition to the two hosts i had to make two separate workers, one for each host.
This is actually really handy as it enables load balancing.
I set out the detail of my steps in a post here
http://www.alwayssunny.com/blog/?p=174
Thanks... Good one
could you please tell me the meaning of worker.app2.lbfactor=1 , what i understood is it could handle one request at a time.
But whenever i request the url it ediredts the url to same tomcat.Is that ok or the cluster i have created has some issues.
The request is transferred to the next tomcat only if the previous tomcat is stopped.
How does this work if the tomcat instance is on a different machine than the apache instance? For example, you have the following in the VirtualHost of httpd.conf, but the DocumentRoot here assumes that tomcat is on the same machine apache. What would the DocumentRoot be (see below) if tomcat were on a remote machine?
VirtualHost *:80
ServerName www.app1.com
DocumentRoot "/usr/local/tomcat8080/webapps/ROOT"
Directory "usr/local/tomcat8080/webapps/ROOT"
# Options Indexes FollowSymLinks MultiViews
Options None
AllowOverride None
Order allow,deny
allow from all
I am using tomcat6 & apache2 with jk_mod. I put these lines just below the LoadModules section
"
JkWorkersFile conf/workers.properties
JkLogFile logs/mod_jk.log
JkLogStampFormat "[%b %d %Y - %H:%M:%S] "
JkRequestLogFormat "%w %V %T"
JkLogLevel info
JkOptions +ForwardKeySize +ForwardURICompat -ForwardDirectories
deny from all
"
Now i created vhosts.conf file in conf.d directory and put
Alias /jk_test "/opt/test/webapp/"
AllowOverride None
Allow from all
JkMount /jk_test/* myworker
Everything works fine.
Now i tried to create some VirtualHost in httpd.conf file to server some other sites.
E.g: NameVirtualHost xxx.xxx.xxx.xxx:80
ServerAdmin abc@xyz.com
DocumentRoot /opt/site
ServerName site.xyz.com
ErrorLog logs/site.xyz.com-error_log
CustomLog logs/site.xyz.com-access_log common
Restarted my httpd service and now not able to access both sites. I mean neither jk_mod site nor VirtualHost site.
But when i disable the virtualhost jk_mod works fine.
What should i do now ?
can you also give example on a debian/ubuntu platform for virtual hosting in apache2
Hi,
Thanks for the article.
Where do you serve all the image files & how's the configuration ?
Excellent post. just resolved tomcat and apache integration based on it. Thanks.
Mahes
Post a Comment