multi-module Maven project on Dockers - java

I have a multi-module maven project where the single modules are all runnable microservice applications containing their own Dockerfile, so in production every module will be a containerized application.
The parent project, which contains the child-modules only contains the parent pom.xml and the docker-compose.yml
I have tried to use the following Dockerfile (on sub-module level):
FROM sgrio/java-oracle
RUN apt-get update
RUN apt-get install -y maven
COPY ../pom.xml /usr/local/service/Oogaday/pom.xml
COPY pom.xml /usr/local/service/Oogaday/OogadayApi/pom.xml
COPY src /usr/local/service/Oogaday/OogadayApi/src
WORKDIR /usr/local/service/Oogaday/OogadayApi/
RUN mvn package -DskipTests
CMD ["java","-jar","org.oogaday.api-1.0-SNAPSHOT-jar-with-dependencies.jar"]
But I am getting a security error because I am trying to copy the parent pom.xml file (which is not placed in the directory from which I am running the build).
So is there a way to build a maven based sub-module with parent pom?

This is my suggestion, you should take advantage the more you can of docker cache.
Assume this multi-module project pom layout:
+-- my-project
+-- module1
| `-- pom.xml
+-- module2
| `-- pom.xml
`- pom.xml
Dockerfile:
# cache as most as possible in this multistage dockerfile.
FROM maven:3.6-alpine as DEPS
WORKDIR /opt/app
COPY module1/pom.xml module1/pom.xml
COPY module2/pom.xml module2/pom.xml
# you get the idea:
# COPY moduleN/pom.xml moduleN/pom.xml
COPY pom.xml .
RUN mvn -B -e -C org.apache.maven.plugins:maven-dependency-plugin:3.1.2:go-offline
# if you have modules that depends each other, you may use -DexcludeArtifactIds as follows
# RUN mvn -B -e -C org.apache.maven.plugins:maven-dependency-plugin:3.1.2:go-offline -DexcludeArtifactIds=module1
# Copy the dependencies from the DEPS stage with the advantage
# of using docker layer caches. If something goes wrong from this
# line on, all dependencies from DEPS were already downloaded and
# stored in docker's layers.
FROM maven:3.6-alpine as BUILDER
WORKDIR /opt/app
COPY --from=deps /root/.m2 /root/.m2
COPY --from=deps /opt/app/ /opt/app
COPY module1/src /opt/app/module1/src
COPY module2/src /opt/app/module2/src
# use -o (--offline) if you didn't need to exclude artifacts.
# if you have excluded artifacts, then remove -o flag
RUN mvn -B -e -o clean install -DskipTests=true
# At this point, BUILDER stage should have your .jar or whatever in some path
FROM openjdk:8-alpine
WORKDIR /opt/app
COPY --from=builder /opt/app/<path-to-target>/my-1.0.0.jar .
EXPOSE 8080
CMD [ "java", "-jar", "/opt/app/my-1.0.0.jar" ]

Related

How to do a Gradle composite build in Docker?

I am able to do a Gradle composite build locally.
Now I'm trying to build the docker image in a docker container instead of on my local laptop. And I got this error:
Included build '/home/yang-liu/eCommerceServiceDataAccessLayer' does not exist.
So it complains that above package doesn't exist.
I'm not sure how to build docker image inside docker container when multiple packages are involved. My two packages are in different GitHub repositories.
More Context
Project structure
settings.gradle files
settings.gradle for eCommerceService:
rootProject.name = 'eCommerceService'
includeBuild("../eCommerceServiceDataAccessLayer")
settings.gradle for eCommerceServiceDataAccessLayer:
rootProject.name = 'eCommerceServiceDataAccessLayer'
Dockerfile in eCommerceService
########################################################################################
# We are multi-stage builds here to build the docker image.
# https://docs.docker.com/develop/develop-images/multistage-build/
########################################################################################
########################################################################################
# Build Stage
########################################################################################
FROM openjdk:11 AS BUILD_ARTIFACT
RUN ls
ARG APP_NAME=ecommerce-service
ARG USER_NAME=yang-liu
ENV APP_HOME=/home/$USER_NAME/$APP_NAME
WORKDIR $APP_HOME
COPY build.gradle settings.gradle gradlew $APP_HOME
COPY gradle $APP_HOME/gradle
# https://stackoverflow.com/questions/25873971/docker-cache-gradle-dependencies
RUN ./gradlew build || return 0
COPY . .
RUN ./gradlew build
########################################################################################
# Run Stage
########################################################################################
FROM openjdk:11-jre AS RUN_ARTIFACT
# Run as a non-root user to mitigate security risks
# https://security.stackexchange.com/questions/106860/can-a-root-user-inside-a-docker-lxc-break-the-security-of-the-whole-system
ARG GROUP_NAME=ecommerce-service
ARG USER_NAME=yang-liu
ARG APP_NAME=ecommerce-service
ARG USER_NAME=yang-liu
ENV APP_HOME=/home/$USER_NAME/$APP_NAME
RUN addgroup $GROUP_NAME
ENV APP_HOME=/home/$USER_NAME
WORKDIR $APP_HOME
RUN adduser --ingroup $GROUP_NAME $USER_NAME --home $APP_HOME
USER $USER_NAME
# Copy the artifact from BUILD_ARTIFACT stage
COPY --from=BUILD_ARTIFACT $APP_HOME/build/libs/eCommerceService-0.0.1.jar eCommerceService-0.0.1.jar
# Set ENTRYPOINT in exec form to run the container as an executable
ENTRYPOINT ["java","-classpath", "-jar","eCommerceService-0.0.1.jar"]

Java -jar is not running the tests compiled in the container

I can't run the tests in maven install because the database service isn't up to date, so I want to run the test when I run the .jar but it's not working
#
# Build stage
#
FROM maven:3.8.3-openjdk-16 AS build
WORKDIR /app
COPY src /app/src
COPY pom.xml /app
RUN mvn -f /app/pom.xml clean package -DskipTests=true
RUN mvn test-compile
#
# Package stage
#
FROM openjdk:16
COPY --from=build /app/target/bank-api.jar /usr/local/lib/bank-api.jar
EXPOSE 8080
ENTRYPOINT ["java","-jar","/usr/local/lib/bank-api.jar"]

Unable to run 'RUN ./mvnw dependency:go-offline -B' when building docker image from "openjdk:8-jdk-alpine" for Spring Boot app

So I am trying to run a spring boot app with maven wrapper inside the docker container. Here is my Docker file:
### Stage 1: Build the application
FROM openjdk:8-jdk-alpine as build
RUN mkdir -p /app
#Set the current working directory inside the image
WORKDIR /app
#copy maven executable to the image
COPY mvnw .
COPY .mvn .mvn
#Copy the pom.xml file
COPY pom.xml .
#Build all the dependencies in preparation to go offline
#This is a separate step so the dependencies will be cached unless
#the pom.xml file has changed
RUN ./mvnw dependency:go-offline -B
#Copy the project source
COPY src src
#Package the application
RUN ./mvnw package -DskipTests
RUN mkdir -p target/dependency && (cd target/dependency; jar -xf ../*.jar)
I have this error:
Step 7/16 : RUN ./mvnw dependency:go-offline -B
---> Running in 642a32f86392
/bin/sh: ./mvnw: not found
ERROR: Service 'app-server' failed to build: The command '/bin/sh -c ./mvnw dependency:go-offline -B' returned a non-zero code: 127
I am working with windows 10 pro. Please I need your help
Maybe a duplicate of Unable to run './mvnw clean install' when building docker image based on "openjdk:8-jdk-alpine" for Spring Boot app
Can you check the line endings of the mvnw shell script?
You could fix it by adding this before executing the mvnw command:
RUN dos2unix mvnw
Alternatively, if the file is in git, you can also fix it by adding the following to a .gitattributes file and checking the file out again:
*.bat text eol=crlf
mvnw text eol=lf
You have to copy the project files into the /app dir first. And you don't have the maven wrapper in the context folder where you run the docker build.
Try change the end of line mvnw file from Windows style CRLF to Unix style LF. Then rebuild the image.

Include local Maven m2 repo in Dockerfile during mvn package stage?

How can I include the local machine ~/.m2/repository in the mvn dependency:resolve stage of docker build? There is no -v option for docker build. Is this possible with just docker build to be compatible with our existing build pipeline (only a solitary Dockerfile is allowed)? This answer didn't help.
Current Dockerfile:
FROM maven:3.6.2-jdk-11 AS maven
WORKDIR /tmp/
# Set the local repository to /tmp/.m2/repository
RUN echo \
"<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 https://maven.apache.org/xsd/settings-1.0.0.xsd'> \
<localRepository>/tmp/.m2/repository</localRepository> \
<interactiveMode>true</interactiveMode> \
<usePluginRegistry>false</usePluginRegistry> \
<offline>false</offline> \
</settings>" \
> /usr/share/maven/conf/settings.xml;
# TODO: How to perform -v ~/.m2/:/tmp/.m2/ but only using `docker build`?
COPY pom.xml /tmp/
RUN mvn dependency:resolve
RUN mvn dependency:resolve-plugins
COPY src /tmp/src/
RUN mvn package
FROM openjdk:11-jre-slim
EXPOSE 8080
RUN apt update
RUN apt -y install curl
WORKDIR /usr/app
ENV TARGET="<target name>.jar"
COPY --from=maven "/tmp/target/$TARGET" .
CMD ["java", "-jar", "$TARGET"]
And of course this doesn't work:
Update: I'm able to mount a folder pointing to the local .m2 repo and copy the whole folder into the docker image. However, now I cannot make mvn use that local repo.
Then
COPY pom.xml /tmp/
RUN mvn -o -s /usr/share/maven/conf/settings.xml dependency:resolve -X
results in the error:
Cannot access central (https://repo.maven.apache.org/maven2) in offline mode and the artifact com.myapp:somename:pom:1.0.4 has not been downloaded from it before.
An idea, that you can do is caching the maven dependencies in the docker. You can make use of mvn dependency-offline (https://maven.apache.org/plugins/maven-dependency-plugin/), doing the dependency-offline maven resolve the dependencies needed in preparation for going offline and reuse them as long as the pom.xml still be the same

How to dockerize maven project? and how many ways to accomplish it?

I am new to Docker, and don't know how to run a java project with maven even though I have read many documents and tried many methods.
Should I build the image using Dockerfile?
What is the commands like when it is to run the maven project in the host with Dockerfile?
Working example.
This is not a spring boot tutorial. It's the updated answer to a question on how to run a Maven build within a Docker container.
Question originally posted 4 years ago.
1. Generate an application
Use the spring initializer to generate a demo app
https://start.spring.io/
Extract the zip archive locally
2. Create a Dockerfile
#
# Build stage
#
FROM maven:3.6.0-jdk-11-slim AS build
COPY src /home/app/src
COPY pom.xml /home/app
RUN mvn -f /home/app/pom.xml clean package
#
# Package stage
#
FROM openjdk:11-jre-slim
COPY --from=build /home/app/target/demo-0.0.1-SNAPSHOT.jar /usr/local/lib/demo.jar
EXPOSE 8080
ENTRYPOINT ["java","-jar","/usr/local/lib/demo.jar"]
Note
This example uses a multi-stage build. The first stage is used to build the code. The second stage only contains the built jar and a JRE to run it (note how jar is copied between stages).
3. Build the image
docker build -t demo .
4. Run the image
$ docker run --rm -it demo:latest
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v2.1.3.RELEASE)
2019-02-22 17:18:57.835 INFO 1 --- [ main] com.example.demo.DemoApplication : Starting DemoApplication v0.0.1-SNAPSHOT on f4e67677c9a9 with PID 1 (/usr/local/bin/demo.jar started by root in /)
2019-02-22 17:18:57.837 INFO 1 --- [ main] com.example.demo.DemoApplication : No active profile set, falling back to default profiles: default
2019-02-22 17:18:58.294 INFO 1 --- [ main] com.example.demo.DemoApplication : Started DemoApplication in 0.711 seconds (JVM running for 1.035)
Misc
Read the Docker hub documentation on how the Maven build can be optimized to use a local repository to cache jars.
https://hub.docker.com/_/maven
Update (2019-02-07)
This question is now 4 years old and in that time it's fair to say building application using Docker has undergone significant change.
Option 1: Multi-stage build
This new style enables you to create more light-weight images that don't encapsulate your build tools and source code.
The example here again uses the official maven base image to run first stage of the build using a desired version of Maven. The second part of the file defines how the built jar is assembled into the final output image.
FROM maven:3.5-jdk-8 AS build
COPY src /usr/src/app/src
COPY pom.xml /usr/src/app
RUN mvn -f /usr/src/app/pom.xml clean package
FROM gcr.io/distroless/java
COPY --from=build /usr/src/app/target/helloworld-1.0.0-SNAPSHOT.jar /usr/app/helloworld-1.0.0-SNAPSHOT.jar
EXPOSE 8080
ENTRYPOINT ["java","-jar","/usr/app/helloworld-1.0.0-SNAPSHOT.jar"]
Note:
I'm using Google's distroless base image, which strives to provide just enough run-time for a java app.
Option 2: Jib
I haven't used this approach but seems worthy of investigation as it enables you to build images without having to create nasty things like Dockerfiles :-)
https://github.com/GoogleContainerTools/jib
The project has a Maven plugin which integrates the packaging of your code directly into your Maven workflow.
Original answer (Included for completeness, but written ages ago)
Try using the new official images, there's one for Maven
https://registry.hub.docker.com/_/maven/
The image can be used to run Maven at build time to create a compiled application or, as in the following examples, to run a Maven build within a container.
Example 1 - Maven running within a container
The following command runs your Maven build inside a container:
docker run -it --rm \
-v "$(pwd)":/opt/maven \
-w /opt/maven \
maven:3.2-jdk-7 \
mvn clean install
Notes:
The neat thing about this approach is that all software is installed and running within the container. Only need docker on the host machine.
See Dockerfile for this version
Example 2 - Use Nexus to cache files
Run the Nexus container
docker run -d -p 8081:8081 --name nexus sonatype/nexus
Create a "settings.xml" file:
<settings>
<mirrors>
<mirror>
<id>nexus</id>
<mirrorOf>*</mirrorOf>
<url>http://nexus:8081/content/groups/public/</url>
</mirror>
</mirrors>
</settings>
Now run Maven linking to the nexus container, so that dependencies will be cached
docker run -it --rm \
-v "$(pwd)":/opt/maven \
-w /opt/maven \
--link nexus:nexus \
maven:3.2-jdk-7 \
mvn -s settings.xml clean install
Notes:
An advantage of running Nexus in the background is that other 3rd party repositories can be managed via the admin URL transparently to the Maven builds running in local containers.
There may be many ways.. But I implemented by following two ways
Given example is of maven project.
1. Using Dockerfile in maven project
Use the following file structure:
Demo
└── src
| ├── main
| │ ├── java
| │ └── org
| │ └── demo
| │ └── Application.java
| │
| └── test
|
├──── Dockerfile
├──── pom.xml
And update the Dockerfile as:
FROM java:8
EXPOSE 8080
ADD /target/demo.jar demo.jar
ENTRYPOINT ["java","-jar","demo.jar"]
Navigate to the project folder and type following command you will be ab
le to create image and run that image:
$ mvn clean
$ mvn install
$ docker build -f Dockerfile -t springdemo .
$ docker run -p 8080:8080 -t springdemo
Get video at Spring Boot with Docker
2. Using Maven plugins
Add given maven plugin in pom.xml
<plugin>
<groupId>com.spotify</groupId>
<artifactId>docker-maven-plugin</artifactId>
<version>0.4.5</version>
<configuration>
<imageName>springdocker</imageName>
<baseImage>java</baseImage>
<entryPoint>["java", "-jar", "/${project.build.finalName}.jar"]</entryPoint>
<resources>
<resource>
<targetPath>/</targetPath>
<directory>${project.build.directory}</directory>
<include>${project.build.finalName}.jar</include>
</resource>
</resources>
</configuration>
</plugin>
Navigate to the project folder and type following command you will be able to create image and run that image:
$ mvn clean package docker:build
$ docker images
$ docker run -p 8080:8080 -t <image name>
In first example we are creating Dockerfile and providing base image and adding jar an so, after doing that we will run docker command to build an image with specific name and then run that image..
Whereas in second example we are using maven plugin in which we providing baseImage and imageName so we don't need to create Dockerfile here.. after packaging maven project we will get the docker image and we just need to run that image..
Here is my contribution.
I will not try to list all tools/libraries/plugins that exist to take advantage of Docker with Maven. Some answers have already done it.
instead of, I will focus on applications typology and the Dockerfile way.
Dockerfile is really a simple and important concept of Docker (all known/public images rely on that) and I think that trying to avoid understanding and using Dockerfiles is not necessarily the better way to enter in the Docker world.
Dockerizing an application depends on the application itself and the goal to reach
1) For applications that we want to go on to run them on installed/standalone Java server (Tomcat, JBoss, etc...)
The road is harder and that is not the ideal target because that adds complexity (we have to manage/maintain the server) and it is less scalable and less fast than embedded servers in terms of build/deploy/undeploy.
But for legacy applications, that may be considered as a first step.
Generally, the idea here is to define a Docker image that will contain the application server. You could have your own Tomcat, JBoss or Weblogic base Dockerfile that setup and configure the server.
About the application(s) to deploy on the server, there is not really a standard way.
A possible approach is defining a Dockerfile by application or set of applications to deploy together. The Dockerfile would have as base image the application server Dockerfile created earlier.
That application Dockerfile would have as objective to retrieve the component (JARs/WARs/EARs) from Git or from a Maven repository manager for example and to deploy/install it/them properly on the application server.
For huge applications (millions of line of codes) with a lot of legacy stuffs, and so hard to migrate to a full spring boot embedded solution, that is really a nice improvement.
I will not detail more that approach since that is for minor use cases of Docker but I wanted to expose the overall idea of that approach because I think that for developers facing to these complex cases, it is great to know that some doors are opened to integrate Docker.
2) For applications that embed/bootstrap the server themselves (Spring Boot with server embedded : Tomcat, Netty, Jetty...)
That is the ideal target with Docker.
I specified Spring Boot because that is a really nice framework to do that and that has also a very high level of maintainability but in theory we could use any other Java way to achieve that.
Generally, the idea here is to define a Docker image per application to deploy.
The docker images for the applications produce a JAR or a set of JAR/classes/configuration files and these start a JVM with the application (java command) when we create and start a container from these images.
For new applications or applications not too complex to migrate, that way has to be favored over standalone servers because that is the standard way and the most efficient way of using containers.
I will detail that approach.
Dockerizing a maven application
1) Without Spring Boot
The idea is to create a fat jar with Maven (the maven assembly plugin and the maven shade plugin help for that) that contains both the compiled classes of the application and needed maven dependencies.
Then we can identify two cases :
if the application is a desktop or autonomous application (that doesn't need to be deployed on a server) : we could specify as CMD/ENTRYPOINT in the Dockerfile the java execution of the application : java -cp .:/fooPath/* -jar myJar
if the application is a server application, for example Tomcat, the idea is the same : to get a fat jar of the application and to run a JVM in the CMD/ENTRYPOINT. But here with an important difference : we need to include some logic and specific libraries (org.apache.tomcat.embed libraries and some others) that starts the embedded server when the main application is started.
We have a comprehensive guide on the heroku website.
For the first case (autonomous application), that is a straight and efficient way to use Docker.
For the second case (server application), that works but that is not straight, may be error prone and is not a very extensible model because you don't place your application in the frame of a mature framework such as Spring Boot that does many of these things for you and also provides a high level of extension.
But that has a advantage : you have a high level of freedom because you use directly the embedded Tomcat API.
2) With Spring Boot
At last, here we go.
That is both simple, efficient and very well documented.
There are really several approaches to make a Maven/Spring Boot application to run on Docker.
Exposing all of them would be long and maybe boring.
The best choice depends on your requirement.
But whatever the way, the build strategy in terms of docker layers looks like the same.
We want to use a multi stage build : one relying on Maven for the dependency resolution and for build and another one relying on JDK or JRE to start the application.
Build stage (Maven image) :
pom copy to the image
dependencies and plugins downloads.
About that, mvn dependency:resolve-plugins chained to mvn dependency:resolve may do the job but not always.
Why ? Because these plugins and the package execution to package the fat jar may rely on different artifacts/plugins and even for a same artifact/plugin, these may still pull a different version.
So a safer approach while potentially slower is resolving dependencies by executing exactly the mvn command used to package the application (which will pull exactly dependencies that you are need) but by skipping the source compilation and by deleting the target folder to make the processing faster and to prevent any undesirable layer change detection for that step.
source code copy to the image
package the application
Run stage (JDK or JRE image) :
copy the jar from the previous stage
entrypoint/cmd : run the application
Here two examples.
a) A simple way without cache for downloaded maven dependencies
Dockerfile :
########Maven build stage########
FROM maven:3.6-jdk-11 as maven_build
WORKDIR /app
#copy pom
COPY pom.xml .
#resolve maven dependencies
RUN mvn clean package -Dmaven.test.skip -Dmaven.main.skip -Dspring-boot.repackage.skip && rm -r target/
#copy source
COPY src ./src
# build the app (no dependency download here)
RUN mvn clean package -Dmaven.test.skip
# split the built app into multiple layers to improve layer rebuild
RUN mkdir -p target/docker-packaging && cd target/docker-packaging && jar -xf ../my-app*.jar
########JRE run stage########
FROM openjdk:11.0-jre
WORKDIR /app
#copy built app layer by layer
ARG DOCKER_PACKAGING_DIR=/app/target/docker-packaging
COPY --from=maven_build ${DOCKER_PACKAGING_DIR}/BOOT-INF/lib /app/lib
COPY --from=maven_build ${DOCKER_PACKAGING_DIR}/BOOT-INF/classes /app/classes
COPY --from=maven_build ${DOCKER_PACKAGING_DIR}/META-INF /app/META-INF
#run the app
CMD java -cp .:classes:lib/* \
-Djava.security.egd=file:/dev/./urandom \
foo.bar.MySpringBootApplication
Drawback of that solution ? Any changes in the pom.xml means re-creates the whole layer that download and stores the maven dependencies.
That is generally not acceptable for applications with many dependencies (and Spring Boot pulls many dependencies), overall if you don't use a maven repository manager during the image build.
b) A more efficient way with cache for maven dependencies downloaded
The approach is here the same but maven dependencies downloads that are cached in the docker builder cache.
The cache operation relies on buildkit (experimental api of docker).
To enable buildkit, the env variable DOCKER_BUILDKIT=1 has to be set (you can do that where you want : .bashrc, command line, docker daemon json file...).
Dockerfile :
# syntax=docker/dockerfile:experimental
########Maven build stage########
FROM maven:3.6-jdk-11 as maven_build
WORKDIR /app
#copy pom
COPY pom.xml .
#copy source
COPY src ./src
# build the app and download dependencies only when these are new (thanks to the cache)
RUN --mount=type=cache,target=/root/.m2 mvn clean package -Dmaven.test.skip
# split the built app into multiple layers to improve layer rebuild
RUN mkdir -p target/docker-packaging && cd target/docker-packaging && jar -xf ../my-app*.jar
########JRE run stage########
FROM openjdk:11.0-jre
WORKDIR /app
#copy built app layer by layer
ARG DOCKER_PACKAGING_DIR=/app/target/docker-packaging
COPY --from=maven_build ${DOCKER_PACKAGING_DIR}/BOOT-INF/lib /app/lib
COPY --from=maven_build ${DOCKER_PACKAGING_DIR}/BOOT-INF/classes /app/classes
COPY --from=maven_build ${DOCKER_PACKAGING_DIR}/META-INF /app/META-INF
#run the app
CMD java -cp .:classes:lib/* \
-Djava.security.egd=file:/dev/./urandom \
foo.bar.MySpringBootApplication
As a rule of thumb, you should build a fat JAR using Maven (a JAR that contains both your code and all dependencies).
Then you can write a Dockerfile that matches your requirements (if you can build a fat JAR you would only need a base os, like CentOS, and the JVM).
This is what I use for a Scala app (which is Java-based).
FROM centos:centos7
# Prerequisites.
RUN yum -y update
RUN yum -y install wget tar
# Oracle Java 7
WORKDIR /opt
RUN wget --no-cookies --no-check-certificate --header "Cookie: gpw_e24=http%3A%2F%2Fwww.oracle.com%2F; oraclelicense=accept-securebackup-cookie" http://download.oracle.com/otn-pub/java/jdk/7u71-b14/server-jre-7u71-linux-x64.tar.gz
RUN tar xzf server-jre-7u71-linux-x64.tar.gz
RUN rm -rf server-jre-7u71-linux-x64.tar.gz
RUN alternatives --install /usr/bin/java java /opt/jdk1.7.0_71/bin/java 1
# App
USER daemon
# This copies to local fat jar inside the image
ADD /local/path/to/packaged/app/appname.jar /app/appname.jar
# What to run when the container starts
ENTRYPOINT [ "java", "-jar", "/app/appname.jar" ]
# Ports used by the app
EXPOSE 5000
This creates a CentOS-based image with Java7.
When started, it will execute your app jar.
The best way to deploy it is via the Docker Registry, it's like a Github for Docker images.
You can build an image like this:
# current dir must contain the Dockerfile
docker build -t username/projectname:tagname .
You can then push an image in this way:
docker push username/projectname # this pushes all tags
Once the image is on the Docker Registry, you can pull it from anywhere in the world and run it.
See Docker User Guide for more informations.
Something to keep in mind:
You could also pull your repository inside an image and build the jar as part of the container execution, but it's not a good approach, as the code could change and you might end up using a different version of the app without notice.
Building a fat jar removes this issue.
Jib as a Maven plugin, makes it easy and flexible enough to use Docker daemon or ignore it. Both in command line or in pom.xml.
For more info you can refer to build-a-docker-image-using-maven
Here is a simple pom.xml:
...
<properties>
<java.version>11</java.version>
<docker.name>amirkeshavarz/hellomavendocker</docker.name>
<docker.REGISTRY_USERNAME>your-dockerhub-username</docker.REGISTRY_USERNAME>
<docker.REGISTRY_PASSWORD>your-dockerhub-password</docker.REGISTRY_PASSWORD>
</properties>
...
<build>
<plugins>
...
<plugin>
<groupId>com.google.cloud.tools</groupId>
<artifactId>jib-maven-plugin</artifactId>
<version>2.8.0</version>
<configuration>
<from>
<image>openjdk:17-jdk-alpine</image>
<auth>
<username>${docker.REGISTRY_USERNAME}</username>
<password>${docker.REGISTRY_PASSWORD}</password>
</auth>
</from>
<to>
<image>${docker.name}</image>
<auth>
<username>${docker.REGISTRY_USERNAME}</username>
<password>${docker.REGISTRY_PASSWORD}</password>
</auth>
</to>
<container>
<environment></environment>
<ports>
<port>8080</port>
</ports>
<creationTime>USE_CURRENT_TIMESTAMP</creationTime>
</container>
</configuration>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>build</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
...
Updated Docker file for #Mark O'Connor answer -
FROM maven:3.8.6-openjdk-18-slim AS build
COPY src /home/app/src
COPY pom.xml /home/app
RUN mvn -f /home/app/pom.xml clean package
FROM openjdk:20
COPY --from=build /home/app/target/simple-spring-application-0.0.1-SNAPSHOT.jar /usr/local/lib/my-app.jar
EXPOSE 8080
ENTRYPOINT ["java","-jar","/usr/local/lib/my-app.jar"]
build, run and logs (replace DOCKER-ID) -
$ docker build -t my-app .
$ docker run -dp 8080:8080 --rm -it my-app:latest
$ docker logs --follow <DOCKER-ID>
Link to git project - https://github.com/idanuda/simple-spring-application
Create a Dockerfile
#
# Build stage
#
FROM maven:3.6.3-jdk-11-slim AS build
WORKDIR usr/src/app
COPY . ./
RUN mvn clean package
#
# Package stage
#
FROM openjdk:11-jre-slim
ARG JAR_NAME="project-name"
WORKDIR /usr/src/app
EXPOSE ${HTTP_PORT}
COPY --from=build /usr/src/app/target/${JAR_NAME}.jar ./app.jar
CMD ["java","-jar", "./app.jar"]

Categories

Resources