How to Automate Docker Container Deployment with Maven

Containerization using Docker has revolutionized application development and deployment over the past few years. However, it also introduces many new complexities. Adopting Docker requires significant organizational and process changes. Developers struggle with a steep learning curve understanding containers. Many teams attempting to containerize applications find themselves managing a tangled mess of container build, deployment and management scripts.

This is where integrating Docker with Maven comes in. Maven is the most popular dependency management and build automation tool within Java ecosystem. Over 70% of Java developers use Maven as part of their toolchain. Automating container builds within the Maven process provides a cleaner way to adopt Docker without disrupting established development flows.

This guide aims to equip Java developers who are familiar with Maven to start automating Docker deployments. We will walk through a step-by-step example. The goal is to demonstrate an effective, real-world approach conforming to best practices for integrating Docker containerization into Maven-based build pipelines.

The Case for Containers

Let‘s first briefly discuss the appeal of Docker and containers.

Modern applications are increasingly deployed as distributed, cloud-native microservices architectures. This enables much needed agility and velocity within IT organizations. However, it also imposes massive complexity. Developers struggle with dependency conflicts, environment inconsistencies and configuration drift across many services deployed to diverse runtimes.

The following statistics highlight the scale of the problem:

  • 70% of companies are either using or evaluating containers according to RightScale
  • 80% of organizations utilizing microservices face frequent environment issues
  • 65% of developer time wasted due to configuration issues across environments

Containers encapsulate applications together with their entire runtime environment into isolated processes. This provides predictable, reliable and portable deployments. Containers stand up quickly and apply changes consistently across deployments:

Benefits of Using Containers

However, organizations can realize these benefits only if they effectively adopt containerization across their infrastructure. And this requires overcoming cultural and technological barriers.

Surveys indicate the top challenges with container adoption:

  • Struggling with organizational changes needed (62%)
  • Lack of container skills within teams (68%)
  • Manual container deployments (73%)

The reality is most developers find adopting Docker difficult. It often ends up becoming just another piece of tooling instead of an integral part of deployments. Developers wrestle with low-level Docker commands, scripts and custom infrastructure instead of focusing on application code.

Automating container builds within existing development toolchains helps overcome these adoption challenges.

Why Docker and Maven?

Docker has clearly emerged as the industry standard tool for containerization. Other complementary technologies like Kubernetes later provide orchestration and management at scale.

Maven dominates the Java world when it comes to build automation and dependency management. Here are some key statistics:

  • Over 70% of Java developers use Maven
  • 86% claiming higher productivity with Maven over other build tools
  • 800% increase in Maven Central downloads over last decade

Integrating Maven with Docker allows harnessing both technologies more effectively:

1. No context switching between tools

Developers can build and deploy Docker containers without leaving the familiar Maven toolchain. They avoid switching between separate scripts or command line interfaces during development.

2. Consistent containerization pipeline

Image building can be codified as part of the existing Maven continuous integration process enabling reliable flows from dev through to production.

3. Standardized process across teams

Placing container configuration within the Maven pom.xml imposes consistent conventions. This simplifies onboarding and knowledge sharing across developer teams.

4. Simpler path to container adoption

Packaging applications as containers becomes just another standard phase rather than a separate hurdle in the development toolchain.

Now that we have motivated the value proposition of integrating Docker and Maven, let‘s look at a step-by-step example.

Sample App Overview

To illustrate an effective real-world workflow, we will containerize a Java web application using Maven and Docker.

The application displays a simple responsive homepage with greeting messages. It leverages Spring Boot for the middleware framework and Bootstrap for front-end styling.

The page dynamically loads random greetings from an API endpoint:

Hello World App Demo

The project build and dependencies are managed by Maven. Out of the box, developers would execute mvn package to produce a Java archive which can be deployed to stand-alone Tomcat or Jetty servers.

We will enhance this setup to build Docker images as part of the Maven packaging phase. Let‘s get started!

Step 1 – Install Prerequisites

You will need the following installed to follow along with the examples:

Step 2 – Clone Sample App GitHub Repository

Let‘s clone the sample web app repository which has the starting point code:

git clone https://github.com/TestUser/docker-maven-demo.git
cd docker-maven-demo

The pom.xml file is configured for Spring Boot and the project builds a deployable JAR file by default.

Step 3 – Define Docker Image with Dockerfile

We need to describe how to package the application as a Docker image by creating a new Dockerfile:

# Start from base Java image
FROM adoptopenjdk/openjdk11:jre-11.0.15_1

# Add Author Info 
LABEL maintainer="TestUser"

# Copy generated jar 
COPY target/*.jar app.jar

# Run the jar  
ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-jar","/app.jar"]

This Dockerfile:

  1. Uses AdoptOpenJDK 11 as parent image
  2. Sets the maintainer label
  3. Copies the Jar build artifact
  4. Configures entry point to launch the application

When complete, the Maven build will produce a Docker image instead of just the deployable Jar file.

Step 4 – Copy Docker Resources

We now update pom.xml to copy Dockerfile and other assets into the Maven target directory:

<plugin>
  <artifactId>maven-resources-plugin</artifactId>
  <executions>
    <execution>
      <id>copy-docker-resources</id>
      <phase>validate</phase>
      <goals> 
        <goal>copy-resources</goal>
       </goals>
       <configuration>
         <outputDirectory>${basedir}/target</outputDirectory>
         <resources>
            <resource>
              <directory>src/main/docker</directory>
            </resource>
          </resources> 
       </configuration>
     </execution>
  </executions> 
</plugin>

This copies Dockerfile as well as other configuration files into target ready for Docker image build.

Step 5 – Invoke Docker Build and Tag Image

Next, we will utilize maven-antrun-plugin to directly call Docker commands to build and tag image:

<plugin>
  <artifactId>maven-antrun-plugin</artifactId>
  <executions>
    <execution>
      <phase>package</phase>
      <goals>
        <goal>run</goal>
      </goals>
      <configuration>
        <target>
          <exec executable="docker">
            <arg value="build"/>
            <arg value="-t"/>
            <arg value="${project.artifactId}:${project.version}"/>
            <arg value="target"/>
          </exec>
        </target>
      </configuration> 
    </execution>
  </executions>  
</plugin>

This executes docker build as part of the Maven package phase. The image is tagged with artifact ID and version defined in pom.xml.

Step 6 – Push Image to Docker Registry

For distributing the image to other teams, we should push it to a registry. DockerHub is the default public registry to host Docker images.

First, let‘s configure Maven to authenticate with our Docker ID:

<properties>
    <docker.username>testuser</docker.username>
    <docker.password>pa55w0rd!</docker.password> 
</properties>

<servers>
  <server>
    <id>docker-hub</id>
    <username>${docker.username}</username>
    <password>${docker.password}</password>
  </server>
</servers>

Next, we add the push command by updating maven-antrun-plugin:

<exec executable="docker">
  <arg value="tag"/>
  <arg value="${project.artifactId}:${project.version}"/>
  <arg value="docker.io/${docker.username}/${project.artifactId}:latest"/>
</exec>

<exec executable="docker">
  <arg value="push"/>
  <arg value="docker.io/${docker.username}/${project.artifactId}:latest"/>  
</exec>

We now have a complete automated Docker image build pipeline integrated with Maven!

Step 7 – Build and Run Container

With above configuration in place, we can run:

mvn clean package

This will first compile Java classes, then build the Docker image and push it to DockerHub.

To launch the published containerized application locally:

docker run -p 8080:8080 testuser/docker-maven-demo:latest  

The application will be accessible on http://localhost:8080.

Automating the entire pipeline through Maven allows simplified adoption without disrupting existing development flows.

Advanced Customizations

There are many options for further customizing and extending this automated Docker setup through Maven.

Parameterizing Configurations

We can externalize container configurations, tags, registry URLs etc. into pom.xml properties. These can then be overridden via Maven command line arguments or environment variables.

For example:

<properties>
  <container.tag>latest</container.tag>
</properties>

<docker.image>myregistry.com/${project.artifactId}:${container.tag}</docker.image>

Build with:

mvn package -Dcontainer.tag=release-2.3

Setup for Different Environments

Separate Maven profiles can define custom configurations for each environment:

<profiles>
  <profile>  
    <id>development</id>
    <properties>
      <docker.registry>dev-docker-registry</docker.registry> 
    </properties>
  </profile>

  <profile>
    <id>production</id> 
    <properties> 
       <docker.registry>prod-docker-registry</docker.registry>
    </properties>
  </profile>
</profiles>

<docker.registry>${docker.registry}</docker.registry>

Now build images with:

mvn package -P production

Similarly, we can also have granular control over environment variables, configuration secrets etc.

Integration with CI/CD Pipelines

There are Maven plugins available for integrating with most leading DevOps pipelines tools:

These plugins enable setting up continuous build of Docker images on code commits or automated deployments when tests pass.

Container Registry Authentication

Instead of hardcoding Docker authentication, utilize Maven‘s encrypted password storage. Create secure server credentials:

mvn --encrypt-password mypassword
{lR9zaidavNLxm74Ec7M3JGz1TLqdBjJupvkZyBm9+yQ=}

Configure encrypted passwords within pom.xml:

<servers>
  <server>
    <id>docker-registry</id>
    <username>testuser<username>
    <password>{lR9zaidavNLxm74Ec7M3JGz1TLqdBjJupvkZyBm9+yQ=}</password>
  </server>  
</servers>

Zero-Downtime Deployments

For mission critical systems, utilize Docker healthchecks, multiple replicas etc. to avoid application downtime while redeploying containers:

Zero Downtime Deployment

There are open source tools like Fabric8 that integrate well with Maven for enabling advanced container deployments.

Container Orchestration and Management

As an organization scales to hundreds of container instances across many hosts, Kubernetes becomes vital for lifecycle management.

The Kubernetes Maven plugin allows deploying Docker images built through Maven onto a Kubernetes cluster. Use alongside CI/CD systems for complete end-to-end automation.

Conclusion

In this comprehensive guide, we walked through containerizing a sample Java application using Maven and Docker – including building images and pushing to an registry.

Key highlights:

  • Motivation for adopting Docker containers and microservices
  • Overview of Maven and Docker platform statistics
  • Step-by-step tutorial containerizing a Java web application
  • Detailed configurations for automating build pipeline with Maven
  • Options for customization and production deployments

Maven and Docker both provide immense value individually. Integrating them unlocks even more potential. It paves a gradual adoption path for developers by fitting containers within existing toolchain.

If you found this guide useful, do check out my other articles on Docker, Kubernetes and cloud-native technologies. Let me know what else you would like to see covered!

Similar Posts