Scala, Akka and Docker, oh my!

Scala and Docker, living together

Mario Camou / @thedoc

Who am I?

  • Working with Scala for a few years
  • Currently working at eBay EPD
  • Recently moved a project to Docker


  • Introduction to Docker
  • Docker and Scala
  • Docker and Akka

Introduction to Docker

  • What is Docker?
  • Why Docker?
  • Docker basics

What is Docker?

"A way to pack, ship and run any application as a lightweight container"
- From the Docker website
A way for developers to package any application to run on (almost) any infrastructure

What is a Docker image?

A way of packaging applications

  • Self-contained
  • Isolated
  • Centrally stored
  • Snapshot of a container

What is a Docker container?

  • A running instance of an image
  • Isolated set of resources
    • Processes
    • Networking
    • Filesystem
  • OS-agnostic*
  • Lighter than a VM
  • Usually runs a single process

Why Docker?

Dependency and environment management

An image contains all dependencies
  • JVM
  • Libraries (including native)
  • Scripts
  • Configuration files

...and is created from a text description
  • Infrastructure as code
  • Controlled in VCS

Standardized infrastructure

Independent of:
  • Underlying OS
  • Linux distro and packages

No more:
  • "it works on my machine"
  • "it works on QA but not in production"

Ephemeral, immutable infrastructure

  • Do not update: destroy and recreate!
  • Machines should be able to come and go
  • All changes are in VCS
  • No hot-fixes
  • No ad-hoc package updates

Docker basics

Installing Docker

Docker must be installed on all machines that will generate or use Docker images:
  • Developers
  • CI
  • Production hosts

Installing Docker

On Linux:

                # apt-get install docker
(or equivalent command)

On OS/X and Windows:

Install Docker Toolbox which includes docker-machine.

Will use a VirtualBox VM to host the Docker containers

Building a Docker image

Creating a Dockerfile
FROM java:openjdk-8-jre
RUN /usr/bin/apt-get update && \
    /usr/bin/apt-get install -y locales git postgresql-client && \
    /usr/bin/apt-get clean && \
    /usr/sbin/locale-gen en_US en_US.UTF-8 && \
    /usr/sbin/useradd -r -s /bin/false -d /srv appuser
COPY target/scala-2.11/myApp.jar /srv/myApp.jar
COPY bin/ /srv/
COPY lib/etc /srv/etc
RUN chown -R myUser /srv && chmod 0544 /srv/
USER myUser

Building an image

$ docker build .
Sending build context to Docker daemon  78.2 MB
Sending build context to Docker daemon
Step 0 : FROM java:openjdk-8-jre
---> 45a4bb374fcb
Step 1 : RUN /usr/bin/apt-get update && /usr/bin/apt-get install -y locales git postgresql-client && /usr/bin/apt-get clean && /usr/sbin/locale-gen en_US en_US.UTF-8 && /usr/sbin/useradd -r -s /bin/false -d /srv appuser
---> Using cache
---> 01b419a0e4fe
Step 2 : WORKDIR /srv
---> Using cache
---> 0b4060deee6d
Step 3 : COPY target/scala_2.11/myApp.jar /srv/myApp.jar
---> Using cache
---> 5a2fdbe7988f
Step 4 : COPY bin/ bin/
---> Using cache
---> d298418084c5
Step 5 : COPY lib/etc etc
---> Using cache
---> 667c3afe09c5
Step 6 : RUN chown -R myUser /srv && chmod 0544 /srv/
---> Using cache
---> 239fa5c77ad0
Step 7 : USER myUser
---> Using cache
---> 848a559a8550
Step 8 : ENTRYPOINT /srv/
---> Using cache
---> 2a31f1128eee
Successfully built 2a31f1128eee

Running an image

$ docker run 2a31f1128eee
$ docker push myOrg/myApp
$ docker pull myOrg/myApp
$ docker run myOrg/myApp:latest


Images should be ephemeral and immutable

  • Centralized logging
  • Network data sources
  • Config files in build
  • Env vars for dynamic config


Docker containers use a virtual net interface
  • Can't listen to the outside world
  • Can't talk to other containers
  • Docker itself adds network overhead

Command-line to expose ports and connect containers

Docker and Scala

  • Dockerizing a Scala application
  • Integrating Docker with sbt
  • Testing with Docker
  • Docker and Akka

Dockerizing a Scala application

Selecting a base image

  • java:openjdk-8
  • Official java image
  • Based on Ubuntu LTS and OpenJDK
  • JDK and JRE-based for Java 6, 7, 8 and 9

Selecting a base image

There are other Java images using other Linux distributions (Alpine, CentOS). Some of them also include the Oracle JDK/JRE

... or roll your own using Alpine or Ubuntu

Selecting a base image

FROM java:8
FROM anapsix/alpine-java

Create a user, etc.

RUN /usr/bin/apt-get update && \
    /usr/bin/apt-get install -y locales git postgresql-client && \
    /usr/bin/apt-get clean && \
    /usr/sbin/locale-gen en_US en_US.UTF-8 && \
    /usr/sbin/useradd -r -s /bin/false -d /home/appuser appuser

Add your code

  • Use COPY to add any .class or .jar files
  • It's easiest to create a Fat JAR with sbt-assembly or sbt-native-packager
    COPY target/scala-2.11/myApp.jar /home/appuser/lib

Managing configuration files

  • Separate Dockerfiles/images per config
  • Single image, different config files
  • Single config file
COPY bin/ /srv/
COPY etc /srv/etc
RUN chown -R myUser /srv && chmod 0544 /srv/
USER myUser

Creating and publishing the image

$ docker build
$ docker push myOrg/myApp

Integrating Docker with sbt


  • Because we can!


  • Easier integration into build pipeline
  • Running integration tests


An sbt plugin that:
  • Creates a Dockerfile
  • Creates the Docker image
  • Pushes the image to a registry

Setting the image name

imageNames in docker := Seq(
    namespace = Some("mcamou"),
    repository = name.value,
    tag = Some(s"v${version.value}")
    namespace = Some("mcamou"),
    repository = name.value
    tag = Some("latest")

Some useful vals

val artifact = (outputPath in assembly).value
val baseDir = "/srv"
val preInstall = Seq(
  "/usr/bin/apt-get update",
  "/usr/bin/apt-get install -y locales git postgresql-client",
  "/usr/bin/apt-get clean",
  "/usr/sbin/locale-gen en_US en_US.UTF-8",
  s"/usr/sbin/useradd -r -s /bin/false -d $baseDir myUser"
).mkString(" && ")

Configuring the image

dockerfile in docker := {
  new Dockerfile {
    copy(artifact, s"$baseDir/app.jar")
    copy(new File("bin/"), s"$baseDir/")
    copy(new File("local/etc"), "$baseDir/etc")
    runRaw("chown -R myUser $baseDir && chmod 0544 $baseDir/")

Integrating with sbt-assembly

Ensure assembly always runs before docker:
                docker <<= (docker dependsOn assembly)

Creating and publishing the image

sbt> docker
sbt> dockerPush

Caveats and recommendations

  • Create a fat JAR: sbt-assembly or sbt-native-packager
  • In non-Linux platforms, start up docker-machine and set up the environment variables before starting up sbt
    $ docker-machine start theMachine
    $ eval $(docker-machine env theMachine)
Dockerizing your Scala apps with sbt-docker

Testing with Docker

Integration testing

  • Testing interactions with external systems
    • Database
    • Message queue
    • External web services
    • ...
  • Setup is often complicated, especially on dev boxes

Integration testing

Create Docker image(s) containing external resources
  • Start up the containers
  • Wait for them to start
  • Run the tests
  • Stop the containers
Can we automate all of this?

Enter docker-it-scala

Integration testing with docker-it-scala

Defining the container
trait DockerMongodbService extends DockerKit {
  val DefaultMongodbPort = 27017
  val mongodbContainer = DockerContainer("mongo:2.6.5")
    .withPorts(DefaultMongodbPort -> None)
       _.contains("waiting for connections on port")
    .withCommand("mongod", "--nojournal", "--smallfiles", "--syncdelay", "0")

  abstract override def dockerContainers: List[DockerContainer] =
    mongodbContainer :: super.dockerContainers

Integration testing with docker-it-scala

MongoDB example: Writing your tests
class MyMongoSpec extends FlatSpec
  with Matchers
  with DockerMongodbService {
  // Test assumes the MongoDB container is running

Docker and Akka

  • Why?
  • Caveats
  • Akka testing

Docker and Akka


  • Docker is great for elasticity
    • Fast startup
    • Immutable, ephemeral images
    • Orchestration (Amazon ECS, Kubernetes, Docker Swarm)
  • Akka is too
    • Location transparency
    • Clustering

A marriage made in heaven?

Caveats: Remoting

  • Akka < 2.4 can't use NAT (forces use of --net=host)
  • In 2.4, use:
    akka.remote.netty.tcp.bind-hostname // Internal
    akka.remote.netty.tcp.hostname  // External
FAQ: Why are replies not received from a remote actor?

Caveats: Clustering

Problem: Finding the seed nodes
  • docker -link or docker network
  • Docker Compose
  • Roll your own (cloud provider's SDK, Consul, etcd, ...)
  • ZooKeeper (akka-zk-cluster-seed)
  • Typesafe ConductR
Problem: Finding the seed nodes


Akka testing

  • Persistence setup
  • Cluster setup
  • Response when server is down

Use reactive-docker to control the Docker daemon

reactive-docker example

implicit val docker = Docker("localhost")
implicit def timeout: Duration = Duration(20, SECONDS)
val containerName = "reactive-docker"
val cmd = Seq("/bin/sh", "-c", "while true; do echo hello world; sleep 1; done")
val cfg = ContainerConfig("busybox", cmd)
val (containerId, _) = Await.result(docker.containerCreate("myOrg/myApp", cfg, Some(containerName)), timeout)
val started = await(docker.containerStart(containerId))
val stopped = await(docker.containerStop(containerId))

More info: Using Docker, Scala and Akka for Integration Tests presentation by Andreas Gies



  • Simplifies application deployment
  • Can be integrated with sbt
  • Combined with Akka can increase elasticity
  • Can be useful for integration testing
Thank you!

Any questions?