cc by-sa flurdy

Start and stop a Scala application in production

How do you stop and start an application written in Scala
when in a production or a staging environment?

Problem

When you develop a Scala application you mostly use SBT, Gradle or Maven or similar build tools to build, test and run you application locally. Whether is is a Play! framework, Spray, Akka, etc. application, I certainly mostly depend on SBT.

Stopping and starting in SBT is easy, simply execute: run (or if using Revolver: re-start ).

But in a production environment and staging environments in between you cannot do the same. You have to package up a binary that somehow gets deployed and run on the servers. They must also be able to be stopped and started easily and automated.

(I answered a Stack Overflow question recently that is the basis of this document).

Contents
  1. Problem
  2. TL;DR
  3. Assumptions
  4. The hammer
    1. Daemon
    2. Init.d script
    3. Logging
  5. Extensions
      Daemon library
  6. To do
  7. Alternatives
  8. Feedback

TL;DR

Assumptions

The hammer

This solution consists of two parts.

One is to make a daemon class in the application that handles bootstrapping the application itself inside the JVM, and to gracefully shutdown inside the JVM as well.

The other is to create an init.d script so that it can be stopped and started as a service.

Daemon

You application and especially its frameworks may already supply a similar type of class so you may not need this.

In my example the application is a Spray web service that sits on top of Akka. Spray does supply its own example Boot class to stop and start the application. However it is not sufficient enough for my needs.

This solution does create a number of objects, classes, abstract classes and traits. This is so it is clear what does what and follows the conventions of small class and methods that do one thing. And allows further reuse and extension with more complexities.

Standard daemon classes

This daemon utilises the Apache Commons Daemon. Add this dependency to your build.sbt, Build.scala, pom.xml or whatever is relevant: "commons-daemon" % "commons-daemon" % "1.0.15"

Then we create a number of standard classes that can be shared between applications so it may be suitable as a library.

package com.example.myapplication.server import org.apache.commons.daemon._ trait ApplicationLifecycle { def start(): Unit def stop(): Unit } abstract class AbstractApplicationDaemon extends Daemon { def application: ApplicationLifecycle def init(daemonContext: DaemonContext) {} def start() = application.start() def stop() = application.stop() def destroy() = application.stop() } class ApplicationDaemon() extends AbstractApplicationDaemon { def application = new Application } object ServiceApplication extends App { val application = createApplication() def createApplication() = new ApplicationDaemon private[this] var cleanupAlreadyRun: Boolean = false def cleanup(){ val previouslyRun = cleanupAlreadyRun cleanupAlreadyRun = true if (!previouslyRun) application.stop() } Runtime.getRuntime.addShutdownHook(new Thread(new Runnable { def run() { cleanup() } })) application.start() }
Bespoke application daemon class

The actual composition and custom logic of the application is in the Application class below. If using Spray the Boot class can be removed or rather move the logic part of it into parts of the new daemon class.

package com.example.myapplication.server import akka.actor.{Props, ActorSystem} import spray.can.Http import akka.io.IO import com.example.myapplication.api.MyServiceActor class Application() extends ApplicationLifecycle with Logging { private[this] var started: Boolean = false private val applicationName = "MyApplication" implicit val actorSystem = ActorSystem(s"$applicationName-system") def start() { logger.info(s"Starting $applicationName Service") if (!started) { started = true val myService = actorSystem.actorOf(Props[MyServiceActor], "my-service") IO(Http) ! Http.Bind(myService, interface = "0.0.0.0", port = 8280) } } def stop() { logger.info(s"Stopping $applicationName Service") if (started) { started = false actorSystem.shutdown() } } }

The green section in the middle of the Application class above are the specifics for this application and will of course be different for each application. E.g. initialisers, orchestration etc

E.g. an Akka application would create its core orchestrating actors here. Spray does the same and as detailed above it also binds its http service to the desired port. If using cake pattern you could create the specific registry context here.

Init.d script

Now that your application is able to respond to daemon we need to set up the side that interacts with it outside of the JVM. In our case that is a standard Linux init.d script.

For this purpose we will use the tried and tested JSVC from the Apache commons-daemon.

Create a file as /etc/init.d/myapplication:

#!/bin/sh ### BEGIN INIT INFO # Provides: myapplication # Required-Start: $local_fs $remote_fs $network # Required-Stop: $local_fs $remote_fs $network # Should-Start: $named # Should-Stop: $named # Default-Start: 2 3 4 5 # Default-Stop: 0 1 6 # Short-Description: Control myapplication # Description: Control the myapplication daemon. ### END INIT INFO set -e if [ -z "${JAVA_HOME}" ]; then JAVA_HOME=$(readlink -f /usr/bin/java | sed "s:/bin/java::") fi JAVA_OPTS="-Xms512m -Xmx1024m" APP=myapplication PID=/var/run/${APP}.pid OUT_LOG=/var/log/${APP}/${APP}_out.log ERR_LOG=/var/log/${APP}/${APP}_err.log DAEMON_USER=yourserviceuser APP_LOG_CONFIG=/etc/mycompany/${APP}_logback.xml APP_CONFIG=/etc/mycompany/${APP}.conf APP_HOME=/opt/${APP} APP_CLASSPATH=$APP_HOME/${APP}.jar APP_CLASS=com.example.myapplication.server.ApplicationDaemon if [ -n "$APP_LOG_CONFIG}" ]; then JAVA_OPTS="-Dlogback.configurationFile=${APP_LOG_CONFIG} ${JAVA_OPTS}" fi DAEMON_ARGS="-home ${JAVA_HOME} -Dconfig.file=${APP_CONFIG} ${JAVA_OPTS} -pidfile ${PID}" DAEMON_ARGS="$DAEMON_ARGS -user ${DAEMON_USER} -outfile ${OUT_LOG} -errfile ${ERR_LOG}" DAEMON_ARGS="$DAEMON_ARGS -cp ${APP_CLASSPATH} ${APP_CLASS}" . /lib/lsb/init-functions case "$1" in start) log_daemon_msg "Starting ${APP}" cd ${APP_HOME} && jsvc ${DAEMON_ARGS} log_end_msg 0 ;; stop) log_daemon_msg "Stopping ${APP}" cd ${APP_HOME} && jsvc -stop ${DAEMON_ARGS} log_end_msg 0 ;; *) log_success_msg "Usage: {start|stop}" echo "Usage: {start|stop}" exit 1 ;; esac exit 0

Note the DAEMON_ARGS could be on one line, it was split up to fit better on this page.

You will need to make sure you have a suitable service user instead of myserviceuser. Also the following folders need to exist:

If you do not use Logback or do not have an external configuration file you can remove APP_LOG_CONFIG and/or APP_CONFIG. Otherwise you should also have these files present:

Stop and start

First make the init script executable: sudo chmod +x /etc/init.d/myapplication You can then start the application with: sudo service myapplication start and stop the application with: sudo service myapplication stop

Start on boot

For this application to automatically start on boot up in case of restarts etc, add the init script to the correct run level: sudo update-rc.d myapplication defaults Note this command changes dependant on your Linux distrobution.

Logging

You can monitor the logs and most likely initial typo errors at:

That is basically it. You can now start and stop a Scala application as a service on your production or staging environments.

Extensions

Naturally you will extend this set up to suit your needs.

Scripts and configuration version control

You probably want to add your init script to some sort of version control. I add mine to the same configuration repository that I also add the external configuration and logback configuration per environment.

Configuration management

Your servers might already be configured via Puppet, Chef, etc. So an obvious extension is to add the above configuration and scripts to the these configurations as well.

Daemon library

The daemon classes that are common between your applications can easily be extracted to a shared library.

I have made one for myself at github.com/flurdy/sander's lifecycle.scala.

Alternatives

There are other ways to run a Scala application on a remote server. There are different benefits and trade off with each though.

To do

systemd

Describe how to make the init script compatible with systemd.

Feedback & Contact

For typos and minor change suggestions, please clone this document repository at: github.com/flurdy/flurdy.com-docs, and send a pull request.

And/or alternatively please enhance the initial Stack Overflow answer.

For large changes perhaps create your own article and send me the link afterwards.

To hire me as a consultant / contractor contact me via www.eray.co.uk.

Or contact me via flurdy.com/contact.