DEV Community

Michel Sánchez Montells
Michel Sánchez Montells

Posted on

Accelerate Maven Application Builds: Maximizing Efficiency with Docker Volumes for Maven Repository Sharing

In today's software development landscape, dockerization has become a fundamental requirement for every project. It eliminates the notorious "it works on my laptop" dilemma, promotes seamless collaboration within teams, and enables hassle-free deployment across various environments using tools like Docker Compose. Our goal is to achieve all of this while maintaining an easy-to-use and highly efficient development environment.

Now, let's dive into dockerizing a Java Maven application and explore the challenges we face. Traditionally, developers often rely on using a Maven image as the base image for their Docker setup, such as the popular Maven 3.6.0 with JDK 8 on Alpine Linux:

Then we want to dockerize a Java Maven application.

We should to be able of

build:

docker build -t my-java-app .
Enter fullscreen mode Exit fullscreen mode

run:

docker run my-java-app
Enter fullscreen mode Exit fullscreen mode

One of the prevalent approaches is to have a maven image as a docker base image.

FROM maven:3.6.0-jdk-8-alpine
Enter fullscreen mode Exit fullscreen mode

include the pom

COPY pom.xml .
Enter fullscreen mode Exit fullscreen mode

include the code

COPY src ./src
Enter fullscreen mode Exit fullscreen mode

build the app

mvn package
Enter fullscreen mode Exit fullscreen mode

However, this approach poses a significant drawback. Every time we execute the docker build command, Maven will re-download the project's dependencies and rebuild the application from scratch if there are any changes in the dependency list or the source code within the src directory. This process is not only inefficient but also tedious and painfully slow.

But fear not! There's a better way to handle this.

By executing the dependency downloads inside the container and leveraging the power of Docker volumes, we can share the Maven repository between the host and the container. Here's how we can achieve it:

We can do it better.

Execute the download dependencies inside the container and share with
docker volume the maven repository between host and container.

  • Yes, Inherit from maven
FROM maven:3.6.0-jdk-8-alpine
Enter fullscreen mode Exit fullscreen mode
  • Copy pom and code
COPY pom.xml .
COPY src ./src
Enter fullscreen mode Exit fullscreen mode
  • Use docker entrypoint.sh pattern
COPY docker-entrypoint.sh /docker-entrypoint.sh
Enter fullscreen mode Exit fullscreen mode

Note: this file must be created first in the root of the app and must have execution permissions.

  • Code the docker-entrypoint.sh file.
#!/bin/bash -e
mvn package
exec $@
Enter fullscreen mode Exit fullscreen mode
  • Include it as a docker entrypoint
ENTRYPOINT ["/docker-entrypoint.sh"]
Enter fullscreen mode Exit fullscreen mode

By following this approach, we can significantly improve the efficiency of our Docker builds. The Maven dependencies will be downloaded and stored within the Docker volume, eliminating the need for repetitive downloads. Consequently, subsequent builds will be faster, more streamlined, and less error-prone.

Embracing Docker not only simplifies the development and deployment process but also ensures consistent and reliable results across different environments. With these techniques in place, you can confidently build, run, and maintain your Java Maven application within a Dockerized environment.

Following whole example step by step

Create app

run $:

mvn archetype:generate -DgroupId=dev.example -DartifactId=maven-docker-article -DarchetypeArtifactId=maven-archetype-quickstart -DinteractiveMode=false
Enter fullscreen mode Exit fullscreen mode

Update pom.xml

<project  xmlns="http://maven.apache.org/POM/4.0.0"  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>dev.example</groupId>
  <artifactId>maven-docker-article</artifactId>
  <packaging>jar</packaging>
  <version>1.0-SNAPSHOT</version>
  <name>maven-docker-article</name>
  <url>http://maven.apache.org</url>
  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <maven.compiler.source>1.8</maven.compiler.source>
    <maven.compiler.target>1.8</maven.compiler.target>
  </properties>
  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-jar-plugin</artifactId>
        <version>3.2.0</version>
        <configuration>
          <archive>
            <manifest>
              <mainClass>dev.example.App</mainClass>
            </manifest>
          </archive>
        </configuration>
      </plugin>
    </plugins>
  </build>
  <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>3.8.1</version>
      <scope>test</scope>
    </dependency>
  </dependencies>
</project>
Enter fullscreen mode Exit fullscreen mode

Add some code

package dev.example;
public class App 
{
    public static void main( String[] args )
    {
        System.out.println( "This app works and build fast using docker entrypoint and m2 repository volumne sharing" );
    }
}
Enter fullscreen mode Exit fullscreen mode

Create Dockerfile

FROM maven:3.6.0-jdk-11 AS build

WORKDIR /app

COPY pom.xml .
COPY src ./src

COPY docker-entrypoint.sh /docker-entrypoint.sh

ENTRYPOINT ["/docker-entrypoint.sh"]
Enter fullscreen mode Exit fullscreen mode

Create docker-entrypoint.sh file.

#!/bin/bash -e
env
mvn package
exec $@
Enter fullscreen mode Exit fullscreen mode

Add execution permissions

chmod +x docker-entrypoint.sh

Build the image

docker build -t my-maven-java-app .
Enter fullscreen mode Exit fullscreen mode

Execute the container/application

docker run --rm -v maven-cache:/root/.m2 maven-dev-article "java -jar /app/target/maven-docker-article-1.0-SNAPSHOT.jar"
Enter fullscreen mode Exit fullscreen mode

With this command we are executing the java application inside its container and leveraging the maven repository shared by volume our docker volume maven-cache and the container maven repository /root/.m2

From now on you docker have a volume named maven-cache that can be used for whatever other maven app you want to dockerize using this same pattern.

Top comments (4)

Collapse
 
khmarbaise profile image
Karl Heinz Marbaise

Why using ancient old Maven version in the container 3.6.0-jdk-8-alpine ... better use most recent versions 3.9.6....

Collapse
 
montells profile image
Michel Sánchez Montells

Uff. This is just the version I was using in the real project.
Would make any change regarding this article using most recent version?
Thanks 😉

Collapse
 
khmarbaise profile image
Karl Heinz Marbaise • Edited

First newer versions are faster and using less memory ... furthermore you could use things like maven.apache.org/extensions/maven-... (Maven 3.9.X+) ... also I would suggest not to build inside the container... Build outside and deploy the resulting jar into the container... also multi layered container builds ... etc....

Not to mentioned things like using Java 8 (source/target) is a bit out of date... use more recent JDK versions like JDK21+ also for support containers...

Collapse
 
tmsangdev profile image
Sáng Minh Trần