TL/DR: How can I cache the pom.xml
file's <parent>
so my build can be run in offline mode?
I'm using Docker to build a maven project. My goal is to add two steps to the build: one to download all of the dependencies, and another to build the project. Here's what my Dockerfile looks like so far:
FROM maven:3.3-jdk-8
# Download the project dependencies (so they can be cached by Docker)
ADD pom.xml /runtime/
WORKDIR /runtime
RUN mvn dependency:go-offline
RUN mvn dependency:resolve-plugins
# Mount the local repository
ADD . /runtime
# Build the service
RUN mvn clean package -o -DskipTests
This seems to work fine for the plugins. I checked the /root/.m2/repository
and everything seems to be in order.
Edit: When double checking for the /root/.m2/repository
directory, it's no longer there. For some reason, Maven isn't saving any of the dependencies to this location.
Edit 2: After building the Docker image, there's no /root/.m2/repository
directory. However, if I run mvn dependency:go-offline
from within a shell inside the Docker container, the directory is created without a problem.
When I attempt build my application, I get the following error:
[ERROR] [ERROR] Some problems were encountered while processing the POMs:
[FATAL] Non-resolvable parent POM for com.example:service:1.2: Cannot access central (http://jcenter.bintray.com) in offline mode and the artifact org.springframework.boot:spring-boot-starter-parent:pom:1.4.0.M3 has not been downloaded from it before. and 'parent.relativePath' points at wrong local POM @ line 14, column 13
@
[ERROR] The build could not read 1 project -> [Help 1]
[ERROR]
[ERROR] The project com.sample:service:1.2 (/runtime/pom.xml) has 1 error
[ERROR] Non-resolvable parent POM for com.oe:graph-service:1.2: Cannot access central (http://jcenter.bintray.com) in offline mode and the artifact org.springframework.boot:spring-boot-starter-parent:pom:1.4.0.M3 has not been downloaded from it before. and 'parent.relativePath' points at wrong local POM @ line 14, column 13 -> [Help 2]
[ERROR]
[ERROR] To see the full stack trace of the errors, re-run Maven with the -e switch.
[ERROR] Re-run Maven using the -X switch to enable full debug logging.
[ERROR]
[ERROR] For more information about the errors and possible solutions, please read the following articles:
[ERROR] [Help 1] http://cwiki.apache.org/confluence/display/MAVEN/ProjectBuildingException
[ERROR] [Help 2] http://cwiki.apache.org/confluence/display/MAVEN/UnresolvableModelException
The problem seems to be that mvn dependency:go-offline
isn't resolving the parent. When I run the build in offline mode, it breaks.
Here are the relevant portions of my pom.xml
file:
<?xml version="1.0" encoding="UTF-8"?>
<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>
...
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.4.0.M3</version>
</parent>
...
<repositories>
<repository>
<snapshots>
<enabled>false</enabled>
</snapshots>
<id>central</id>
<name>bintray</name>
<url>http://jcenter.bintray.com</url>
</repository>
<repository>
<id>repository.springsource.snapshot</id>
<name>SpringSource Snapshot Repository</name>
<url>http://repo.springsource.org/snapshot</url>
</repository>
<repository>
<id>spring-milestones</id>
<url>http://repo.spring.io/milestone</url>
</repository>
</repositories>
<dependencies>
...
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jetty</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.scala</groupId>
<artifactId>spring-scala_2.11</artifactId>
<version>1.0.0.BUILD-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<!-- disabling Spring cloud AWS until proper testing harnesses can be set up -->
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpcore</artifactId>
<version>LATEST</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-aws-autoconfigure</artifactId>
<version>1.1.0.RELEASE</version>
</dependency>
...
</dependencies>
...
</project>
the local repository is a directory on the computer where Maven runs. It caches remote downloads and contains temporary build artifacts that you have not yet released.
When you run a Maven build, then Maven automatically downloads all the dependency jars into the local repository. It helps to avoid references to dependencies stored on remote machine every time a project is build. Maven local repository by default get created by Maven in %USER_HOME% directory.
If you customize maven's settings.xml
file you can save your repository files on image.
Create a custom version of settings.xml
, with the localRepository
setting modified, like this:
<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0 http://maven.apache.org/xsd/settings-1.0.0.xsd">
<!-- localRepository
| The path to the local repository maven will use to store artifacts.
|
| Default: ${user.home}/.m2/repository -->
<localRepository>/usr/share/maven/repo</localRepository>
...
Then override the default configuration when building your image:
FROM maven:3-jdk-8
COPY settings.xml /usr/share/maven/conf/settings.xml
RUN mkdir -p /usr/src/app
WORKDIR /usr/src/app
ADD . /usr/src/app
RUN mvn dependency:go-offline
RUN mvn clean package
Now your repository is stored in /usr/share/maven/repo
.
You can also create a base image using ONBUILD, this will allow to have custom configured images for every maven project.
Like this:
FROM maven:3-jdk-8
COPY settings.xml /usr/share/maven/conf/settings.xml
RUN mkdir -p /usr/src/app
WORKDIR /usr/src/app
ONBUILD ADD . /usr/src/app
ONBUILD RUN mvn dependency:go-offline
ONBUILD RUN mvn clean package
Then build the image:
docker build -t mvn_bldr .
This will create a template for other maven images. Then you can create your custom downstream image with:
FROM mvn_bldr
If you want to customize your image further you can add more instructions, every instruction of the template will be triggered after the FROM mvn_bldr
command, as in the docs:
The trigger will be executed in the context of the downstream build, as if it had been inserted immediately after the FROM instruction in the downstream Dockerfile.
It may seem like using RUN
with Docker is like executing commands in a shell script, but it's not. Every instance of RUN
gets applied to a new container that results from the changes created by the previous command. So each command is executing inside of a new container context.
Dockerifles can contain a VOLUME
reference, which mounts an external directory inside the Docker container. If there were any files inside the volume, those files are wiped out. If you don't explicitly specify a volume, Docker is happy to instead create an empty folder.
While my Dockerfile doesn't contain an explicit reference to a VOLUME
, its parent does. So, even though my mvn dependency:go-offline
command was running, those files were being wiped out in the next step by the VOLUME
specified in the docker-maven
Dockerfile
.
In the end, I couldn't find a good way to make the maven
Docker image work, so I switched to the openjdk
image and installed maven
via apt-get
instead.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With