Dockerizing Scala

Prerequisites

A working docker installation (boot2docker is fine)

Typesafe’s activator (or use ./activator found at the root of the git-repo — it will download the dependencies)

You can find/fork the sourcecode for all of the following on Github

Intro

The sbt plugin sbt-native-packager provides a really easy way to deploy your sbt project as a docker container.

In the following we will create a docker running a tiny scala helloWorld.

To use the native-packager we just add this line to your project/plugins.sbt

addSbtPlugin(“com.typesafe.sbt” % “sbt-native-packager” % “1.0.3”)

and in the build.sbt put:

enablePlugins(JavaAppPackaging)

enablePlugins(DockerPlugin)

Publishing

At this point our application is ready to run and you can deploy your docker by running

sbt docker:publishLocal

in your terminal. (This might take a while depending on how many layers you have to download)

Let’s verify the process by executing:

docker images

and we should see your newly created docker along with the base image (“java/latest”)

We can run it by executing:

docker run -it sbt-docker-example:1.0

and “Hello, world!” should be printed.

Optimizing/Customizing

There are a few settings the plugins exposes and we can override. You can find a list here.

Our current container occupies quite a lot of space ( > 800 MB) and we will try (and succeed 😛 ) to get it smaller. The current size is mostly because of the base image — “java:latest” — which is the packager’s default (it uses OpenJDK). We can override with any other image that provides a java installation.

For our example we will use frolvlad’s image which is significantly smaller and uses oracle-java. We add the following line to our build.sbt:

dockerBaseImage := “frolvlad/alpine-oraclejdk8”

Publishing the container now results in a much smaller image. However it’s broken…

sbt docker:publishLocal && docker run -it sbt-docker-example:1.0

results in:

env: can’t execute ‘bash’: No such file or directory

The native-packager depends on a working bash installation in the container. So let’s install it.

But first let’s take a look at what’s happening under the hood. We would like to know how the image is being built. Let’s take a look at the docker comands by running

sbt “show dockerCommands”

gives us:

List(Cmd(FROM,frolvlad/alpine-oraclejdk8), Cmd(WORKDIR,/opt/docker), Cmd(ADD,opt /opt), ExecCmd(RUN,List(chown, -R, daemon:daemon, .)), Cmd(USER,daemon), ExecCmd(ENTRYPOINT,List(bin/sbt-docker-example)), ExecCmd(CMD,List()))

If we blank out the scala parts (Cmd,List and ExecCmd) this looks very similar to comands we find in a Dockerfile.

We decide that the right place to install bash is right after the FROM command. Let’s override the dockerComands in our build.sbt by adding:

dockerCommands := dockerCommands.value.flatMap{
case cmd@Cmd(“FROM”,_) => List(cmd, Cmd(“RUN”, “apk update && apk add bash”))
case other => List(other)
}

We could look at the command sequence again but let’s just be bold an run the docker:

sbt docker:publishLocal && docker run -it sbt-docker-example:1.0

Et voilà, “Hello, world!” is back. And the new image size is now < 200 MB


Edit: We just published an ebook: “The Ultimate Guide to Code Review” based on a survey of 680+ developers. Enjoy!

About Codacy

Codacy is used by thousands of developers to analyze billions of lines of code every day!

Getting started is easy – and free! Just use your  GitHub, Bitbucket or Google account to sign up.

GET STARTED

Tutorials

Related Articles