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.