Wednesday, March 07, 2007

Capistrano and Java

I'm sure a lot of you Rails developers who read my blog are well aware of Capistrano. For the non-Rails developers, here's a two sentence intro: Capistrano is automated deployment. Fill in some configuration about which boxes perform which tasks (things like which are the DB servers, which are the web servers, which are the application servers, etc) and then simply type "cap deploy" from a command-line every time you want to release a new version. Needless to say, it's very slick and almost 100% necessary for any application that runs on more than 2 machines.

Capistrano is definitely Rails tailored. It makes a lot of assumptions (in true Rails philosophy fashion) that make it dead simple to use for your new Rails app. However, there's nothing really constricting about Cap itself that forces it to be Rails only. Recently, Maurice and I did our first Java project for Openomy. We knew we'd be deploying to more than one box, so we decided we'd try out Cap for deploying a Java service to multiple boxes. I figured I'd write up some of what we learned and what we'd like to change in the future.

(From here on out, I assume basic working knowledge of Cap.)

How It's Implemented

1. First, we created some new tasks (overriding some of the defaults):

set :application, "file_service"
set :is_test, ""

task :build_and_run do
run <<-CMD
cd #{current_path} &&
env JAVA_HOME=#{java_home} #{ant} dist &&
scripts/start-fileservice #{is_test}
CMD
end

task :deploy do
update
build_and_run
end

task :deploy_test do
set :application, "file_service_test"
set :is_test, "test"

deploy
end


2. Next, we wrote the scripts/start-fileservice script which starts up our Jetty housed service. There are about 50 lines in this Perl script but I'm not going to go into them because I'm sure you can figure out how to start up Jetty in an automated fashion. Basically, we keep a pidfile and use the standard jetty-start.jar to start up and restart.

3. Note that the "#{ant} dist" line runs ant and builds our dist target, which creates our Jar file.

4. Now, if we want to start up our test service, we simply run "cap deploy_test". Otherwise, if we want to start up our production service, we run "cap deploy". That's basically all there is to it.

Things We Assumed

1. We assumed that Perl, Java, and Ant were all installed on each of the application boxes.

2. Further, we assumed that they existed in exactly the same location on each box.

3. We similarly had to make the same assumptions that Cap makes (that subversion exists on each machine, for example).

Things I'd Like To Change

1. Instead of having to pre-install each box with certain software, I'd really like to be able to just acquire a new box with just an OS (and subversion) on it and run "cap deploy" and have everything automagically work. To do that would require me to have JDK and Ant and Perl all within my subversion repository previously built and then use those to run my scripts and services.

2. More realistically, I'd rather not have a dependency on Ant. The primary reason we use Ant is to define our classpath correctly for us. It'd take a lot more engineering to have cap learn about our classpath and then set that correctly before calling our startup script. Add on to that the fact that we really only need to build this once and deploy it everywhere (rather than building everywhere) and Ant seems a little abused in this regard.

Note that neither #1 nor #2 are particularly important at a scale of <25 boxes, in my opinion. Once above that range, though, they start to become very important.

3. As you can see from our tasks, our rollback procedures could use some work, though the standard rollback tasks built in by cap are moderately sufficient for our uses.

4. Cap really needs a web based interface. I'll probably work on this at some point though I've heard rumors that someone else has already started this and it looks good (yay!).

Labels: , , , , , , , ,