Caching Jar dependencies for Maven-based Docker builds - maven

I'm building a Docker image from this Dockerfile:
FROM maven:3.3.3-jdk-8
MAINTAINER Mickael BARON
ADD pom.xml /work/pom.xml
WORKDIR /work
RUN mvn dependency:go-offline --fail-never
ADD ["src", "/work/src"]
RUN ["mvn", "package"]
With this Dockerfile, I force to download the dependencies before packaging my Java project. Thus, I don't have to redownload the dependencies every time I changed a file from my src directory.
But, there is a problem and this problem is depending on the version of Maven (base image). In fact, the dependencies are downloaded but they are not persisted into the ~/.m2 directory of the container. It's empty. Thus, when I change some source file all the dependencies are redownloaded.
However, I noticed that if I change the version of Maven from the base image (for example FROM maven:3.2.5-jdk-8), it works.
Very strange, isn't it?

There is a new instruction regarding this topic:
https://github.com/carlossg/docker-maven#packaging-a-local-repository-with-the-image
The $MAVEN_CONFIG dir (default to /root/.m2) is configured as a volume so anything copied there in a Dockerfile at build time is lost. For that the dir /usr/share/maven/ref/ is created, and anything in there will be copied on container startup to $MAVEN_CONFIG.
To create a pre-packaged repository, create a pom.xml with the dependencies you need and use this in your Dockerfile. /usr/share/maven/ref/settings-docker.xml is a settings file that changes the local repository to /usr/share/maven/ref/repository, but you can use your own settings file as long as it uses /usr/share/maven/ref/repository as local repo.

I'm afraid it's because of this VOLUME instruction they've added:
https://github.com/carlossg/docker-maven/blame/8ab542b907e69c5269942bcc0915d8dffcc7e9fa/jdk-8/Dockerfile#L11
It makes /root/.m2 a volume and thus any changes to that folder made by build steps are not brought on to the following build containers.

Related

How to docker image of spring boot application? [closed]

Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
We don’t allow questions seeking recommendations for books, tools, software libraries, and more. You can edit the question so it can be answered with facts and citations.
Closed 1 year ago.
Improve this question
I'm facing several issues in creating docker application in windows environment.
Can someone help me with a link or reference or steps on how to create docker image for spring boot application in windows environment?
Basically the application runs perfectly fine in IntelliJ or Eclipse STS without any issues (Zero Issues)
Please advise.
Thanks
Example:(https://github.com/briansjavablog/build-and-run-spring-boot-with-docker)
The code of the main class is shown below, and nothing else is added. Next I will use the default actuator health endpoint to test the application.
package com.blog.samples.docker;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
#SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
The following content is the image file defined in the Dockerfile:
FROM maven:3.5.2-jdk-8-alpine AS MAVEN_BUILD
MAINTAINER Brian Hannaway
COPY pom.xml /build/
COPY src /build/src/
WORKDIR /build/
RUN mvn package
FROM openjdk:8-jre-alpine
WORKDIR /app
COPY --from=MAVEN_BUILD /build/target/docker-boot-intro-0.1.0.jar /app/
ENTRYPOINT ["java", "-jar", "docker-boot-intro-0.1.0.jar"]
FROM maven:3.5.2-jdk-8-alpine AS MAVEN_BUILD tells Docker to use the Maven compiler.
maven:3.5.2-jdk-8-alpine builds the basic image used in the first step. Docker will first find the image locally. If it does not exist locally, it will be pulled from DockerHub. Maven will be removed in the final stage (introduced by the subsequent COPY command). Considering the reasons for fast downloading and image size control, the Alpine version of Maven image is selected.
MAINTAINER Brian Hannaway is optional, but providing a touch point for image authors can improve maintainability. (The point of application verification in this experiment)
COPY pom.xml /build/ Create a build directory in the mirror and copy the pom.xml file.
COPY src /build/src/ Copy into the src directory to the build directory in the mirror.
WORKDIR /build/ sets build as the working directory. Any subsequent commands are run in this directory.
RUN mvn package executes the mvn package to run the compiled and packaged application, and generates an executable JAR file. When building a mirror for the first time, Maven will pull all the required dependencies from the public Maven repository and cache them locally on the mirror. Subsequent builds will use this cached version of the mirroring layer, which means that dependencies will be referenced locally without having to pull them from the outside again.
At this point, the image definition has been completed, just wait for it to be built into an executable JAR file. This is the first part of a multi-stage construction. The next stage will get the JAR and run it.
FROM openjdk:8-jre-alpine tells Docker that the next step of the multi-stage build uses the basic image of openjdk:8-jre-alpine. Using the Alpine version of Java 8 JRE again, the choice of this step is actually more important than the previous Maven version selection, because the image in the final version is only openjdk:8-jre-alpine, so if you want to control the final image size as much as possible , It is very important to choose a lightweight JRE mirror.
WORKDIR /app tells Docker to create another /app working directory in the image, and any subsequent commands will run in this directory.
COPY --from=MAVEN_BUILD /build/target/docker-boot-intro-0.1.0.jar /app/ tells Docker to copy ocker-boot-intro-0.1.0.jar from the /build/target directory in the MAVEN_BUILD stage to / app directory.
As mentioned earlier, the advantage of multi-stage construction is that it allows users to copy specific content from one construction stage to another, and discard all other content. If you need to keep everything from the MAVEN_BUILD stage, the final image will contain Maven (including Maven local libraries) tools and all class files generated in the target directory. By selecting the necessary content from the MAVEN_BUILD stage, the resulting image will be much smaller.
ENTRYPOINT ["java", "-jar", "app.jar"] tells Docker which commands to run when the container runs this image. This part uses colons to isolate multiple commands. In this case, you need to copy the execution JAR file to the /app directory to run.
After completing the Docker image definition, you can start building. Open the directory containing the Dockerfile (root directory). Run the following command to build the image:
docker image build -t docker-boot-intro
The -t parameter is the specified name and optional label. If you don't specify a label, Docker will automatically mark it as the latest.
$ docker image build -t docker-boot-intro .
Sending build context to Docker daemon 26.56MB
Step 1/10 : FROM maven:3.5.2-jdk-8-alpine AS MAVEN_BUILD
---> 293423a981a7
Step 2/10 : MAINTAINER Brian Hannaway
---> Using cache
---> db354a426bfd
Step 3/10 : COPY pom.xml /build/
---> Using cache
---> 256340699bc3
Step 4/10 : COPY src /build/src/
---> Using cache
---> 65eb0f98bb79
Step 5/10 : WORKDIR /build/
---> Using cache
---> b16b294b6b74
Step 6/10 : RUN mvn package
---> Using cache
---> c48659e0197e
Step 7/10 : FROM openjdk:8-jre-alpine
---> f7a292bbb70c
Step 8/10 : WORKDIR /app
---> Using cache
---> 1723d5b9c22f
Step 9/10 : COPY --from=MAVEN_BUILD /build/target/docker-boot-intro-0.1.0.jar /app/
---> Using cache
---> d0e2f8fbe5c9
Step 10/10 : ENTRYPOINT ["java", "-jar", "docker-boot-intro-0.1.0.jar"]
---> Using cache
---> f265acb14147
Successfully built f265acb14147
Successfully tagged docker-boot-intro:latest
SECURITY WARNING: You are building a Docker image from Windows against a non-Windows Docker host. All files and directories added to build context will have '-rwxr-xr-x' permissions. It is recommended to double check and reset permissions for sensitive files and directories.
Brians Computer#DESKTOP-077OUJ8 MINGW64 /c/dev/docker-boot-intro (master)
When running the build, Docker will execute each command in the Docker file one by one. Create a layer with a unique ID for each step. For example, the ID of the layer created in step 1 is 293423a981a7.
When building an image for the first time, Docker will fetch any external images it needs from DockerHub, and then start building new layers on top of it. This will make the first build very slow.
During the build process, Docker checks the cache before attempting to build the layer to see if there is already a cached version of the build layer. If a cached version of the layer is available, Docker will use it directly instead of building it from scratch. This means that once an image layer is built, subsequent builds are reused, which will be much faster. You can see that the cache layer is used through the hash value of the Docker cache output in the above build output. Take what happened in step 6 above as an example:
As part of the RUN mvn package command, Docker will fetch all the POM dependencies from the public Maven repository, build it into an executable JAR, and store all of them in the layer with ID c48659e0197e. The next time this image is built, Maven dependencies and application JARs will be taken out of the cache layer without having to download and build again.

Dockerfile to create image of spring boot

I want to create Docker image for a spring boot application.It uses gardle as build tool.
In bitbucket repository i could see below files are placed
--> src
--> build.gradle
--> Dockerfile
--> gradlew
--> gradlew.bat
Now Dockerfile has below content
FROM openjdk:8-jdk-alpine
VOLUME /tmp
ARG DEPENDENCY=target/dependency
COPY ${DEPENDENCY}/BOOT-INF/lib /app/lib
COPY ${DEPENDENCY}/META-INF /app/META-INF
COPY ${DEPENDENCY}/BOOT-INF/classes /app
EXPOSE 8080
ENTRYPOINT ["java","-cp","app:app/lib/*","eds.billnq"]
When i try to create image using Dockerfile i get error
target/dependency/BOOT-INF library not found
target/dependency/META-INF library not found
My query is why here there is no gradle build steps in docker file ?
Also where target/dependency/ will be created ?
why here there is no gradle build steps in docker file ?
It looks like the Dockerfile is expecting the application to have already been built (by Gradle) before the Docker image is built, and for the resulting class files and jars to be available in various directories under target/dependency.
The Spring Boot Docker guide referred to by #WonChul Heo in the comments goes into how to build a Docker image using Gradle and specifically with the plugin gradle.plugin.com.palantir.gradle.docker:gradle-docker.
Also where target/dependency/ will be created ?
I'm guessing they should be created when you run your Gradle build, but without knowing more about how the build is configured, it's hard to say for sure.
I suggest you read through the guide again and compare what it recommends to what you have in your codebase, especially the Gradle build definitions.
At the end of the day, if you've pulled code from a private code repo and can't figure out how to make it build, it's probably best to seek out advice from the people who've committed to the codebase than from SO.
When working With Gradle ur jar file is going to be generated inside build/libs/{Your jar}
Can you please change ARG DEPENDENCY=target/dependency to ARG DEPENDENCY=build/libs and try? It will work.

Where m2 file is stored when installing apache maven on a unix box

I downloaded maven gz file, unzipped same, but i dont know where the m2 is stored. I imagine im missing a step but i cant see what one?
Is there aninstall script etc?
[root#atddpvm5 apache-maven-3.5.4]# cd /var/tmp/apache-maven-3.5.4/
[root#atddpvm5 apache-maven-3.5.4]# ls
apache-maven DEPENDENCIES doap_Maven.rdf LICENSE maven-builder-
support maven-core maven-model maven-plugin-api
maven-resolver-provider maven-settings-builder NOTICE README.md
CONTRIBUTING.md deploySite.sh Jenkinsfile maven-artifact maven-compat
maven-embedder maven-model-builder maven-repository-metadata maven-
settings maven-slf4j-provider pom.xml src
By default the .m2 folder is stored in the home folder of the user. In this case since you are using root, the path is most likely /root/.m2. You also have to use the -a switch with ls to see that folder, since it's a hidden folder (it starts with a .). Note that the folder will only be created on the first usage of Maven, i.e. when you call a maven command on a maven project, like mvn clean install.
Additionally it looks like you have downloaded the source distribution of Maven, which only makes sense if you want to work on Maven itself. You might want to download the binary distribution, if you just want to use it.

Using Maven artifact in Google Cloud Docker build

I have a google cloud container build with the following steps
gcr.io/cloud-builders/mvn to run a mvn clean package cmd
gcr.io/cloud-builders/docker to create a docker image
My docker image includes and will run tomcat.
Both these steps work fine independently.
How can I copy the artifacts built by step 1 into the correct folder of my docker container? I need to move either the built wars or specific lib files from step 1 to the tomcat dir in my docker container.
Echoing out the /workspace and /root dir in my Dockerfile doesn't show the artifacts. I think I'm misunderstanding this relationship.
Thanks!
Edit:
I ended up changing the Dockerfile to set the WORKDIR to /workspace
and
COPY /{files built by maven} {target}
The working directory is a persistent volume mounted in the builder containers, by default under /workdir. You can find more details in the documentation here https://cloud.google.com/container-builder/docs/build-config#dir
I am not sure what is happening in your case. But there is an example with a Maven step and a Docker build step in the documentation of the gcr.io/cloud-builders/mvn builder. https://github.com/GoogleCloudPlatform/cloud-builders/tree/master/mvn/examples/spring_boot. I suggest you compare with your files.
If it does not help, could you share your Dockerfile and cloudbuild.yaml. Please make sure you remove any sensitive information.
Also, you can inspect the working directory by running the build locally https://cloud.google.com/container-builder/docs/build-debug-locally#preserve_intermediary_artifacts

Docker multi-stage build and mounting/sharing from previous stage

I would like to use multi-stage builds to avoid downloading all the Maven dependencies required by my Java project every time I build the app.
I am thinking of resolving the Maven dependencies in a first stage, then building the app in a second stage which would require access to the dependencies downloaded in the previous stage.
If I understood well multi-stage builds I could copy files created in the first stage to the second stage, but ideally I would like to be able to "mount" or "share" the folder from the first stage where the dependencies live instead of copying the files, is it possible? Or is there a better way to achieve this?
Thanks.
EDIT:
This was the first stage I was thinking about
FROM some-image-with-maven AS maven-repo
WORKDIR /workspace/
COPY pom.xml .
RUN mvn -B -f pom.xml dependency:resolve
But since the pom file will be different most of the times (because I would like to share this stage across projects), the following step that resolves dependencies will download all of them again (instead of using a cached layer).
You can only copy stuff from the first stage if you are not using volumes. When using volumes, you can share data between stages which are basically separate container instances.
Since missing to clean up volumes is often not handled properly I suggest to keep to the copy strategy. There is no real benefit using bind-mount to share data over the copy approach.
I don't believe there's a way to do this currently. To share from one build stage to the next, the only option is to COPY files from one stage's directory to the current stage.
To use the first stage as a build cache and avoid copying all the dependencies, I'd run your build in that first stage. Or you can make a second intermediate stage that is FROM stage1name if you want additional separation between the stages. The output of your build can then be copied to the final layer, avoiding the need to copy all the build dependencies.
Answering from the future...
If using buildkit or compatible (most people probably are by now), you can mount a previous stage with a bind mount. Something like this would accomplish what the original post was asking:
FROM someimage as build
COPY pom.xml .
RUN mvn -Dmaven.repo.local=/.m2_repository -B -f pom.xml dependency:resolve
FROM runtimeimage
COPY pom.xml .
COPY src/ ./src/
RUN --mount=type=bind,from=build,source=/.m2_repository,target=/.m2_repository \
mvn package
But more to the point, there is also a cache mount that you can use instead and you would incur the cost of downloading all the deps on first run, but subsequent would be able to find those deps in the cache:
FROM runtimeimage
COPY pom.xml .
COPY src/ ./src/
RUN --mount=type=cache,target=/.m2_repository,sharing=locked \
mvn package

Resources