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.