how to deploy clojure applications using bakesale

2013-08-04 · Computing

In this guide, I show how to deploy Clojure applications using bakesale. I wrote bakesale because I wanted to return to the main ingredients of deployment, doing only so much as is finger-saving, and leaving the rest to the user to sort. I find this gives a lot of flexibility, whilst not cluttering my deployment config with lots of custom settings. bakesale is written as a collection of simple Shell scripts, and is pretty transparent. It’s built around the premise that deployment is much like baking a cake for a bakesale. Although I usually use it to deploy Ruby applications, deploying Clojure applications is just as straightforward. For this guide, I deploy MTRX9 (GitHub; Twitter), a simple websockets matrix monitoring tool which uses the notions of streams, chars, and time.

server prerequisites

The server, running Ubuntu 12.04 LTS, has a sudoer user called mtrx9, with a corresponding home directory. RubyGems is installed as a package, because I’ll use Foreman to write upstart scripts on the server. This isn’t necessary, of course, but it’s pretty convenient. Note that this server isn’t running Ruby applications, so I’m not using RVM or another version manager; instead, I just let the package dependencies get satisfied. I have Java OpenJDK installed, again as a package.

I manage these prerequisites using Puppet (a masterless setup, also deployed using bakesale); however, this is about deploying Clojure, so I’ll assume you’ve set up the server in whichever way makes you happy. To manually install the necessary packages:

apt-get install rubygems openjdk-7-jdk

install bakesale

bakesale just needs to be somewhere on your local system so that you can source it in scripts. I assume a local user called mlnw, with a corresponding home directory at /Users/mlnw/. I also assume your code repository is at /Users/mlnw/mtrx9/. Clone bakesale somewhere:

git clone git://github.com/tiredpixel/bakesale.git /Users/mlnw/bakesale

That’s all that’s needed to use bakesale; by default, the master branch will be used, which is (hopefully) stable.

define settings and services

bakesale discourages having the deployment config in the code repository itself (let’s stop embedding config in repositories). Create a directory to hold your deployment config:

mkdir -p /Users/mlnw/Deployments/mtrx9/

Create a file containing environment variables for the application; this will be exported to upstart using Foreman. MTRX9 only uses environment variables settings, but if you have other settings, you can write these in a similar manner. Copy the example and edit as appropriate:

cp /Users/mlnw/mtrx9/.env.example /Users/mlnw/Deployments/mtrx9/mtrx9.env

The Procfile defines services for the application. We will compile into a standalone uberjar, so write a custom Procfile:

# /Users/mlnw/Deployments/mtrx9/mtrx9.Procfile

web: java -jar target/*-*-standalone.jar $PORT

write deployment recipe

The deployment recipe defines the details of the deployment. However, it’s just a Shell script, so you can put whatever custom logic in there you need. If you find yourself using the same small fragment of code a lot across scripts, then it might be a good candidate for a new bakesale ‘stage’. It would be excellent if you would be so kind as to fork bakesale and submit a pull request with your improvement. Include bakesale at the top of your script:

source /Users/mlnw/bakesale/bakesale.sh

Then, just write a Shell script to deploy the application, using the bakesale ‘stage’ helpers. Without further ado, here is the complete script for deploying MTRX9. It clones the MTRX9 repository (fresh each time, by design), writes the settings and services configs, compiles the application into an uberjar, rsyncs everything to the server, uses Foreman to export it to upstart (Foreman gets automatically installed, if it’s not already, as part of that command), and restarts the exported services. This only takes a few lines:

# /Users/mlnw/Deployments/mtrx9/mtrx9.sh

#!/bin/bash

source /Users/mlnw/bakesale/bakesale.sh

ssh1=mtrx9@example.com

sshs="($ssh1)"

# = Bake

bakesale bake git git://github.com/tiredpixel/mtrx9.git

bakesale bake copy /Users/mlnw/Deployments/mtrx9/mtrx9.env .env
bakesale bake copy /Users/mlnw/Deployments/mtrx9/mtrx9.Procfile Procfile

bash -c "cd '$bakesale_cakebox/'; lein uberjar" # or any other custom step

# = Carry

bakesale carry rsync_ssh "$sshs" mtrx9/

# = Wave

bakesale wave ssh_foreman "$sshs" "export --root mtrx9/ --app mtrx9 --user mtrx9 --concurrency web=1 upstart /etc/init; restart mtrx9"

bakesale supports multi-server deployments; just pass an array of locations to the SSH commands.

deploy application

The deployment config is just a script, so to deploy, just execute it:

bash /Users/mlnw/Deployments/mtrx9/mtrx9.sh

At some point, you’ll be asked for the sudo password; this is necessary so Foreman can export it to upstart.

comments

bakesale isn’t particularly fast at deployment; there are a few reasons for this. One is that a fresh clone of the code repository is made each time; this is to provide absolute assurance that nothing’s been included by accident. Another reason for slowness is that the ‘cakebox’ is rsynced up to the server, rather than letting the server perform the clone; this is so the server doesn’t need to have Git installed, and doesn’t require the server to have access to the code repository. bakesale is just as opinionated as any other deployer; it doesn’t claim to be the best solution, only a solution built on a certain set of ideas (such as not having to pull hair out about server access to repositories, and forwarded SSH settings). It’s still a young project, so if you’d like to help improve it, please do get in contact—or just fork it and see.