GoogleContainerTools / jib

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

ClassNotFoundException when using .nar dependencies #3888

Open salvalabajos opened 1 year ago

salvalabajos commented 1 year ago

Environment:

Description of the issue: When packaging a Java application with jib-gradle-plugin and running the resulting docker image the application crashes with a ClassNotFoundException. In our particular example the error was ClassNotFoundException: com.scurrilous.circe.checksum.Crc32cIntChecksum, which is a dependency of Pulsar.

Expected behavior: The application should run fine and all dependencies should be included in the docker image.

Steps to reproduce: I tried creating a project with as little dependencies as possible but there aren't that many .nar libraries out there. So I used Pulsar but I don't know how to reproduce the class not found error, there must be some specific configuration for Pulsar that I am missing.

Anyways, I think we can clearly see the problem without getting to the ClassNotFoundException:

  1. Add pulsar as a dependency to your project:
    dependencies {
    implementation 'org.apache.pulsar:pulsar-client-original:2.10.2'
    }
  2. Create the docker image with jib:
    jib.from {
    image = 'amazoncorretto:11'
    }
    jib.to {
    image = "proof-of-concept:latest"
    }
    jib.container {
    mainClass = 'org.example.Main'
    }
  3. Generate the image and run it.
  4. Inside the container execute cat /app/jib-classpath-file to see the list of dependencies. You will not see circe-checksum-4.14.5.nar nor cpu-affinity-4.14.5.nar.
  5. If you look at the libs folder with ls /app/libs/ | grep nar you will see the .nar files there.

So this means the dependencies are copied to the image, as they should, but they are not included in the classpath file and thus are "invisible" to the application, which explains the class not found.

Additional Information: I think the issue is the way jib calculates dependencies:

  @Override
  public List<Path> getDependencies() {
    List<Path> dependencies = new ArrayList<>();
    FileCollection runtimeClasspath = project.getConfigurations().getByName(configurationName);
    // To be on the safe side with the order, calling "forEach" first (no filtering operations).
    runtimeClasspath.forEach(
        file -> {
          if (file.exists()
              && file.isFile()
              && file.getName().toLowerCase(Locale.US).endsWith(".jar")) {
            dependencies.add(file.toPath());
          }
        });
    return dependencies;
  }

https://github.com/GoogleContainerTools/jib/blob/master/jib-gradle-plugin/src/main/java/com/google/cloud/tools/jib/gradle/GradleProjectProperties.java#L309 This excludes anything that is not a .jar file and would explain the problem.

Workaround: If you are facing this problem, one thing you can do is override the entrypoint of the docker image like this:

jib.container {
    mainClass = 'org.example.Main'
    entrypoint = [
        '/bin/bash',
        '-c',
        'java -cp $(cat /app/jib-classpath-file):$(echo /app/libs/*.nar | tr \' \' \':\') @/app/jib-main-class-file'
    ]
}

This will add all *.nar files from /app/libs/ to the classpath, on top of whatever the jib-classpath-file contains.

mpeddada1 commented 1 year ago

@salvalabajos Thanks for the analysis and providing a workaround to demonstrates how the jib.container.entrypoint can be leveraged in case you don't want to rely on the default entrypoint. Adding in a reference doc link as a supplement: https://github.com/GoogleContainerTools/jib/blob/afe98e7f23a0ea56b6b3bbde29600280777fe75e/jib-gradle-plugin/README.md#custom-container-entrypoint

Abhijeetmishr commented 1 year ago

@mpeddada1 Is this issue need fix should I pick this up ?

mpeddada1 commented 1 year ago

@Abhijeetmishr Thanks for reaching out. We are accepting contributions for this issue.

Abhijeetmishr commented 1 year ago

@mpeddada1 Hey Mridula, I have tried building project locally using this command ./gradlew build but I am getting some error any hints/suggestion.

Regrads