In my Spring Boot app, I read some csv file located in resources\data folder and I have the following Dockerfile:
FROM maven:3.8.2-openjdk-17-slim AS builder
# add pom.xml and source code
ADD ./pom.xml pom.xml
ADD ./src src/
# package jar
RUN mvn clean install
# Second stage: minimal runtime environment
FROM openjdk:17-alpine
# copy jar from the first stage
ARG JAR_FILE=target/*.jar
COPY --from=builder ${JAR_FILE} app.jar
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "/app.jar"]
When I run the app after building the container, I get the following error:
"class path resource [data/employees.csv] cannot be resolved to absolute file path because it does not reside in the file system: jar:file:/app.jar!/BOOT-INF/classes!/data/employees.csv"
I think I need to add the resources\data folder to my container. So, how can I do this? What is the most proper way?
Note: I also tried to fix the problem using new ClassPathResource(filename).getInputStream(), but it did not make any sense and still the same problem.
Related
I reference an HTML file in my code, and access it with:
Path filePath1 = Path.of("./email.html");
When I run the project locally, the project works fine, and the file loads normally. However, when running the project in a Docker container, I get the following error:
java.nio.file.NoSuchFileException: ./email.html
Here is my Docker file for reference
FROM openjdk:11.0-jdk-slim as builder
VOLUME /tmp
COPY . .
RUN apt-get update && apt-get install -y dos2unix
RUN dos2unix gradlew
RUN ./gradlew build
# Phase 2 - Build container with runtime only to use .jar file within
FROM openjdk:11.0-jre-slim
WORKDIR /app
# Copy .jar file (aka, builder)
COPY --from=builder build/libs/*.jar app.jar
ENTRYPOINT ["java", "-Xmx300m", "-Xss512k", "-jar", "app.jar"]
EXPOSE 8080
Thank you for the answers. So this is a Java project, so there is no index.html to add. I tried changing the work directory to /src, but it is still not picking it up
Docker has no access to the filesystem fromm the host OS.
You need to put it in there as well:
COPY ./index.html index.html
There's a couple of options:
Copy the index.html in the docker image (solution by ~dominik-lovetinsky)
Mount the directory with your index.html file as a volume in your docker instance.
Include the index.html as a resource in your app.jar, and access it as a classpath resource.
The last option: including resources as classpath resource, is the normal way webapps work, but I'm not sure if it works for you.
I use docker-compose to launch different Spring Boot apps.
My docker images are defined with this kind of Dockerfile:
FROM openjdk:8-jdk-alpine
ADD app.jar app.jar
ENTRYPOINT ["java", "-Djava.security.egd=file:/dev/./urandom", "-jar", "/app.jar"]
However, I would like to benefit from debugging and hot-reload features using something like mvn spring-boot:run without being dependent of a particular IDE.
What is the best way to accomplish debugging and hot-reloading with Spring Boot in a Docker container without being dependent of a particular IDE?
Notes:
my source files are build into a jar (with Maven) which is copied to a different location containing the definition of my Docker images ; meaning my sources files are not in the docker image.
the reason I want to develop in the Docker container is that my apps depend on each other, and are configured in the docker-compose environment, so I cannot easily run one app alone outside the docker network and environment.
I thought of mounting a volume containing my spring boot projects in the docker containers, and then use mvn spring-boot:run in the container ; but I can't prevent maven to download all dependencies from the internet (I tried specifying a local repository containing all my dependencies without success). I would like to know if this a decent solution and how to make it work.
You have to follow the following steps to build and run spring boot application in docker.
Step-1 : Create a File called Dockerfile in your Project.
Step-2 : Write the Following Code on you Dockerfile
# Use the official maven/Java 8 image to create a build artifact.
# https://hub.docker.com/_/maven
FROM maven:3.6-jdk-11 as builder
# Copy local code to the container image.
WORKDIR /app
COPY pom.xml .
COPY src ./src
# Build a release artifact.
RUN mvn package -DskipTests
# Use AdoptOpenJDK for base image.
# It's important to use OpenJDK 8u191 or above that has container support enabled.
# https://hub.docker.com/r/adoptopenjdk/openjdk8
# https://docs.docker.com/develop/develop-images/multistage-build/#use-multi-stage-builds
FROM adoptopenjdk/openjdk11:alpine-slim
# Copy the jar to the production image from the builder stage.
COPY --from=builder /app/target/your-app-name*.jar /your-app-name.jar
# Run the web service on container startup.
CMD ["java", "-Djava.security.egd=file:/dev/./urandom", "-jar", "/your-app-name.jar"]
Step-3 : Start Your Docker Desktop Application
Step-4 : Open Your Terminal or Windows PowerShell. Then go to Project Directory.
Step-6 : Write the Following Command to create image for your application (You must have internet connection to download all dependencies).
docker build -f Dockerfile -t your-app-name .
Step-7 : After image creation success. Write the following code to run the image in Docker container.
docker run -p docker-port:app-port image-name
Following your line of thinking you can try to copy your dependencies from a volume into the project container and then use the offline mode in something like this:
FROM openjdk:8-jdk-alpine
WORKDIR /app
# copy the Project Object Model file
COPY ./pom.xml ./pom.xml
# copy your dependencies
COPY app.jar app.jar
# copy your other files
COPY ./src ./src
# Set fetch mode to offline to avoid downloading them from the internet
RUN mvn dependency:go-offline
Apparently it's also possible to configure the offline mode globally by setting the offline property in the ~/.m2/settings.xml file, you can setup that and copy your m2 file and reference it when running the container
<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">
<offline>true</offline>
</settings>
mvn -Dmaven.repo.local=~/.m2/settings.xml ...
You can find more information here:
https://www.baeldung.com/maven-offline
Specifying Maven's local repository location as a CLI parameter
I want to run a gradle build inside a container, then execute the jar with its config file and server parameters. This is what I have
WORKDIR /app
COPY . /app
RUN chmod +x gradlew \
&& gradle build
COPY config/*.yml build/libs
RUN cd build/libs \
&& ls -la
EXPOSE 8081
ENTRYPOINT ["java", "-jar", "/app/build/libs/app.jar", "-Dconfig.location=app/build/libs/config.yml", "-Dgrails.env=prod"]
I can see that both the yml file and the jar file are in the build libs folder, but when I build the dockerfile, I get error indicating missing yml file. When I reorder them and place config location before the jar file, it finds the config, but doesn't execute the jar file. When I purposely put an incorrect path to jar file, I get an error that jar is not found.
What I get after docker run is
Configuring Spring Security Core ...
... finished configuring Spring Security Core Configuring Spring Security REST 2.0.0.RC1...
... finished configuring Spring Security REST
And that's it. Although running locally I get "application running.." aswell.
Any help appreciated!
I cannot post a comment as I am a newbie.
I see that you are in build/libs when you run-
RUN cd build/libs
Then your ENTRYPOINT command is using relative path for config location-
"-Dconfig.location=app/build/libs/config.yml"
You can try starting the location with "/app" instead of "app".
Also, if that doesn't help, can you paste the exact errors you are getting in the 2 scenarios you mentioned?
Dockerfile:
FROM java:8-jdk-alpine
RUN mkdir -p /usr/app
RUN mkdir -p /usr/app/logs/
COPY ./storefront/build/libs/storefront-0.0.1-SNAPSHOT.jar /usr/app
WORKDIR /usr/app
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "storefront-0.0.1-SNAPSHOT.jar"]
start.sh
sudo docker build ./ -t platform
sudo docker run -p 8080:8080 platform
Error:
2020-05-11 11:53:01.925 ERROR 1 --- [ main] o.s.boot.SpringApplication : Application run failed
org.springframework.beans.factory.BeanDefinitionStoreException: Failed to parse configuration class [lv.dz.platform.storefront.StorefrontApplication]; nested exception is java.io.FileNotFoundException: /home/daniel/projects/MY/platform/storefront/src/main/resources/application.properties (No such file or directory)
Note:
Working with java -jar storefront-0.0.1-SNAPSHOT.jar and ./gradlew bootRun
Any ideas?
Update:
Issue was in one of the modules (not storefront), where #PropertySource was defined as full path to application.properties. Resolved by removing this line, since it was old code.
Its works in you computer, because you have dependency to your local file /home/daniel/projects/MY/platform/storefront/src/main/resources/application.properties. After building and running java file inside container your code still tries fo load application.properties file from /home/daniel/projects/MY/platform/storefront/src/main/resources/application.properties which is not exists inside container.
Inside you application change absolute path to relative.
Use dockers feature multi-staged build. Build your jar file during building an docker image.
Have you tried to to put property out of jar like below ?
ADD application.properties /usr/app/application.properties
ENTRYPOINT ["java" ,"--spring.config.location=classpath:file:/usr/app/application.properties","-jar","storefront-0.0.1-SNAPSHOT.jar"]
Context:
It's an application written in Kotlin and using Spring-boot with Maven.
Basically, I'd like to know if it makes sense what I'm doing.
Running mvn install then the target folder will be created with the corresponding jar file.
Therefore the Dockerfile will be just copying the jar file into the working directory of the container and run java -jar WHATEVER.jar.
Example of the simple Dockerfile
FROM openjdk:8-jre-alpine
COPY target/app-DEV-SNAPSHOT.jar .
EXPOSE 8089
CMD ["java", "-jar", "./app-DEV-SNAPSHOT.jar"]
But I'd say, makes much more sense to me to use the multi-stage building and in the first stage generate the jar file and in the second stage, execute it. I tried this second approach but I'm facing an issue with main class doesn't exist
Multi-stage Dockerfile:
FROM maven:3.5.2-jdk-8-alpine as BUILD
ENV APP_HOME=/usr/src/service
COPY ./src /usr/src/service
COPY pom.xml /usr/src/service
WORKDIR /usr/src/service
RUN mvn install
FROM openjdk:8-jre-alpine
COPY --from=BUILD /usr/src/service/target/app-DEV-SNAPSHOT.jar ./
EXPOSE 8080
CMD ["java", "-jar", "./app-DEV-SNAPSHOT.jar"]
Which one is the correct one?
You should use the multistage dockerfile. Reason being you want to have least dependency on the host system. When you run mvn on host you add dependency of mvn and in turn java.
My recommendation would be to use multistage docker to build in one stage and copy to another stage