Wednesday, February 24, 2016

Using Maven to produce Docker imarges

Docker is fast becoming the deployable artifact of choice. There are good reasons for this. It standardizes deployables across all technical stacks. This has the effect of streamlining work for a DevOps team working to manage large numbers of deployables. It allows developers to control the technical stack under which they deploy without and any tooling able to deploy Docker containers will just work. The reasons go on and on.

As a result, I've begun adding Docker artifacts to my standard builds for Java microservices and web applications. I begun using the Spotify Maven docker plugin.  Quite nicely done, although there were a few rough patches in getting everything to work correctly.  While the plugin itself is documented quite nicely, there's gaps in the documentation for the touch points between Docker and the Spotify Maven Docker plugin that created some headaches. This is especially true if you've a Windows development environment, which many of us have. I'm documenting this in my blog for myself as much as anybody else.

This entry uses my microservice example Moneta and its Spring Boot deployment build that can optionally produce a Docker artifact.  In fact the specific build I'm using as an example can be found here.  Pay attention to the Profiles section for id=Docker.

Using the Spotify Maven Plugin

This section guides you through adding the Spotify Maven plugin to your project.  It assumes that you've used Spring Boot, Dropwizard, or some other product to package your service into an executable jar.

Add the Spotify Maven Plugin to the build

Here's an example of the plugin include from my Moneta project.

    <!-- <targetPath>/</targetPath> -->

The plugin does provide special tags so that you can effectively code the dockerfile in the POM.   I prefer to keep the dockerfile separate; more readable and easier to maintain. You do need to include all resources needed by your dockerfile.  As an example, I've incldued my Spring Boot application jar.  All resources get bundled and transmitted to the Docker host where the docker file is run. If you dockerfile has COPY statements, those resources need to be included as resources in the POM.

Create a Dockerfile

The Spotify plugin can accept your docker build information a couple ways. You can put tags in your maven build to specify your base image, exposed ports, and entry point. Or you can create a Dockerfile and specify the directory that contains it.  I prefer this method as developers already using Docker will be familiar with the Dockerfile.  Documentation for Dockerfile syntax can be found here.

Include COPY commands for any build artifacts you want to include in the Docker image. These artifacts must also be listed as a Resource in the docker plugin section of your build.  I'll talk more about that later.

Add Preparation Tasks

I have two preparation tasks.  The Dockerfile doesn't easily support Maven variables such as ${version}, hence I had to copy my Spring Boot jar artifact into a constant name without the version number.  For example, copying file moneta-springboot-${project.version}.jar to moneta-springboot.jar that's referenced in my dockerfile.

I also checked to make sure that the environment variables DOCKER_HOST and DOCKER_CERT_PATH were set. The advantage is that developers get a more meaningful error message than "connection refused" if they are not set.  Obtaining values for these variables isn't obvious if you're using a Windows environment; see section "Verifying Docker Environment" below for details on this.

Consider using Spring profiles to make production of your Docker artifact optional. Adding a docker deployable adds significantly. It might not be needed with all local development builds and integration tests. 

Verifying Docker Environment

You must set DOCKER_HOST and DOCKER_CERT_PATH environment variables for the Spotify Docker maven plugin to work.  Figuring out proper values for these variables wasn't obvious on a Windows environment. Well, it's easy once you've figured out where to look.

The Docker Quickstart Terminal is nice enough to give you the IP address of the default docker machine it creates, but not the port. To obtain the value specify in your DOCKER_HOST setting, use the command below.  My docker host setting from this output is tcp://
    docker-machine ls

You can obtain the DOCKER_CERT_PATH value to use by executing the command below in your Quickstart terminal.  The output is also shown below.