mercoledì 17 settembre 2014

Apache, Tomcat and Grails! Virtualhosts, hosts and contexts configurations!

I was in need to deploy a Grails application as a war on my Digitalocean instance.
The architecture i wanted to have was an Apache which redirects some calls to Tomcat (while manages others directly) and have my grails application deployed as a war on tomcat, responding without context path.

You can see the final result here at www.appromocodes.com (at the moment of this article there is only a landing page open to the public!)

This post is strictly related to DigitalOcean setup and Ubuntu 14.04.

So, to be clear i want my app (which comes from a war called appromocodes.war) to respond to www.appromocodes.com and i want that phpmyadmin responds at www.appromocodes.com/phpmyadmin and i want to have some other virtualhosts possibilities open.

So here are the steps:

Install servers

First of all, i installed a LAMP using DO images (but i thin it's a common ubuntu LAMP installation) then i installed tomcat and in the end i installed phpmyadmin. DO has a very comprehensive list of tutorials on how to do this, so check out for them. They will guid you through standard ubuntu installations

Enable modjk

This guide gives you hint on how to connect Apache 2 and Tomcat 7 using (and installing) mod_jk but there are a few points such as modifiyng 000-default.conf which are not best practice so i'll give you my way. Install mod jk via
sudo apt-get install libapache2-mod-jk
then configure tomcat to receive mod jk incoming traffic
sudo vim /etc/tomcat7/server.xml
uncommenting
<Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />
Now let's create a worker file for Apache via
sudo vim /etc/apache2/workers.properties
And set this content:
# Define 1 real worker using ajp13
worker.list=worker1
# Set properties for worker (ajp13)
worker.worker1.type=ajp13
worker.worker1.host=localhost
worker.worker1.port=8009
Now to ask Apache to use this worker

sudo vim /etc/apache2/mods-available/jk.conf

and change the JkWorkersFile property to
/etc/apache2/workers.properties
Now the guide linked before told you to modify 000-default.conf but this is not the correct way. We will create a specific conf and add it to apache via a2ensite command (this guide is very clear).

Configure Apache

Let's create a file. In my example it is called appromocodes.com.conf under the sites-available Apache folder and put this content within:
<VirtualHost *:80>
     
ServerAdmin admin@appromocodes.com
ServerName appromocodes.com
ServerAlias www.appromocodes.com
DocumentRoot /var/lib/tomcat7/webapps/appromocodes/
     
ErrorLog ${APACHE_LOG_DIR}/error.log
CustomLog ${APACHE_LOG_DIR}/access.log combined

JkUnMount /phpmyadmin* worker1
JkMount /* worker1
     
</VirtualHost>
As you can see this virtualhost responds to my www.appromocodes.com domain and pass any call to Tomcat via ajp. Only /phpmyadmin path is still controlled by Apache thanks to the JkUnMount directive.

Now
sudo a2ensite appromocodes.com.conf
and your site configuration will be enabled (a2dissite command to disable it).
Ah, don't forget
sudo service apache2 restart
to see changes enabled.
At this moment, apache serves correctly /phpmyadmin calls and redirect all the traffic for www.appromocodes.com to tomcat. If you want to create another virtualhost for another domain you will see that everything works correctly.

Configure Tomcat

Now that apache sends the traffic to tomcat, we must instruct tomcat on what to do with this traffic since any call to www.appromocodes.com will be redirected to www.appromocodes.com:8080 default site (even if we will not see the port number), but our app is deployed via war (or folder) with a specific context. At this moment (suppose i've already deployed my app on tomcat), my webapp will respond to www.appromocodes.com/appromocodes. We must still do something to achieve our final result
Ok, so we could still use apache and define rewrite rules but tomcat offers "virtual hosts" too. It is not as comfortable as apache but it's a very good solution.
You can find tons of information on tomcat docs regarding Host tag used to define virtual hosts but one thing you must be aware is that you must pay attention because it's easy to get multiple deploys of the same webapplication so after struggling a lot i found a good way to proceed and achieve my targets.
Modify tomcat' server.xml in its Engine section in this way (this is my example obviously):
<Engine name="Catalina" defaultHost="localhost">
<Realm className="org.apache.catalina.realm.LockOutRealm">
<Realm className="org.apache.catalina.realm.UserDatabaseRealm" resourceName="UserDatabase"/>
</Realm>
<Host name="localhost" appBase="webapps" unpackWARs="false" autoDeploy="false">
<Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs" prefix="localhost_access_log." suffix=".txt" pattern="%h %l %u %t &quot;%r&quot; %s %b" />
</Host>
<Host name="appromocodes.com" unpackWARs="false" autoDeploy="false">
<Alias>www.appromocodes.com</Alias>
</Host>

 
</Engine>
I have bolded the different elements respect the original ones. This section is telling Tomcat that we have two different hosts: localhost and appromocodes.com host to manage.
We have disabled autodeploys and war unpacking so tomcat will not do anything like this automatically (this will help avoid autoredeploy of a war in a first moment as a war and in a second as a directory after war extraction).
We are saying that both share the webapps folder (we could have specified another folder for appromocodes.com but it was useless now  and remeber to give that folder tomcat7:tomcat7 ownership and privileges).
Now, as specifically stated in the Tomcat docs, if you want to have an app that responds without context, you must declare it in a folder called ROOT or in a war called ROOT.war.
But i don't want to rename it any time so this is what we can do.
Execute
sudo service tomcat7 restart
and have a look at conf/Catalina. You will find a new folder called appromocodes.com. We ca now create a file called ROOT.xml in conf/Catalina/appromocodes.com with this content:
<Context
docBase="/var/lib/tomcat7/webapps2/appromocodes.war"
path=""
reloadable="true"
/>
This file will specify the ROOT application context for our appromocodes.com host and as you can see you can address any war file you want (or folder if you prefer).
If you now restart tomcat
sudo service tomcat7 restart
you will see that it will deploy a ROOT folder under webapps folder (if we had specified a different one in the server.xml we would have find it there so we could have specific ROOT folder for any host) and that's it!
Now our app responds correctly to www.appromocodes.com and we won't find any /appromocodes context in any call!

It is fundamental to define an external ROOT.xml file otherwise you will be able to specify only a different context instead of the natural one (war filename or folder name) but not a ROOT context and you will be subject to multiple redeploys.

Ok but...this is a general process not a Grails specific one!

That's right! This guide is ok for any general war deploy and apache 2 plus tomcat 7 configuration, but i have specified Grails too since it shows you how you can start from a Grails application, package it as a war via grails war (or grails prod war) and then deploying it as a J2EE webapplication.
Somebody might say it is better to run-app directly. In this case you can also specify different port such as 80 and have grails running directly your app. But in this way you loose the possibility to use Apache applications such as phpmyadmin or wordpress combined with your java app. And really important: grails consume much more memory while tomcat requires a lot less for the same app.

That's all folk, hope you save time!
Now that you have read my article, i would like to show you another thing: i've developed an app to help increase customers registration and customers conversion.

You can find it at appromocodes.com

Nessun commento:

Posta un commento