How to make vert.x project work in a Docker container? - java

I downloaded a vert.x starter project from http://start.vertx.io/ and would like to run the compiled binary with java -jar .. inside a Docker container.
Current invocation command:
mvn package exec:java -DskipTests
Current Dockerfile:
FROM java:10
COPY target/project-1.0-SNAPSHOT.jar project.jar
ENTRYPOINT java -jar project.jar
which gives the following error message when run
no main manifest attribute, in
/project/target/vertx-start-project-1.0-SNAPSHOT.jar
Is there a simpler way than building a full deployment assembly as is usually done with Maven?

When you run a command like so:
mvn package exec:java
The vert.x specfic configuration will make Maven create:
SNAPSHOT.jar
SNAPSHOT-fat.jar
like so:
the fat.jar has all the files in it, so you only need to copy that jar file to the Docker image.
FROM openjdk:10-jre-slim
COPY target/SNAPSHOT-fat.jar fat.jar
ENTRYPOINT java -jar fat.jar
you build the docker image with:
docker build -t foo .
then run the docker image as a container with:
docker run -it foo

A runnable jar requires a Main-Class entry in MANIFEST.MF with the name of the class to launch.
You will most likely also need your dependencies copied in.
Consider this a full deployment of your application.

Like what Thorbjørn said, this command executes a "runnable" jar, which means it should have packed with a MANIFEST.MF that points to some certian main method in your project

Related

Building docker image for Spring Boot app using Dockerfile and running using JarLauncher

I was using a DockerFile for building a spring boot jar file, uploads to artifactory and deploys to K8S. For doing this I wrote a standard Dockerfile like as shown below
FROM maven:3.6.6-jdk-11 AS build
WORKDIR source
COPY . source
RUN mvn -q clean package -DskipTests
FROM maven:3.6.6-jdk-11
COPY --from=build source/app-0.0.1-SNAPSHOT.jar app.jar
EXPOSE 8080
RUN chmod +x app.jar
ENTRYPOINT ["java","-jar","app.jar"]
But I have seen a different project where they have mentioned like as shown below
FROM maven:3.8.4-jdk-11 AS builder
WORKDIR source
COPY . .
RUN mvn -q clean package -DskipTests
RUN cp target/cdacapp-0.0.1-RELEASE.jar ./cdacapp.jar
RUN java -Djarmode=layertools -jar cdacapp.jar extract
FROM openjdk:11-jre-slim
WORKDIR application
COPY --from=builder source/dependencies/ ./
COPY --from=builder source/spring-boot-loader/ ./
COPY --from=builder source/webjars/ ./
COPY --from=builder source/application/ ./
ENTRYPOINT [ "java", "org.springframework.boot.loader.JarLauncher"]
I would like to know what benefit we will get by running the application using JarLauncher
This setup produces an image that is smaller to download when you deploy an updated version of the image with just changed code, but not any of the changed dependencies.
A Docker image is made of layers, and in the setup you show here, each COPY command is a separate layer. In the first form, the COPY with the fat jar produces a single layer, and if you change anything in the application you have to download that entire layer again (including all of the unchanged dependencies). In the second form, though, if the dependencies and spring-boot-loader trees haven't changed (which is probably the common case) then only the later layers will need to be downloaded.
For a more practical example, let's say you're labeling your Docker images with a date stamp, and the most recent build was registry.example.com/your-image:20220722. Now if you rebuild with the second Dockerfile
docker build -t registry.example.com/your-image:20220725 .
docker push registry.example.com/your-image:20220725
Docker will recognize that the registry already contains the "dependencies" layer and not push it again. Similarly, if you update a Kubernetes manifest
image: registry.example.com/your-image:20220725 # was 20220722
the cluster will download a piece of data called the image manifest and realize it already has the base layers, so it only needs to download the updated application layer at the end.
If you're working totally locally, there's not especially a benefit to one approach over the other.
The Spring Boot documentation has a section on Container Images which discusses this specific approach further.

Docker Springboot Webapp Included Libraries

I've got a Springboot jar deployed as a webapp via Docker. Here is the basic outline of my Dockerfile.
#
# Build App
#
FROM maven:3.6.3-openjdk-15-slim AS build
# Copy Parent Project
COPY src /home/app/src
COPY pom.xml /home/app
RUN mvn -f /home/app/pom.xml clean package -DskipTests
#
# Deploy App
#
FROM openjdk:15-jdk-alpine
COPY --from=build /home/app/my-app/target/my-app-rest-0.0.1-SNAPSHOT.jar /usr/local/lib/app.jar
ENTRYPOINT exec java -Djava.security.egd=file:/dev/./urandom -jar /usr/local/lib/app.jar
I originally found a pattern similar to this Dockerfile around the internet and I thought I understood most of it, but one thing is eluding me: where are all of the dependency jars that are needed by my app.jar to run? My project is running and executing just fine, so those have to be somewhere in my container. But I've searched and searched but can't find any of the dependency jars or even the WEB-INF directory for my webapp. I know that my actual application jar is in /usr/local/lib/, but that's about all I can deduce.
Is there some default location where the dependency/webapp config would go? Is there something that I can add to my Dockerfile to define where it should go?
Spring boot puts all your dependencies (jars) in fat executable jar, which is the one you are passing to the run stage, and the exact one you are running with the java command.
Since jar files are just compressed archives you can extract them using unzip an peek inside them:
docker cp <container-id>:/home/app/my-app/target/my-app-rest-0.0.1-SNAPSHOT.jar myjar.jar
unzip myjar.jar -d myjar
ls -al myjar/BOOT-INF/lib
And you'll get the list of all the jars you spring boot app depends on.
More information here and here
Maven is used to build your .jar package, while you only need to put the .jar package into docker image and add java -jar ... as the entry point.
Note that Docker is strongly recommending programmers to separate applications into individual docker images. Even if you can have both Maven and .jar together, don't do that.
Is there some default location where the dependency/webapp config would go?
Yes. This is the Dockerfile I use at work. You run maven commands such as clean, package on your development machine, instead of inside a Dockerfile.
FROM openjdk:8-jdk-alpine
# You want to change JDK version.
ADD target/*.jar app.jar
# You have to place this Dockerfile in the SAME directory with the target folder. Then this Dockerfile pulls whatever jar you have under target, renames it to app.jar and adds it to the build.
EXPOSE 9001
# You want to change this to whatever port your java app listens on.
ENTRYPOINT ["java", "-jar", "/app.jar"]
# Typical command you use to run .jar package but this time, you use it as the entrypoint.
As soon as you have that .jar package, cd into the Dockerfile directory and docker build ..

Maven packaging with Docker

I have a Spring project and a corresponding JAR file.
After i change something in one of my Java class files and run mvn package, building my Docker image of that project later is using the cached JAR.
Only if i run mvn clean package, my Docker build process does not use the cached JAR:
Step 4/6 : COPY app/target/app-${VERSION}.jar app.jar
---> 19987e6dda16
Is that expected?
Do i always have to run mvn clean package instead of mvn package after changing some code?

Deploy .war file in docker container: No main manifest attribute in app.war

I have a .war-file of my java application and would like to deploy that to a docker container.
My Dockerfile looks like this:
FROM java:8
EXPOSE 8080
ADD /App.war app.war
ENTRYPOINT ["java", "-jar", "app.war"]
I have my App.war in the same directory.
When I want to run it with the following command:
docker run -p 8080:8080 -t app
it throws the following exception:
no main manifest attribute, in app.war
However, when I inspect app.war, there's a manifest in /META-INF/MANIFEST.MF stating the following options:
Manifest-Version: 1.0
Start-Class: com.package.app.AppMainClass
Spring-Boot-Classes: WEB-INF/classes/
Spring-Boot-Lib: WEB-INF/lib/
Spring-Boot-Version: 1.4.0.RELEASE
Main-Class: org.springframework.boot.loader.WarLauncher
So as far as I understand the error message it states that a Main-Class attribute is missing in the manifest (which is impossible because it's there).
I have also tried adding the real class as Main-Class.
That led to the same error.
Can someone please help me back on track?
First of all don't build a spring boot project as WAR. You would only need WAR if you had some old ancient project which forced you to deploy the app to a web server/servlet container. You'll need to remove war related logic from your build script.
Second. Make sure you're building the project correctly. If it is gradle, you need to build it with ./gradlew build, for maven use mvn package. That will package your archive as an uberjar - including all needed dependencies and making the proper archive structure.
Find more details on how to put a spring boot application to Docker in the user guides.
If and only if by some strange occurrences you need to build it as WAR and run it as an executable archive make sure you do it using the war-plugin provided by spring boot (not the built in war plugin). That's the only working way to build an executable war archive.

Docker build fails to find source folder for COPY command

I have a multi module maven project, and I want to create and publish an image for each module, except for a common, utility module, which is a dependency for all the other modules.
Project structure is like this:
project_root
common_module
-pom.xml
module_a
-pom.xml
-Dockerfile
module_b
-pom.xml
-Dockerfile
-pom.xml
-docker-compose.yml
To build module_a fox example, I need to copy the common_module folder too to the container, and run mvn install on the common folder first. If that is built, I can run mvn install on the module_a pom.xml too.
I'm new to Docker, and this is what I've been trying, which I think should work, according to the documentation of the COPY command, which states:
Multiple src resources may be specified but the paths of files and directories will be interpreted as relative to the source of the context of the build.
I have the following Dockerfile in module_a:
FROM openjdk:8-jre-alpine
# Install Maven
# (skipped for brevity)
# Create the project root
RUN mkdir -p /usr/src/project_root
WORKDIR /usr/src/project_root
# Copy all the necessary files for the packaging
# Copy the common module
COPY common_module /usr/src/project_root/common_module
# Copy the service registry module (src + config folder)
COPY module_a /usr/src/project_root/module_a
# Run maven install
RUN cd common_module
RUN mvn clean install
RUN cd ../module_a
RUN mvn clean install
And I issue this command from the project_root folder (making it the context of the build?):
docker build -t image_name:4.0 ./module_a
The build fails at the first COPY command, stating:
COPY failed: stat /var/lib/docker/tmp/docker-builder380120612/common_module: no such file or directory
I'm not even sure the RUN commands would work, as the build fails before those.
What am I doing wrong? How can one copy folders/files outside of the Dockerfile location?
By the way I'm running Docker version 18.03.1-ce, build 9ee9f40, on Windows 10 Pro.
When you run the command
docker build -t image_name:4.0 ./module_a
You define the build context as the module_a directory. Then you try to COPY the directory common_module into your container but it's not possible as it is not in the build context.
Use this command instead:
docker build -t image_name:4.0 -f ./module_a/Dockerfile .
This way module_a, common_module and module_b will be included in the build context.

Categories