Running Play Framework in AWS

Some months ago, at Another Place Productions we evaluated different frameworks and tools to start building our backend services.

For us, Play Framework was a clear winner for many reasons, but mainly:

– Written in Scala but with an up-to-date Java API so you can plug any of the existing battle tested Java libraries
Easy to use for people who come from web development using interpreted languages
– The JVM is blazing fast (even if we like to make jokes out of it!)
– Actively maintained and well documented

After some weeks developing code, it was time to deploy our first alpha to an AWS EC2 instance and I discovered that apparently there were no “best practices” to achieve it.

And after reading some blog posts we decided to go this way:

Build an artifact

If you look at the Play Framework documentation you will see different ways to create a standalone distribution.

As of version 2.3, the recommended way to get started with Play is via a Typesafe Activator template. Activator is a bit out of the scope of this post but IMHO it is a brilliant way to bootstrap JVM projects and it comes with a set of tools on top of SBT for ease of testing, packaging and many others.

And with the idea of building the easiest deploy possible artifact we went with

activator universal:package-zip-tarball

which creates a tar.gz with a binary version of the application with all the jar files added to it enabling us to run Play without having to install any other dependencies in that instance apart from the JVM itself

We have integrated this task in our Jenkins CI server so every time we push code, we run the test suite and if all tests pass we generate this tar.gz file and upload it to a releases S3 bucket.

We have also added the excellent SBT Git tool so that every artifact name will use the Git commit hash

This way, you have a centralised repository of releases accessible from every instance with the appropriate AIM role, ready to be downloaded, uncompressed and run!

Running Play Framework as a daemon

The whole idea of artifacts is that they should be deployable and runnable without installing anything else. We create a small init file so that our application will run as a service (this is for Ubuntu, so you may have to adapt some things to your distro)

start on runlevel [2345]
stop on runlevel [!2345]
 
env PLAYFWDIR=/opt/{{ version }}
env PLAYENV={{ env }}
 
script
  $PLAYFWDIR/bin/{{ app }} -Dhttp.port={{ port }} -Dlogger.resource=$PLAYENV-logger.xml -Dconfig.file=$PLAYFWDIR/conf/application-$PLAYENV.conf -J-server -mem {{ jvm_memory }} >> /var/log/playframework/application.log
end script

As you can see, there are several variables here which should be filled by your automation tool like Ansible or whatever you prefer:

version: This is the name of the artifact generated (which should be uploaded / deployed to /opt). If you are using sbt-git this will be your app name + your git commit hash
app: This will be your application binary
port: Play framework runs under port 9000 but you can change it here
jvm_memory: This is the max JMX memory. By default it is set to 1G which is fine but it makes Play Framework not able to run on a micro instance. Reducing this value we can have micro instances running our pet projects or test environments.
env: This is used to load different configs for different environments, using the HOCON syntax and overriding the defaults as explained here

And you? How are you running Play Framework in production?

You may also like...