Building Docker images with SBT

07/26/2015

A typical way to setup Jenkins is to connect it to your source repository (e.g. with the Git Plugin), run your tests after each commit, and then build a package for deployment when the tests pass. We’ll use SBT’s sbt-native-packager for this last step, which allows you to package your applications in numerous different formats including zip, deb, rpm, dmg, msi, and docker.

To setup sbt-native-packager to publish you Docker images you need to add sbt-native-packager to your project and specify your Docker repo in your build.sbt. E.g. dockerRepository := Some("quay.io/myorganization"). You now need to setup the credentials to publish to your Docker repository. This typically goes in ~/.dockercfg. You can place the .dockercfg in the Jenkins home directory, which on Ubuntu will by default be located at /var/lib/jenkins/.

The next thing you need to setup is the build step to build the Docker image. This can be a bit confusing because Jenkins has build steps and post-build actions and it’s not completely clear what the difference is. I’ve found that the build step does what we want. You can use the Jenkins SBT Plugin to run your sbt tests with each commit. Now, to build a Docker image you can click “Add build step” followed by “Build using sbt” and in the Actions field enter “docker:publish”

Another thing you may need to deal with is having SBT sub-projects. E.g. let’s assume you have a project named “myproj”, which depends on other libraries. You can set "project myproj" docker:publish in the Jenkins build step so that SBT switches to your myproj project before building the docker image, so that it won’t try to run docker:publish on your subprojects. If you’re using SBT’s aggregation to compile or run the tests of these sub-projects when doing the same for myproj, you’re probably going to want to disable this for publishing the Docker image. You can do this by adding the setting aggregate in Docker := false to your build.sbt:

lazy val myproj = project
    .enablePlugins(DockerPlugin, GitVersioning, PlayJava, SbtWeb)
    .dependsOn(subproj).aggregate(subproj)
    .settings(
      aggregate in Docker := false  // when building Docker image, don't build images for sub-projects
    )

Note that you’ll have to handle garbage collection of old Docker images. Docker has this on their roadmap. Until then, I recommend Spotify’s Docker GC.

Be Sociable, Share!