Issue
I would like to package my Spring Boot application as a Docker container using Jib with Gradle.
More Other than following Jib's readme, I have copied a sample from an simple JHipster project. Guess what? It didn't work.
Okay, here is the code.
JHipster generator creates a dedicated gradle/docker.gradle
file that is applied from the main Gradle build file, and is importing the Jib plugin
So I copied the JH jib file to start
My gradle/jib.gradle
jib {
from {
// image = "adoptopenjdk:8-jre"
image = "adoptopenjdk:11-jre-hotspot"
}
to {
image = "myapp:latest"
}
container {
entrypoint = ["bash", "-c", "/entrypoint.sh"]
ports = ["8080"]
environment = [
SPRING_OUTPUT_ANSI_ENABLED: "ALWAYS",
SPRBOOT_SLEEP: "0"
]
creationTime = "USE_CURRENT_TIMESTAMP"
}
extraDirectories {
paths = file("src/main/jib")
permissions = ["/entrypoint.sh": "755"]
}
}
The commented part is because I want to use JDK8 but I am trying with what JHipster did.
Second, I copied my own entrypoint.sh
based on JH's repository. Here is src/main/jib/entrypoint.sh
#!/bin/sh
echo "The application will start in ${SPRBOOT_SLEEP}s..." && sleep "${SPRBOOT_SLEEP}" # Author's note: it's pointless
exec java "${JAVA_OPTS}" -noverify \
-XX:+AlwaysPreTouch \
-Djava.security.egd=file:/dev/./urandom \
-cp /app/resources/:/app/classes/:/app/libs/* \
"com.acme.MyApplication" "$@"
And of course I had to add the jib
plugin 3.0.0
to my Gradle file, applying the jib.gradle
script
plugins {
id 'org.springframework.boot' version '2.4.2'
id 'io.spring.dependency-management' version '1.0.11.RELEASE'
id 'java'
id 'war'
id "com.google.cloud.tools.jib" version '3.0.0'
id 'org.unbroken-dome.test-sets' version '3.0.1'
id 'org.flywaydb.flyway' version '7.5.4' apply false
id 'com.telenia.gradle.MybatisGenerator' version '2.1.5'
id 'net.ltgt.apt-eclipse' version '0.21' apply false
}
apply from: 'gradle/jib.gradle'
Explanation time
Now I expect that gradle jibDockerBuild
creates a container with my application's files under /app
, and starts the Spring Boot executable using the entrypoint. I don't care about the sleep, which is 0 and is inherited by JH
Of course, when I build the Docker image from the JHipster project, it works like a charm. When I build my own container, running it fails with the classic Unable to locate main class
error
The application will start in 0s...
Error: Could not find or load main class
Caused by: java.lang.ClassNotFoundException:
I tried to bash
into the container and there is no /app
directory at all. There is an /app
directory under JHipster's built container.
Trying to inspect both images while writing this post, I have found a few differences. JHipster deploys files under /app
, while my Gradle script deploys jars under /var/lib/jetty/webapps/ROOT
. My application enables the war
plugin consistently, while JHipster enables the plugin only if the -Pwar
variable is set.
I don't care about using war or embedded server in the image, as soon as the result is production-grade. I still need to be able to build a war file.
Question
How to properly configure an existing Gradle-based Spring Boot project to be built as a Docker container using Jib? What is wrong in my code, given I gave priority to the existing code from JHipster which is Spring Boot based?
Edit 1
Cool: On JHipster project, gradle -Pwar jibDockerBuild
deploys files under /jetty/webapps
inside the container and when I start the application I get the magic error Could not find or load main class
Solution
My mistake was not reading Jib documentation fully.
Jib decides what to do on War projects
Jib also containerizes WAR projects. If the Gradle project uses the WAR Plugin, Jib will by default use jetty as a base image to deploy the project WAR. No extra configuration is necessary other than using the WAR Plugin to make Jib build WAR images.
And this is likely the reason why JHipster demands a -Pwar
launch property to expose war tasks, so that you can either build the containerized version of Spring Boot jar, or the web-server-ed container of the resulting war file.
Shame on me for not reading the docs
Answered By - usr-local-ΕΨΗΕΛΩΝ
Answer Checked By - Mary Flores (JavaFixing Volunteer)