Adventures in HttpContext All the stuff after 'Hello, World'

Akka Clustering with SBT-Docker and SBT-Native-Packager

Since my last post on akka clustering with docker containers a new plugin, SBT-Docker, has emerged which allows you to build docker containers directly from SBT. I’ve updated my akka-docker-cluster-example to leverage these two plugins for a smoother docker build experience.

One Step Build

The approach is basically the same as the previous example: we use SBT Native Packager to gather up the appropriate dependencies, upload them to the docker container, and create the entrypoint. I decided to keep the start script approach to “prep” any environment variables required before launching. With SBT Docker linked to Native Packager all you need to do is fire

docker

from sbt and you have a docker container ready to launch or push.

Understanding the Build

SBT Docker requires a dockerfile defined in your build. I want to pass in artifacts from native packager to docker. This allows native packager to focus on application needs while docker is focused on docker. Docker turns into just another type of package for your app.

We can pass in arguments by mapping the appropriate parameters to a function which returns the Dockerfile. In build.spt:

// Define a dockerfile, using parameters from native-packager
dockerfile in docker <<= (name, stagingDirectory in Universal) map {
  case(appName, stageDir) =>
    val workingDir = s"/opt/${appName}"
    new Dockerfile {
      //use java8 base box
      from("relateiq/oracle-java8")
      maintainer("Michael Hamrah")
      //expose our akka port
      expose(1600)
      //upload native-packager staging directory files
      add(stageDir, workingDir)
      //make files executable
      run("chmod", "+x", s"/opt/${appName}/bin/${appName}")
      run("chmod", "+x", s"/opt/${appName}/bin/start")
      //set working directory
      workDir(workingDir)
      //entrypoint into our start script
      entryPointShell(s"bin/start", appName, "$@")
    }
}

Linking SBT Docker to SBT Native Packager

Because we’re relying on Native Packager to assemble our runtime dependencies we need to ensure the native packager files are “staged” before docker tries to upload them. Luckily it’s easy to create dependencies with SBT. We simply have docker depend on the native packager’s stage task:

docker <<= docker.dependsOn(com.typesafe.sbt.packager.universal.Keys.stage.in(Compile))

Adding Additional Files

The last step is to add our start script to the native packager build. Native packager has a mappings key where we can add files to our package. I kept the start script in the docker folder and I want it in the bin directory within the docker container. Here’s the mapping:

mappings in Universal += baseDirectory.value / "docker" / "start" -> "bin/start"

With this setting everything will be assembled as needed and we can package to any type we want. Setting up a cluster with docker is the same as before:

docker run --name seed -i -t clustering
docker run --name c1 -link seed:seed -i -t clustering

It’s interesting to note SBT Native Packager also has docker support, but it’s undocumented and doesn’t allow granular control over the Dockerfile output. Until SBT Native Packager fully supports docker output the SBT Docker plugin is a nice tool to package your sbt-based apps.