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
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
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:firstname.lastname@example.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())
build function is quite simple
def build(): with cd('stage/%s' % app_name()): run('lein uberjar')
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
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.
I hope this article could be interesting.
Gianni Di Sanzo