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

montells

Michel Sánchez Montells

Posted on May 19, 2024

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.

💖 💪 🙅 🚩
montells
Michel Sánchez Montells

Posted on May 19, 2024

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related