GoogleContainerTools / jib

🏗 Build container images for your Java applications.
Apache License 2.0
13.58k stars 1.43k forks source link

JIB-CLI uses outdated classname in entrypoint when dockerzing a Spring-Boot-3.2 executable JAR. #4175

Open stefan-prange opened 7 months ago

stefan-prange commented 7 months ago

Environment:

Description of the issue: Our backend is Spring Boot based. We're building executable JAR files with the "spring-boot-maven-plugin" and its "repackage" goal. Afterwards, we're dockerizing these JARs with jib-cli. Example command: jib jar --target ${TARGET_IMAGE} ${APP_JAR} --from ${BASE_IMAGE} ...

After upgrading from Spring Boot 3.1 to Spring Boot 3.2 we encountered the following problem with the Docker images created by jib-cli: Their entrypoint statements refer to an outdated Java class name. Current, wrong entrypoint: "java", "-cp", "/app", "org.springframework.boot.loader.JarLauncher"

Running the Docker image fails with a very short error output, that contains a ClassNotFoundException. The launcher class org.springframework.boot.loader.JarLauncher is not found in the JAR.

Problem analysis: With Spring Boot 3.2, the "JarLauncher" class moved to a different package. Its fully-qualified class name is now org.springframework.boot.loader.launch.JarLauncher (Please note the new .launch. naming part). In Jib cli's class SpringBootExplodedProcessor, the old Launcher class name is hard-coded in its method computeEntrypoint. See here

Solution proposal: The "SpringBootExplodedProcessor" should read the class to use in the image's entrypoint from the JAR file's MANIFEST.MF file (attribute main-class). That is, it should work like Jib's class "StandardExplodedProcessor" already does. Its method "computeEntrypoint" already evaluates the JAR's manifest.

Expected behavior: We expected Jib to use the same class in the Docker image's entrypoint as defined in the Main-Class attribute in the file META-INF/MANIFEST.MF, which is located inside the to-be-dockerized JAR file. As we built our JAR based on Spring Boot 3.2.2 and used Spring Boot's Maven Plugin (goal 'repackage') we expected that class to be org.springframework.boot.loader.launch.JarLauncher. But it's org.springframework.boot.loader.JarLauncher instead.

Steps to reproduce:

  1. Create a minimal Spring Boot application. Go to https://start.spring.io, let an app be created with

    • Build tool: Maven
    • Build tool: Java
    • Spring Boot version: 3.2.2
    • Packaging: JAR
    • Java: 17
    • Dependencies: none
  2. Download the generated project's code and build it with the command mvn package

  3. Navigate to the generated fat-jar. Its path might be demo/target/demo-0.0.1-SNAPSHOT.jar (depending on the app name you chose in step 1).

  4. Unpack the JAR, navigate to its META-INF/MANIFEST.MF file, open that file and look at its Main-Class attribute. Its value is org.springframework.boot.loader.launch.JarLauncher.

  5. Use jib-cli to dockerize that JAR. Example command: jib jar --target target-image-tag:v0.1 demo-0.0.1-SNAPSHOT.jar --from base-image-tag:v0.1

  6. Inspect the generated Docker image with "docker inspect". You'll find the image's entrypoint to contain a different class name than the one detected in step 4.:

            "Entrypoint": [
                "java",
                "-cp",
                "/app",
                "org.springframework.boot.loader.JarLauncher"
            ],

Additional Information: I already fixed the problem in my local version of jib's code base and the fix solves the problem described above. I'll send a PR soon. It turned out that Jib-cli's class StandardExplodedProcessor already uses the Main-Class from the JAR's manifest. So basically, I just had to copy its computeEntrypoint logic to the SpringBootExplodedProcessor class. Of course, I also extended SpringBootExplodedProcessor's unit test.

mpeddada1 commented 1 month ago

Thanks for bringing this up! However, as a workaround, I believe the jib cli command has a way to customize the entrypoint through this parameter.

stefan-prange commented 1 month ago

Thanks @mpeddada1 for pointing out the workaround again. I was using the --entrypoint parameter wrong and tried it again now. Now I learned how to use it and it works fine for me :-) . We'll start using that parameter in our build process. By using the workaround, this issue is solved for me, but nonetheless adapting JIB to new Spring Boot versions would be a good idea.