Deploying a Play! app to Azure via Jenkins

Here at Wanari, we use Play Framework for many of our projects and maintain products on our own servers. Not too long ago, a client of ours requested to move such product to Azure Cloud. After a bit of research we found 3 options:

  1. We create a Virtual Machine on which we arrange the environment we use on our own servers. The advantage of this is that automatic installation would barely change at all. A big drawback though is that we would need to maintain one more machine.
  2. Another option is that we install the product as an App Service. The advantage is losing the maintenance of the machine, plus installation is easy: WAR file to be copied. Its drawback is that only with an external tool can we compile Play! into a WAR file. Moreover, if we were to start with different configurations we would have to compile it over and over again. If you are interested, here is a writing on this.
  3. The third solution – the one we chose – is somewhere between the two extremes above. Cloud Service gives us a machine with private access, where maintenance is not our task, though we can run our own applications. Its only drawback is that installation is a bit uneasy. There is a plugin (Azure Toolkit for Eclipse) with which you can easily install it through Eclipse (here’s a tutorial if you’re interested). But once it comes to automation, it doesn’t get through. However the plugin contains the necessary Ant tasks, but unfortunately we cannot run them even when allowed a long time.

This is how we’ve come to write this very own powershell script for installation. This post is about how we’ve solved the problem of installing a Play! 2.x application onto Azure Cloud Sevice via Jenkins.

deploying a play app to azure via Jenkins

What are we going to do?

There are three main tasks we need to take care of:

  • Creating code that can run from the app’s source code
  • Creating a CloudServicePackage from that
  • And finally starting the deploy

The starting point

  • We have the source of a Play! project in a git repository – this is what we want to put to Azure
  • We have a Jenkins server with the following:
  • We have a Windows machine that acts as a Jenkins slave and has:
  • We also have an Azure account

Preparations

In Azure, we’ll need:

  • a Cloud service
  • a Storage account
    • Azure portal > + New > Storage account
    • Deployment model: Classic
    • Performance: Standard

The Cloud service is where our app will run and we need the Storage (account) for the deploy process (the service will be copying the needed files from here).

Compiling a Play! application

This Jenkins job will do the following:

  • We download the project’s source code from the repository
  • We compile it with SBT
  • We correct the generated starter scripts

Creating the Jenkins job:

Jenkins > New Item> Freestyle project

  • Name it e.g.: ExampleProject-pack

General > Execute concurrent builds if necessary

  • JDK: choose which Java version we want to compile.

Secure Code Management > Git

  • Set reaching the repository
  • Choose the branch we want to compile e.g.: */master

Build > Add build step > Build using sbt

  • Set Actions: clean dist (this creates the launch-able app)

Build > Add build step > Execute shell

  • The following script needs to be corrected and it can be found here: workspace/target/universal/exampleproject-1.0-SNAPSHOT.zip > exampleproject-1.0-SNAPSHOT/bin/exampleproject.bat
    • “if not defined JAVAINSTALLED set JAVAOK=false” line (51.) needs to be deleted
    • “set “APP_CLASSPATH=%APP_LIB_DIR%” line (130.) needs to be shortened to set “APP_CLASSPATH=%APP_LIB_DIR%\..\conf\;%APP_LIB_DIR%\*”
  • For this we need to write a short script. This is what I’ve put together, because I was using a machine with Linux.

Note: if you’re interested in why you need this correction, you can read about it in an older post of Martin Sawicki.

Creating a CloudServicePackage (CSPKG)

In order for you to run anything in Azure’s system, you need to pack it in a .cspkg file. Not only your program, but various other configuration files are also in the .cspkg file. In order to make this file, you need a few things. Not to have to spend too much time on this, I’ve made a powershell script that does most of the work for us. If you are interested in the details of what this is, read more here.

Creating the necessary files:

Download the script (AzureCSPKGFilesGenerator_20161026_wanari.zip) and generate the files for your own project:

  • storageName: the name of the storage account we’ve made during preparations
  • storageAccessKey: primary access key to the storage account, you can look at it through the Azure portal
  • vmsize: defines the performance of the worker role (~virtual machine) that our application will be running on – you can learn about the sizes on the Azure website.
  • workerRoleName: the name of the worker role (~virtual machine) (it doesn’t have a lot of weight, could be anything without a special character)
  • zulu: to run a Play! app on our worker role, we need a Java JDK. Zulu JDK is recommended in Azure, so my script allows only that. Here you have to provide the exact file name of the version we would like. We can check the available versions on the ZuluJDK website.

We need a publishsettings file in order to give our script access to Azure. If we go to this page, it can be downloaded automatically (for now, don’t bother with the text on the page). Unpack the generated ZIP somewhere and change the credentials.publishsettings file to the one we just downloaded. Then, upload the whole thing to a separate repository so that Jenkins will be able to use it.

Now to the Jenkins job that will do the following:

  • Downloads the assisting files we just created
  • Copies the corrected ZIP from the job we created under the “Compiling a Play! applicaton” section
  • Creates the .cspkg file

Creating the Jenkins job:

Jenkins > New Item> Freestyle project

  • Name it e.g.: ExampleProject-cspkg

General > Restrict where this project can be run

  • Label Expression: the name of the Jenkins slave to which the AzureSDK has ben installed. For example, mine is windows-slave.
  • (Can be skipped if it’s been installed to the Jenkins’ default slave.)

Secure Code Management > Git

  • Set the repository’s reach we just created
  • Choos the branch we want to compile e.g.: */master

Build > Add build step > Copy artifacts from another project

  • Project name: name of the project you are compiling, pl.: ExampleProject-pack
  • Which build: Copy from WORKSPACE of latest completed build
  • Artifacts to copy: target/universal/*.zip (whatever we want to copy – this will be one ZIP file)
  • Target directory: ExampleRole/ (Write your WorkerRole’s name here!!)
  • Flatten directories

Build > Add build step >  Windows PowerShell

(The cspack.exe will be installed with the Azure SDK, find the exact route to this and set it here.)

Deploy to Azure from Jenkins

The deployment will be done by a script that I’ve written according to a post by Barni. Let’s download (AzureDeploy-v2_20161026_wanari.zip) our ZIP and copy it into the Jenkins slave from where we would like to do the deployment (this also has to be a machine with Windows & the Azure SDK installed).

The Jenkins job will the the following:

  • It copies the following files from the job we created in the “Creating the CloudServicePackage (CSPKG)” section:
    • credentials.publishsettings
    • ServiceConfiguration.cscfg
    • WindowsAzurePackage.cspkg
  • Starts the deployment script

Creating the Jenkins job:

Jenkins > New Item> Freestyle project

  • Name it! E.g.: ExampleProject-deploy

General > Restrict where this project can be run

  • Label Expression: the name of the Jenkins slave to which the AzureSDK has ben installed. for example, mine is windows-slave
  • (Can be skipped if it’s been installed to Jenkins’ default slave.)

Build > Add build step > Copy artifacts from another project

  • Project name: the compiling job’s name, e.g.: ExampleProject-cspkg
  • Which build: Copy from WORKSPACE of latest completed build
  • Artifacts to copy:  credentials.publishsettings

Build > Add build step > Copy artifacts from another project

  • Project name: the compiling job’s name, e.g.: ExampleProject-cspkg
  • Which build: Copy from WORKSPACE of latest completed build
  • Artifacts to copy:  ServiceConfiguration.cscfg

Build > Add build step > Copy artifacts from another project

  • Project name: the compiling job’s name e.g.: ExampleProject-cspkg
  • Which build: Copy from WORKSPACE of latest completed build
  • Artifacts to copy:  WindowsAzurePackage.cspkg

Build > Add build step >  Windows PowerShell

  • This script can be reached through “D:\Jenkins\scripts\AzureDeploy-v2.ps1” for me. Change it to where you copied it.
  • subscription: your Azure subscription’s name, containing the Cloud service and Storage account we created during preparations. You can find it through the Azure portal.
  • storageName: the name of the Storage account we created during preparations
  • cloudServiceName: the name of the Cloud service we created during preparations
  • deploymentLabel: this could be anything, later on might help you with identifying the deployment (e.g. you can have Jenkins include the number of the build in it.)

Linking the Jenkins jobs

Deployment would be all done by starting these three Jenkins jobs one after another (given that we did everything correctly). But to truly deploy the new version with one click, we need to make these jobs notify each other when they are done.

 

Edit the first job, for me it’s called ‘ExampleProject-pack’:

Post-build Actions > Add post-build action > Build other projects

  • Projects to build: the name of the job that creates the the cspkg e.g.: ExampleProject-cspkg

Then edit the second job (for me it’s ExampleProject-cspkg):

Post-build Actions > Add post-build action > Build other projects

  • Projects to build: name of the azure deploy job e.g.: ExampleProject-deploy

 

Finally, to make it look real good, install the Build Pipeline Plugin to Jenkins and produce one:

Jenkins > New Item> Build Pipeline View

  • Name it e.g.: ExampleProject

Select Initial Job: the name of the first job (for me it’s ExampleProject-pack)

The editing order doesn’t matter, but the logical structure does. Don’t mess up your jobs. – the ed.

After all this, clicking Run will start the entire process.

When the deployment script is done & has signaled “DEPLOY SUCCESSFUL” it doesn’t mean that it actually started in Azure. You can follow the Role’s condition on the portal, when it’s changed to “Running” then it’s done. Sometimes this takes 5 minutes, other times it takes 20 minutes, be patient.

Conclusions

This is what became the base of the process we nowadays use for our automatic installations. We further extended this in order to match various internal expectations (for example: included build numbers in the configuration files and now we create a separate configured compilation for the various systems (QA, UAT, PROD)), but this is another story it’s perhaps better explored in another blog post if you guys are interested…

If you have any ideas, recommendations or notes regarding this post or the scripts, don’t keep them to yourself!

Kristof Horvath

Kristof Horvath

Sleep is the coffee of the weak.

Latest posts by Kristof Horvath (see all)