In this post I’ll describe how to automate the deployment of a new stable version of a Luminus Clojure web application and limit the total size of data to upload via network to the server.

I’ve developed the site4 web application based on the Clojure web framework Luminus. The sources are put under revision control on a local Git repository. Commits are then pushed to an internet Git provider. The application lives on a VPS server.

Luminus is a web micro-framework for Clojure. It’s based on consolidated web development practices and popular Clojure libraries. Clojure is a functional language for the JVM. Clojure is a challenge for a procedural, OOP developer, but it’s worth studying it. Immutable data structures is an important option to develop secure and scalable concurrent applications. Some ideas are useful even when you return programming with not functional languages.

Developed the Luminus application, I followed the good instructions in the Luminus website to go on line configuring a VPS Linux server with java ed nginx frontend.

The server was configured for executing the uberjar, a standalone executable of the site4 application, in the /opt/deploy directory. The application can be started, stopped, restarted via standard systemctl command

The uberjar size of half hundred MB or more could require several time to upload via common DSL. To obviate the necessity to upload the entire uberjar for a little change in the application, between various possibilities, I opted to build the uberjar in the VPS server itself, pulling the sources from the remote Git, building the uberjar, installing in the deploy directory and restarting the application.

First times, I did this by hand, then I’ve automated using Fabric/fabfile.

Fabric is a simple but powerful alternative to Chef, Puppet or Ansible to automate the deployment process.

What I needed is a Fabric deploy task that execute in sequence the following tasks

  • source – clone the application or pull the source differences from the Git repository
  • build – create the uberjar
  • stage – store a backup of the previous uberjar and prepare the new one for installation
  • install – install the new uberjar and restart the application

First of all, I define some auxiliary functions: app_name to return the app name and git_url to return the url of the remote Git repository

env.app_name = 'site4'
env.git_url = 'https://username:password@bitbucket.org/account/%s.git'

def app_name():
    return env.app_name

def git_url():
    return env.git_url % app_name()

Then I define the source function that detects if in the user stage directory does exist the app Git repository. If it exists then update the app sources, otherwise download the repository

def source():
    with cd('stage'):
        if exists(app_name()):
            with cd(app_name()):
                # run('git pull')
                run('git pull %s master' % git_url())
        else:
            run('git clone %s' % git_url())

The build function is quite simple

def build():
    with cd('stage/%s' % app_name()):
        run('lein uberjar')

The stage function prepare the environment for the installation making a backup of the current app and setting the ownerships of the new app

def stage():
    with cd('stage'):
        # Backup in the 'stage' directory of the current application
        sudo('cp -a /opt/deploy/%s.jar %s-rollback.jar' % (app_name(), app_name()))
        # Copy in stage of the new application
        sudo('cp -a %s/target/uberjar/%s.jar .' % (app_name(), app_name()))
        sudo('chown deploy:deploy %s.jar' % app_name())

All is ready, so here it is the install function that copies the uberjar in the deploy dir and restart the application.

def install():
    with cd('stage'):
        sudo('cp -a %s.jar /opt/deploy/%s.jar' % (app_name(), app_name()))
        sudo('systemctl restart %s' % app_name())

The four previous tasks are executed by deploy function

def deploy():
    source()
    build()
    stage()
    install()

Here is the full source of the fabfile.py

When I need to deploy the current version of the application pushed on the remote Git repository, I run the following command

fab vps_server.domain.it deploy

If you configured your host in an SSH Config File with name vps_server, you can run

fab -H vps_server deploy

If you'd prefer, you can run the single tasks one by one, i.e. the first one

fab -H vps_server source

to control each phase of deployment.

That’it!
I hope this article could be interesting.
Ciao!!!
Gianni Di Sanzo