1

Created basic HelloWorld microservice using Spring Boot (2.1.3), Java 8, Maven.

pom.xml has maven plugin entry like below

<plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
            <configuration>
                    <mainClass>com.example.HelloWorldApplication</mainClass>
            </configuration>
        </plugin>

Dockerfile looks like below

FROM openjdk:8
VOLUME /tmp
ADD target/helloworld.jar helloworld.jar
EXPOSE 8081
ENTRYPOINT ["java","-jar","helloworld.jar"]

Created image on local machine using command

docker build . -t helloworld:v1

Verified by creating container out of it. Checked in code to docker-hub account and github account.

Logged into Google cloud platform (GCP), created kubernetes cluster, created pipeline(using container builder) by configuring github url where helloworld microservice code resides. There are two options to run build (use Dockerfile or cloudbuild.yaml). I am using Dockerfile to run build.

When build is picked up to run, it fails for this line in Dockerfile

ADD target/helloworld.jar helloworld.jar

Error seen in GCP logs:

ADD failed: stat /var/lib/docker/tmp/docker-builderxxxxxx/target/helloworld.jar: no such file or directory

I tried to replace it with COPY command and still the issue is same.

Note: I tried to go with cloudbuild.yaml Here is how my cloudbuild.yaml looks:

  steps:
  # Build the helloworld container image.
  - name: 'gcr.io/cloud-builders/docker'
    args:
      - 'build'
      - '-t'
      - 'gcr.io/${PROJECT_ID}/helloworld:${TAG_NAME}'
      - '.'

This didn't make any difference. Issue remains the same.

Any idea if Springboot Java application has some specific configuration for Dockerfile to be built fine in Google Cloud Platform?


UPDATE - 1

Based on comments tried below steps on local machine:

  1. ran command mvn clean . That cleaned target folder

  2. updated Dockerfile

FROM maven:3.5-jdk-8 AS build
COPY src .
COPY pom.xml .
RUN mvn -f pom.xml clean package

FROM openjdk:8
VOLUME /tmp
COPY --from=build target/helloworld.jar helloworld.jar
EXPOSE 8081
ENTRYPOINT ["java","-jar","helloworld.jar"]

  1. Ran docker build . -t helloworld:v1 command and that created image.

  2. Then run command to start container: docker run -p 8081:8081 -n helloworld-app -d helloworld:v1

container starts and exits with error in log:

Exception in thread "main" java.lang.ClassNotFoundException: com.example.HelloWorldApplication at java.net.URLClassLoader.findClass(URLClassLoader.java:382)

Shivraj
  • 462
  • 2
  • 9
  • 27
  • Do you run the maven build in the cloud? – Thorbjørn Ravn Andersen Feb 16 '19 at 22:00
  • @ThorbjørnRavnAndersen No. Looks like it worked on my local as I had done maven build on my local before `docker build`. Is there any way to trigger maven build from cloudbuild.yaml before building docker container image? Or is there any command in Dockerfile to server the purpose? – Shivraj Feb 16 '19 at 23:01
  • You mention that you're triggering the build from a GitHub URL. Presumably your GitHub repo only contains your source and does not include the target directory. If so, you will need a precursor step that runs the `mvn package` to generate the target in Cloud Build so that you may then build a Docker image using it. See https://github.com/GoogleCloudPlatform/cloud-builders/blob/master/mvn/examples/spring_boot/cloudbuild.yaml – DazWilkin Feb 21 '19 at 05:15

1 Answers1

2

Looks like a problem with file paths.

Try the following updated Dockerfile, which explicitly sets the working directory. It also uses explicit file paths when copying the jar between images.

FROM maven:3.5-jdk-8-slim AS build
WORKDIR /home/app
COPY src     /home/app/src
COPY pom.xml /home/app
RUN mvn clean package

FROM openjdk:8-jre-slim
COPY --from=build /home/app/target/helloworld-0.0.1-SNAPSHOT.jar /usr/local/lib/helloworld.jar
EXPOSE 8081
ENTRYPOINT ["java","-jar","/usr/local/lib/helloworld.jar"]

Additional Notes:

  • See the related answer for a full example building a spring boot app
  • I've based the second stage on a JRE image. Reduces the size of the output image.
Mark O'Connor
  • 76,015
  • 10
  • 139
  • 185
  • This worked for me. I compared commands which I have used and what you have suggested- `COPY src .` and `COPY --from=build /target/helloworld.jar helloworld.jar` However, you suggested to use: `COPY src /home/app/src` and `COPY --from=build /home/app/target/helloworld.jar /usr/local/lib/helloworld.jar` Two questions: I didn't understand why there is need to have workingdirectory /home/app ? can't we just use current directory using `.` Why there is need to use`/usr/local/lib` ? – Shivraj Feb 23 '19 at 15:24
  • Course you can use the current directory.... but what is the current directory in the first stage build? You need to know the path when copying the jar into the second stage, so I think it's a good practice to explicitly spell it out. As for using "/usr/local"... that's just following the standard Linux directory convention for libs. Not hugely important. – Mark O'Connor Feb 25 '19 at 01:46