Building, tagging and pushing Docker images with Maven

A standard use case for Docker is to build a container to run a pre-built application so that the containerized app can be run on any Docker enabled host. The application and the container are sometimes developed and built separately. First the application is built, then a container is defined and built to include the application. However, it can be better to promote the Docker container to a first-class build artifact. That is, the build process always builds the deployed component and its container at the same time. This saves a manual build step and also ensures that the Docker container is always up to date with the latest application build. It allows us to easily develop and test against the Dockerized application directly Рevery build results in a new deployable container.

There are a number of ways to do this. This article looks at hooking the Docker tasks into the Maven build process.

The Docker Maven Plugin

The docker-maven-plugin from Spotify allows us to perform Docker operations from within Maven including build, tag and push images. The Docker operations can be run from command line through Maven but the real power comes from binding them to Maven build phases. This allows us to build new images on every build.

Build a Dockerfile

Building a Dockerfile from Maven is very straightforward. The plugin needs just a couple of configuration settings:

The <imageName> tag specifies the name of the image to be built, prefixed with your Docker Hub account name (hotblac in my case). ${project.artifactId} makes the image name the same as the Maven project name. The <dockerDirectory> location contains the Dockerfile and any resources required to build the image. If any build time resources need to be added (such as the built artifact), they can be specified in the <resources> section.

Running

will build the Maven project and the specified Docker image.

Creating images without a Dockerfile

It’s also possible to build a Docker image entirely based on Maven configuration properties. This is pretty neat if all you want is a simple Java 8 Alpine container to run an executable jar.

This example creates a Docker image containing files containing the project’s jar.¬†Just the <baseImage> and <entryPoint> command need to be specified. This is a basic configuration to build an executable jar and a simple container to run it. Again, the Docker image can be built with:

Binding to build phases

The real power of the plugin is here. By binding plugin goals to build phases, we can have Maven automatically build a Docker container on every build.

This simply triggers the plugin’s build goal whenever the Maven package phase is invoked.

We can also have Maven tag and push images to a Docker repo (Docker Hub for example) whenever the project is deployed:

The syntax here is a little odd so let me explain.

  • <image> is the name of the image to be taggged / pushed.
  • <newName> is an alias for the image name with the name of the repository (docker.io in this case) prepended. This tells the plugin where to push the image.
  • <serverId> is a lookup for a server configuration in your Maven settings.xml file. This allows the Docker repo username / password to be external to the Maven POM file which is likely to be public.
  • <pushImage> is a switch for the plugin’s tag goal which pushes the image after tagging it. This allows us to tag and push in a single execution step. There is also a push plugin goal, but it’s not capable of tagging the image.
  • Finally, the whole execution configuration is copied twice. This is a workaround for Docker’s special behaviour for the ‘latest’ tag. We actually want to tag and push two images: the first tagged with the project version ( ${project.version}) and the second with the latest tag. I can’t figure out a nicer way to do this.

Now, we can simply run

and Maven will:

  • Build your project’s main artifact (jar, war or whatever)
  • Build the Docker image from your Dockerfile or from the plugin’s configuration
  • Tag the image with the Maven project version number and latest
  • Push the image to the Docker repo

The result is that every time the project is deployed (or released), the Maven artifacts will be published to the Maven repo and the Docker images will be published to the Docker repo. The Docker repo will then contain runnable containers for every historic application build, with no additional steps in the build or release processes. Lovely.

Try it yourself!

This example worked particularly nicely with my Spring Boot application. Because Spring Boot packages web applications as self-contained executable jars rather than wars / ears to be deployed to an application server (such as Tomcat), deployment to Docker is very simple. We can simply copy the jar into a basic Java container and start it.

The source code for this example is available in the Spanners project, version 4.1 at GitHub. And of course, the resulting Docker containers are also available at Docker Hub.

 

2 Comments

  • Gene
    November 29, 2016 - 10:12 pm | Permalink

    I’m trying to follow your tutorial but it’s not working…
    I’m trying to push to this repository= https://hub.docker.com/u/chuangg/
    ————————————————————————————————————-
    Here is my pom.xml:

    4.0.0

    com.gene.app
    VendingMachineDockerPush
    1.0-SNAPSHOT
    jar

    Maven Quick Start Archetype
    http://www.gene.com

    org.apache.maven.plugins
    maven-jar-plugin

    com.gene.sample.Customer_View

    org.apache.maven.plugins
    maven-compiler-plugin
    3.1

    1.7
    1.7

    junit
    junit
    4.8.2
    test

    build-docker

    1.8.0

    com.spotify
    docker-maven-plugin
    0.4.5

    chuangg/gene_docker_example
    <!– chuangg/${project.artifactId} //This doesn’t work! –>
    java:openjdk-8-jdk-alpine
    [“java”, “-jar”, “/${project.build.finalName}.jar”]

    /
    ${project.build.directory}
    ${project.build.finalName}.jar

    build-image
    package

    build

    tag-image-version
    deploy

    tag

    chuangg/gene_docker_example
    docker.io/chuangg/fuckyes1
    docker-hub
    true

    tag-image-latest
    deploy

    tag

    chuangg/gene_docker_example
    docker.io/chuangg/fuckyes1:latest
    docker-hub
    true

    gc
    https://index.docker.io/v1/
    default
    chuangg

    http://mojo.codehaus.org/my-project

    chuangg
    chuangg
    https://index.docker.io/v1/

    ————————————————————————————————————————–
    Inside my C:UsersUsername.m2settings.xml, I added this to the top of the xml file:

    docker-hub
    https://index.docker.io/v1/
    chuangg
    ******
    gc.genechuang@gmail.com

    ——————————————————————————————————————-
    The console result:

    Uploading: https://index.docker.io/v1/com/gene/app/VendingMachineDockerPush/1.0-SNAPSHOT/VendingMachineDockerPush-1.0-20161129.211015-1.jar
    Uploading: https://index.docker.io/v1/com/gene/app/VendingMachineDockerPush/1.0-SNAPSHOT/VendingMachineDockerPush-1.0-20161129.211015-1.pom

    Build Failure

    Failed to execute goal org.apache.maven.plugins:maven-deploy-plugin:2.7:deploy (default-deploy) on project VendingMachineDockerPush: Failed to deploy artifacts: Could not find artifact com.gene.app:VendingMachineDockerPush:jar:1.0-20161129.205056-1 in gc (https:/index.docker.io/v1/)

  • Gene
    November 29, 2016 - 10:18 pm | Permalink
  • Leave a Reply

    Your email address will not be published. Required fields are marked *