Automated Infrastructure with Pallet and Clojure
Monday, May 05, 2014At Ona we are rebuilding our data management platform. We are starting with a light weight front-end that will serve up content pulled from the REST API of our current application. We are aiming to have the back-end in Clojure, the front-end in ClojureScript, and the infrastructure in Clojure using Pallet. We are excited to have a single (and a great) language handle all of these responsibilities.
We are still at a very early stage but we are a distributed team and like to have our apps on development boxes as we go. This allows us to share a common reference point, give mini-demos, and QA each other’s changes. Like Fabric for Python and Capistrano for Ruby, Pallet let’s us do quick deploys of the latest master or branch code.
Even better, Pallet let’s us write Clojure to bring up new clusters, similarly to Puppet, Chef, or Ansible – but in Clojure. We deploy to EC2 on AWS and are glad to avoid spending time mucking around in the AWS GUI. A succinct pallet file specifies the instance, the web application, and the deployment. Putting the current code online and bringing up a server (if one doesn’t already exist) is a single command:
lein do uberjar, with-profile +pallet pallet up \
--phases install,configure,deploy
This tells Leiningen to first create an uberjar
,
which puts all of our app’s dependencies in a single jar file. It then uses
the pallet
profile to install
, configure
, and deploy
our application. This
command is idempotent, making it easy to push the latest jar up.
A nuance we did not anticipate is that you cannot output logs to stdout
in a
Jetty app. This is not particularly surprising, but using stdout
was a development
configuration that we had not yet bothered to abstract.
For now we are handling this with the below middleware wrapper:
(defn wrap-with-logger [handler verbose?]
(if verbose?
(logger/wrap-with-logger handler "/dev/stdout")
(fn [request] (handler request))))
This does the normal logging if verbose?
is true and otherwise does nothing.
When you run lein ring server-headless
a handler is called which sets verbose?
to true. When you run the app through java -jar ...
, as in our pallet configuration,
verbose?
is set to false.
The ona-viewer project is a work-in-progress and we would welcome any feedback. Check it out on github.