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.