I have the following docker file that runs a spring boot application:
# For Java 11, try this
FROM adoptopenjdk/openjdk11:alpine-jre
#
ARG JAR_FILE=/build/libs/pokerstats-0.0.1-SNAPSHOT.jar
#
WORKDIR /opt/app
#
COPY ${JAR_FILE} app.jar
#
ENTRYPOINT ["java","-jar","app.jar"]
The issue is that currently I have to run gradle clean build on my host machine first to create the jar file on my local machine at path:
/build/libs/pokerstats-0.0.1-SNAPSHOT.jar
How can I put this gradle clean build into my docker file so that the build step is done inside the container?
edit:
I want the steps for a user to be:
Clone my project from github
run docker build -t pokerstats . - which will do the gradle build
run docker container run -d -p 8080:8080 pokerstats
The user will clone my project from github - I then want them to be able to run the docker container without having to build the project with gradle first - I.e. I want the docker file to do the build and copy the jar into the container.
After reading this article I have been able to solve this using a Multi Stage Docker Build. Please see the Docker file below:
# using multistage docker build
# ref: https://docs.docker.com/develop/develop-images/multistage-build/
# temp container to build using gradle
FROM gradle:5.3.0-jdk-alpine AS TEMP_BUILD_IMAGE
ENV APP_HOME=/usr/app/
WORKDIR $APP_HOME
COPY build.gradle settings.gradle $APP_HOME
COPY gradle $APP_HOME/gradle
COPY --chown=gradle:gradle . /home/gradle/src
USER root
RUN chown -R gradle /home/gradle/src
RUN gradle build || return 0
COPY . .
RUN gradle clean build
# actual container
FROM adoptopenjdk/openjdk11:alpine-jre
ENV ARTIFACT_NAME=pokerstats-0.0.1-SNAPSHOT.jar
ENV APP_HOME=/usr/app/
WORKDIR $APP_HOME
COPY --from=TEMP_BUILD_IMAGE $APP_HOME/build/libs/$ARTIFACT_NAME .
EXPOSE 8080
ENTRYPOINT exec java -jar ${ARTIFACT_NAME}
Build stage
FROM gradle:jdk11-alpine AS BUILD_STAGE
COPY --chown=gradle:gradle . /home/gradle
RUN gradle build || return 1
Package stage
FROM openjdk:11.0.11-jre
ENV ARTIFACT_NAME=app.jar
ENV APP_HOME=/app
COPY --from=BUILD_STAGE /home/gradle/build/libs/$ARTIFACT_NAME $APP_HOME/
WORKDIR $APP_HOME
RUN groupadd -r -g 1000 user && useradd -r -g user -u 1000 user
RUN chown -R user:user /app
USER user
ENTRYPOINT exec java -jar ${ARTIFACT_NAME}
Mutli stage build. Be sure to have build.gradle and settings.gradle files included in your project directory, I tried to make it as clean and concise as possible:
#Build stage
FROM gradle:latest AS BUILD
WORKDIR /usr/app/
COPY . .
RUN gradle build
# Package stage
FROM openjdk:11-jre-slim
ENV JAR_NAME=app.jar
ENV APP_HOME=/usr/app/
WORKDIR $APP_HOME
COPY --from=BUILD $APP_HOME .
EXPOSE 8080
ENTRYPOINT exec java -jar $APP_HOME/build/libs/$JAR_NAME
Make sure to change JAR_NAME variable to your generated jar file name.
Related
I am building a Docker container using the following Dockerfile and actually the app is running on the created container.
FROM eclipse-temurin:17-jdk-jammy as builder
RUN addgroup demogroup; adduser --ingroup demogroup --disabled-password demo
USER demo
WORKDIR /app
# copy pom.xml, mvnw and source code
COPY .mvn/ .mvn
COPY mvnw ./
COPY pom.xml ./
COPY src/ src
#RUN dos2unix ./mvnw
RUN ./mvnw clean install <-- this line gives error
# Second stage: minimal runtime environment
FROM eclipse-temurin:17-jre-jammy
WORKDIR /app
# copy jar from the first stage
COPY --from=builder /app/target/*.jar /app/app.jar
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "/app/app.jar"]
When executing RUN ./mvnw clean install line, I get " /bin/sh: 1: ./mvnw: not found" error. I tried many things, but cannot fix it. Is there any problem in my Dockerfile?
RUN mvn
runs mvn inside the docker image.
The docker image is basically a pared down Linux image. Or a Windows image, or whichever OS was used as the base image.
Presuming your base image is Linux, after docker build completed, at your project folder, you can do
docker run -it
and you will be in your dockerized Linux image, where you can run basic Linux commands (provided your image has those basic commands installed).
You can then navigate and inspect if your docker image.
To solve the problem, include Maven build in your Dockerfile instead of running maven command from project folder via mvnv:
COPY src/main/resources/data/ ./src/data
I am building a Docker container using the following Dockerfile and actually the app is running on the created container.
FROM eclipse-temurin:17-jdk-jammy as builder
RUN addgroup demogroup; adduser --ingroup demogroup --disabled-password demo
USER demo
WORKDIR /app
# copy pom.xml, mvnw and source code
COPY .mvn/ .mvn
COPY mvnw ./
COPY pom.xml ./
COPY src/ src
#RUN dos2unix ./mvnw
RUN ./mvnw clean install <-- this line gives error
# Second stage: minimal runtime environment
FROM eclipse-temurin:17-jre-jammy
WORKDIR /app
# copy jar from the first stage
COPY --from=builder /app/target/*.jar /app/app.jar
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "/app/app.jar"]
When executing RUN ./mvnw clean install line, I get " /bin/sh: 1: ./mvnw: not found" error. I tried many things, but cannot fix it. Is there any problem in my Dockerfile?
RUN mvn
runs mvn inside the docker image.
The docker image is basically a pared down Linux image. Or a Windows image, or whichever OS was used as the base image.
Presuming your base image is Linux, after docker build completed, at your project folder, you can do
docker run -it
and you will be in your dockerized Linux image, where you can run basic Linux commands (provided your image has those basic commands installed).
You can then navigate and inspect if your docker image.
To solve the problem, include Maven build in your Dockerfile instead of running maven command from project folder via mvnv:
COPY src/main/resources/data/ ./src/data
Docker build works on my laptop but on GitLab I get
Error: Could not find or load main class org.gradle.wrapper.GradleWrapperMain
Tried a lot of different setups but nothing works...fails at gradlew build...Any ideas are welcome
My .gitlab-ci.yaml
....variables here
publish:
image:
name: amazon/aws-cli
entrypoint: [""]
services:
- docker:dind
before_script:
- amazon-linux-extras install docker
- aws --version
- docker --version
- export GRADLE_USER_HOME=`pwd`/gradle
- export CLASSPATH=`pwd`/gradle/wrapper
cache:
paths:
- gradle/wrapper
- .gradle/wrapper
- .gradle/caches
script:
- docker build -t $DOCKER_REGISTRY/$APP_NAME:$CI_PIPELINE_IID .
My Dockerfile
FROM openjdk:11
ENV wdir=code
ENV MY_SERVICE_PORT=8080
WORKDIR /$wdir
COPY . /code
RUN echo "Running build"
RUN ["/code/gradlew", "build"]
EXPOSE $MY_SERVICE_PORT
# Run the service
CMD ["java", "-jar", "build/libs/code-1.0-SNAPSHOT.jar"]
Created a workaround by installing gradle...works now:
FROM openjdk:11
ENV wdir=code
ENV MY_SERVICE_PORT=8080
WORKDIR /code
# Install Gradle
RUN wget -q https://services.gradle.org/distributions/gradle-6.5-bin.zip \
&& unzip gradle-6.5-bin.zip -d /opt \
&& rm gradle-6.5-bin.zip
ENV GRADLE_HOME /opt/gradle-6.5
ENV PATH $PATH:/opt/gradle-6.5/bin
# Prepare by downloading dependencies
ADD build.gradle /code/build.gradle
ADD src /code/src
RUN echo "Running build"
RUN cd /code
RUN gradle --no-daemon build
EXPOSE $MY_SERVICE_PORT
# Run the service
CMD ["java", "-jar", "build/libs/code-1.0-SNAPSHOT.jar"]
I've been following the guide from the spring website [here][1] but when I try and run Docker I get a class not found exception. I cant see what I'm doing wrong here.
Below is my dockerfile:
# syntax=docker/dockerfile:experimental
FROM openjdk:15-jdk-alpine AS build
WORKDIR /workspace/app
COPY . /workspace/app
RUN --mount=type=cache,target=/root/.gradle ./gradlew clean build
RUN mkdir -p build/dependency && (cd build/dependency; jar -xf ../libs/*.jar)
FROM openjdk:15-jdk-alpine
VOLUME /tmp
ARG DEPENDENCY=/workspace/app/build/dependency
COPY --from=build ${DEPENDENCY}/BOOT-INF/lib /app/lib
COPY --from=build ${DEPENDENCY}/META-INF /app/META-INF
COPY --from=build ${DEPENDENCY}/BOOT-INF/classes /app
ENTRYPOINT ["java","-cp","app:app/lib/*","TFSSearchServiceApplication"]
And If I look into the docker image here's the folder structure I see:
/app # ls
META-INF application-local.yml application-production.yml application.yml network
application-local.properties application-production.properties application.properties lib
/app/network/mytest/search # ls
TFSSearchServiceApplication.class controller exception service
config converter model validators
/app/network/mytest/search #
Which is what I'd expect, however can anyone spot why I'm getting the class not found error, I can see it from last codeblock.
Many thanks
[1]: https://spring.io/guides/topicals/spring-boot-docker/
change your Dockerfile to the following (only change in the last line):
syntax=docker/dockerfile:experimental
FROM openjdk:15-jdk-alpine AS build
WORKDIR /workspace/app
COPY . /workspace/app
RUN --mount=type=cache,target=/root/.gradle ./gradlew clean build
RUN mkdir -p build/dependency && (cd build/dependency; jar -xf ../libs/*.jar)
FROM openjdk:15-jdk-alpine
VOLUME /tmp
ARG DEPENDENCY=/workspace/app/build/dependency
COPY --from=build ${DEPENDENCY}/BOOT-INF/lib /app/lib
COPY --from=build ${DEPENDENCY}/META-INF /app/META-INF
COPY --from=build ${DEPENDENCY}/BOOT-INF/classes /app
ENTRYPOINT ["java","-cp","app:app/lib/*","network.mytest.search.TFSSearchServiceApplication"]
The reason for the ClassNotFoundException is that your classpath (-cp) is searching inside /app and /app/lib/*. Your SpringBootApplication resides in a package (network.mytest.search.TFSSearchServiceApplication). So you have to point the java command to it by specifying the package additionally.
Make sure TFSSearchServiceApplication has the #SpringBootApplication annotation.
I am new to java and maven. I have built an application that executes a flink job. I have created a base docker image but I am not sure how to excecute/run like I run the application in the terminal.
I currently run the application in the terminal as follows:
mvn package exec:java `-D exec.args="--runner=FlinkRunner --flinkMaster=localhost:8081 --filesToStage=.\target\maven_benchmark-1.0-SNAPSHOT-jar-with-dependencies.jar `" -P flink-runner`
Here is my docker file
FROM maven:latest 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 openjdk:14
COPY --from=build /usr/src/app/target/maven_benchmark-1.0-SNAPSHOT-jar-with-dependencies.jar /usr/app/maven_benchmark-1.0-SNAPSHOT-jar-with-dependencies.jar
WORKDIR /usr/app
EXPOSE 8080
ENTRYPOINT ["java","-jar","maven_benchmark-1.0-SNAPSHOT-jar-with-dependencies.jar"]
Any suggestions?
Thanks in advance!
You are running your app with a maven plugin and with a maven profile. You need your app to be runnable outside of maven first.
Then, you need to cleanup your docker steps a bit, here are some suggestions:
Move copy src after coyping pom and downloading dependencies
Do not use root user for runnable image
Use slimer base image for runnable image
Use exploded jars instead of fat jars to get slimmer layers
Make use of .dockerignore to prevent copying unwanted things to the image
Here is a sample Dockerfile:
FROM maven:3.6.3-openjdk-14-slim AS build
WORKDIR /build
# copy just pom.xml (dependencies and dowload them all for offline access later - cache layer)
COPY pom.xml .
RUN mvn dependency:go-offline -B
# copy source files and compile them (.dockerignore should handle what to copy)
COPY . .
RUN mvn package
# Explode fat runnable JARS
ARG DEPENDENCY=/build/target/dependency
RUN mkdir -p ${DEPENDENCY} && (cd ${DEPENDENCY}; jar -xf ../*.jar)
# Runnable image
FROM openjdk:14-alpine as runnable
VOLUME /tmp
VOLUME /logs
ARG DEPENDENCY=/build/target/dependency
# Create User&Group to not run docker images with root user
RUN addgroup -S awesome && adduser -S awesome -G awesome
USER awesome
# Copy libraries & meta-info & classes
COPY --from=build ${DEPENDENCY}/BOOT-INF/lib /app/lib
COPY --from=build ${DEPENDENCY}/META-INF /app/META-INF
COPY --from=build ${DEPENDENCY}/BOOT-INF/classes /app
# Run application
ENTRYPOINT ["java","-cp","app:app/lib/*","com.myawesomeness.Application"]
Then your app, must be runnable outside of maven first.