Docker Springboot Webapp Included Libraries - java

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 ..

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.

I can't find my jar file when trying to copy it in a multi stage docker build

So I am very new to docker and I have been trying to dockerise my spring-boot application. I managed to get it working with a given JAR file but now I would like to make a multi stage build to build the JAR file using gradle so that I don't have to manually generate the JAR before running docker build {app-name}
This is my current setup. It fails to find the jar file and I don't know how I can navigate the filesystem to find it (other than running shell commands in the dockerfile to print out the files & folders)
DOCKERFILE
FROM gradle:jdk8 as build
COPY --chown:gradle:gradle . /home/gradle/src
WROKDIR /home/gradle/src
RUN gradle clean build --parallel
FROM openjdk:8-alpine
WORKDIR /usr/app
COPY --from=build /home/gradle/src/app-name.jar /usr/app/
RUN sh -c 'touch app-name.jar'
ENTRYPOINT ["java", "-jar", "app-name.jar"]
Console Output
Step 7/9. : COPY —from=build /home/gradle/src/app-name.jar /usr/app/
COPY failed: stat /var/lib/ldocker/overlay2/22e326d9f26f581a629417c35e226428f3cc63fd496799c55dde4be413ca26690/merged/home/gradle/src/app-name.jar: no such file or directory
The COPY directive needs to copy from gradle's build/libs/ directory which is the default location for build artifacts - as it stands it's looking for the JAR in the project root. This should work:
COPY —from=build /home/gradle/src/build/libs/app-name.jar /usr/app
This assumes the Dockerfile is located in your project's root directory. If it's not, then you'll need to adjust the copy path used in the dockerfile accordingly.

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

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

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.

How to Dockerize a tomcat app

I am trying to dockerize some Tomcat application but I never touch Java application before so the lack of understand it makes it really hard to understand what should I do.
So far I have this but it doesn't work and I don't if it's the correct path as well
FROM tomcat:6
ENV APP_ROOT /app_name
RUN apt-get update && apt-get install -y default-jdk
COPY . $APP_ROOT/
WORKDIR $APP_ROOT
RUN jar -cvf app_name.war *
# this fail for some reason, when I do `ls` the file is there but if fail to copy it
COPY app_name.war $CATALINA_BASE/webapps/app_name.war
I am just going on loop on this because I don't understand and Google Search do not help me that much (I don't know how to ask).
Should I use the jar command in the build? If not, I guess I have to build it locally and just make sure that the .war is there right?!
How the building of the Java with Tomcat app works? and How to integrate with Docker?
Sorry for being too generic but I don't understand anything about Java
Looking at your code this is what I could gleam:
You have some java files stored in current directory (.)
When you call COPY you copy all these contents to /app_name
You create a .war on the file
There are some things to note, first is that the app_name.war is not on the host disk, it is currently inside of the docker file system. What this means is that you cannot COPY the .war.
What you are really after is this: RUN cp app_name.war $CATALINA_BASE/webapps/app_name.war
This would look like the following:
Dockerfile
FROM tomcat:6
ENV APP_ROOT /app_name
RUN apt-get update && apt-get install -y default-jdk
COPY . $APP_ROOT/
WORKDIR $APP_ROOT
RUN jar -cvf app_name.war *
RUN cp app_name.war $CATALINA_BASE/webapps/app_name.war
Adding the docker COPY reference here as it explains the command in detail. It might also be helpful for you to make a script called provision.sh, then do something like:
COPY provision.sh /tmp/provision.sh
RUN sh /tmp/provision.sh
That way you can put all your building, configuring and other in a single script that you can test locally (again if it helps)
EDIT: Adding mention about building locally and copying into dockerfile
You can build the .war on your machine, use COPY to put is on the machine.
Dockerfile
FROM tomcat:6
ENV APP_ROOT /app_name
RUN apt-get update && apt-get install -y default-jdk
COPY app_name.war $CATALINA_BASE/webapps/app_name.war
WORKDIR $APP_ROOT
The above copies the file app_name.war then add it to the filesystem of the container at the path $CATALINA_BASE/webapps/app_name.war. So for that you do this:
Build the .war on your machine with java
Put .war in directory with Dockerfile
COPY app_name.war into the container's filesystem
You can try to do this "by hand" before trying to automate it, it should help to understand the process. You don't need to extend a tomcat official image to be able to deploy a war on a dockerized tomcat, you can use the image directly if you don't need to customize permissions and users (in production, you need).
If you need Tomcat 6.x because your webapp implements servlet API < 3, do this :
sudo docker run --name tomcat --detach --port 8080:8080 tomcat:6
Now, your Tomcat is running in background (--detach), waiting for a deployment. You've exported port 8080 from the container and mapped it to port 8080 from you host, so the app will be available at http://localhost:8080/ on your host.
From now if you copy your .war in /usr/local/tomcat/webapps into the container, the app will be deployed :
sudo docker cp app_name.war tomcat:/usr/local/tomcat/webapps/
I don't use docker, I use a similar AWS product called codedeploy for provisioning instances, so I tell you what I do for Tomcat setup in my provisioning scripts. Should be easy to port to docker as just bash comands.
1) Build the WAR
Most java applications these days are built using Maven but Gradle is catching up. Maven and the WAR plugin are used to turn java code into a WAR file which you can deploy on Tomcat. But it looks like you already have the WAR built by someone else? Either way, you dont run the war directly, you put it in Tomcat, unless you've bundled Tomcat into the app, in which case it would be a JAR but lets not talk about that....The simple solution is build the war from java code using a build tool like Maven or Gradle. By build, I mean turn it from source code to binary.
2) Install Tomcat
yum install tomcat6,7,8 etc etc (Whichever version you need)
Then turn it on
service start tomcat8
3) Deploy the war
To run the war place it in the webapps folder of the Tomcat installation. I generally like to shut tomcat off when I do this but you can do it while its running. After a few seconds the WAR, which is really just a zip file, will be exploded/unzipped to create a directory.
4) Accessing the application/site
If you rename your war to ROOT.war then you can access the applicaition at http://localhost:8080 if your configuration is to have it listen on 8080. If you war is named pets.war then your webapp URL would be http://localhost:8080/pets. You configure which port for Tomcat to listen on in the server.xml file in its conf folder.
Most Important
Tomcat documentation is very good once you know what to look for. The primary configuration files are web.xml, context.xml, and server.xml. The central tomcat guides explain each component https://tomcat.apache.org/tomcat-7.0-doc/setup.html you just need to find the doc that corresponds to your version of Tomcat.

Categories