Thursday, June 17, 2010

Grails, Ant, Hudson and automated deployments

One of the things I like about Grails is the embedded server, it makes deployment and getting things up and running nice and simple. I've tried building a WAR and deploying into Jetty and Tomcat, but for some reason the system resources seem to spike when deployed in this fashion compared to the built in 'run-app' functions of the Grails command line tools.

With this in mind, I wanted to use the Grails command line to do automated deployment based off our Hudson continuous integration container via Ant build scripts.

First I created an init.d start/stop script for the server. At theTeam we can be working on multiple projects at the same time, so we need to set some conventions about port usage so we can manage our applications easier, so this is the first step towards just that.

NAME=projectname
PORT=port
ENVIRONMENT=staging
URI=$NAME.$ENVIRONMENT
DIRECTORY=/mnt/apps/$ENVIRONMENT/$NAME
case "$1" in
start)
lsof -i :$PORT
if [ $? -eq 0 ]; then
echo "The port "$PORT" is already in use"
else
echo -n "Starting daemon: "$NAME
nohup grails -Dserver.port=$PORT -Duri=$URI prod run-app &
echo "."
fi
;;
stop)
echo -n "Stopping daemon: "$NAME
ps -ef | grep $PORT | grep -v grep | awk '{print "kill " $2}' | sh
echo "."
;;
*)
echo "Usage: "$1" {start|stop}"
exit 1
esac
exit 0


This script is simple enough, on start it checks to see if that port is used, if so it bombs out, if not it nohup's the Grails command, telling it which port to use, and also just for ease of management, adding the name of the site. This means running commands like ps -ef and pipping to grep shows what's running. Stopping searches for a process running with that port and kills it.

That's the first step. Next is the Ant script to deploy the latest code after tests and static analysis. In this, the target folder has the latest code, we stop any server that's running, do a Grails clean and then run the server. The script then waits to see if it can open an socket to the port that the server is running on, and fails it it cannot. This usually means the server has failed to startup. The variables are gathered from a properties file and should be fairly obvious.

<target name="staging.deploy">
<echo message="Stopping the server"/>
<exec executable="${staging.directory}/etc/init.d/${staging.startstop}" dir="${staging.directory}">
<arg line="stop"/>
</exec>
<echo message="Cleaning the environment"/>
<exec executable="grails">
<env key="JAVA_HOME" value="/usr/lib/jvm/java-6-sun/"/>
<arg line="clean"/>
</exec>
<echo message="Starting the server"/>
<exec executable="${staging.directory}/etc/init.d/${staging.startstop}" dir="${staging.directory}" spawn="true">
<env key="BUILD_ID" value="dontKillMe" />
<env key="JAVA_HOME" value="/usr/lib/jvm/java-6-sun/"/>
<arg line="start"/>
</exec>
<echo message="Checking and waiting on port ${staging.port}"/>
<waitfor maxwait="120" maxwaitunit="second" checkevery="5" checkeveryunit="second" timeoutproperty="deploy.failed">
<socket server="localhost" port="${staging.port}"/>
</waitfor>
<fail if="deploy.failed" message="Timed out waiting for deploy"/>
</target>


Now, you might have noticed a few oddities in the script like setting JAVA_HOME and BUILD_ID. First, when running in Hudson, any processes are run using the JRE and JDK that is deployed with Hudson, and I wanted to ensure it was the system Java I had installed. The BUILD_ID is a little trick that tells Hudson not to terminate the process after the build has finished, which it had a habit of doing in earlier versions.

When starting Hudson, a flag should also be set to tell it not to kill child processes. My start command is thus:

java -jar -Dhudson.util.ProcessTreeKiller.disable=true hudson.war


Adding this all together means it's possible to get automated deployments with Grails added into your builds.

No comments: