keeganwitt / docker-gradle

Docker images with Gradle
https://hub.docker.com/_/gradle/
Apache License 2.0
147 stars 73 forks source link

Running Docker in Jenkins: Failed to load native library 'libnative-platform.so' for Linux amd64. #271

Closed DenWav closed 9 months ago

DenWav commented 10 months ago

Trying to run a build using the 8.5.0-jdk21 image, I get this error:

19:01:24  * What went wrong:
19:01:24  Gradle could not start your build.
19:01:24  > Could not initialize native services.
19:01:24     > Failed to load native library 'libnative-platform.so' for Linux amd64.
19:01:24  
19:01:24  * Try:
19:01:24  > Run with --info or --debug option to get more log output.
19:01:24  > Run with --scan to get full insights.
19:01:24  > Get more help at https://help.gradle.org/.
19:01:24  
19:01:24  * Exception is:
19:01:24  org.gradle.initialization.exception.InitializationException: Gradle could not start your build.
19:01:24    at org.gradle.launcher.cli.ExceptionReportingAction.execute(ExceptionReportingAction.java:49)
19:01:24    at org.gradle.launcher.cli.ExceptionReportingAction.execute(ExceptionReportingAction.java:26)
19:01:24    at org.gradle.launcher.cli.DefaultCommandLineActionFactory$WithLogging.execute(DefaultCommandLineActionFactory.java:361)
19:01:24    at org.gradle.launcher.Main.doAction(Main.java:35)
19:01:24    at org.gradle.launcher.bootstrap.EntryPoint.run(EntryPoint.java:50)
19:01:24    at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103)
19:01:24    at java.base/java.lang.reflect.Method.invoke(Method.java:580)
19:01:24    at org.gradle.launcher.bootstrap.ProcessBootstrap.runNoExit(ProcessBootstrap.java:60)
19:01:24    at org.gradle.launcher.bootstrap.ProcessBootstrap.run(ProcessBootstrap.java:37)
19:01:24    at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103)
19:01:24    at java.base/java.lang.reflect.Method.invoke(Method.java:580)
19:01:24    at org.gradle.launcher.GradleMain.main(GradleMain.java:34)
19:01:24  Caused by: org.gradle.internal.service.ServiceCreationException: Could not initialize native services.
19:01:24    at org.gradle.internal.nativeintegration.services.NativeServices.initialize(NativeServices.java:159)
19:01:24    at org.gradle.internal.nativeintegration.services.NativeServices.initializeOnClient(NativeServices.java:135)
19:01:24    at org.gradle.launcher.cli.NativeServicesInitializingAction.execute(NativeServicesInitializingAction.java:42)
19:01:24    at org.gradle.launcher.cli.NativeServicesInitializingAction.execute(NativeServicesInitializingAction.java:26)
19:01:24    at org.gradle.launcher.cli.ExceptionReportingAction.execute(ExceptionReportingAction.java:41)
19:01:24    ... 11 more
19:01:24  Caused by: net.rubygrapefruit.platform.NativeException: Failed to load native library 'libnative-platform.so' for Linux amd64.
19:01:24    at net.rubygrapefruit.platform.internal.NativeLibraryLoader.load(NativeLibraryLoader.java:67)
19:01:24    at net.rubygrapefruit.platform.Native.init(Native.java:56)
19:01:24    at org.gradle.internal.nativeintegration.services.NativeServices.initializeNativeIntegrations(NativeServices.java:170)
19:01:24    at org.gradle.internal.nativeintegration.services.NativeServices.initialize(NativeServices.java:155)
19:01:24    ... 15 more
19:01:24  Caused by: java.io.IOException: No such file or directory
19:01:24    at java.base/java.io.UnixFileSystem.createFileExclusively0(Native Method)
19:01:24    at java.base/java.io.UnixFileSystem.createFileExclusively(UnixFileSystem.java:258)
19:01:24    at java.base/java.io.File.createNewFile(File.java:1045)
19:01:24    at net.rubygrapefruit.platform.internal.NativeLibraryLocator.find(NativeLibraryLocator.java:45)
19:01:24    at net.rubygrapefruit.platform.internal.NativeLibraryLoader.load(NativeLibraryLoader.java:45)
19:01:24    ... 18 more

I've tested this locally on macOS without issues, with and without volume binding. On Jenkins it gives this error regardless of the volume binding or not. I'm running under the Jenkins user, which from my research seems to be the likely cause of this issue, though I'm not sure what a good fix would be considering running as root is not an option.

keeganwitt commented 10 months ago

I'm running under the Jenkins user, which from my research seems to be the likely cause of this issue

That is likely the cause, yes. It was after multiple folks had similar problems that I reluctantly made root the default user.

What matters is that the UID of your running user (id -u) in the container matches the UID of the mounted volume (ls -l), since is the permission Linux will check.

I'm not sure how you're executing this. If you're using Kubernetes, you could use fsgroup to use an ID that has permission on the volume. If you're executing directly with Docker, I'm not aware of any argument to run that can change this. You have to either run as a user that has access (such as root) or chown the files you're going to mount.

DenWav commented 10 months ago

Thanks, that's good info. What is confusing to me is the volume I have mounted is owned by the Jenkins user - I wish the error message Gradle gave also specified which file it's trying to read or write then maybe I could track down the specific issue. Currently the permissions appear to be correct, but I'll keep digging.

pschyma commented 9 months ago

Hi, I ran into the same issue after upgrading from gradle:17-focal to gradle:21-jammy. In contrast to @DenWav, I'm not even able to run a build with a user mapping even locally.

The error stems from the fact that /home/gradle is owned by root and does not allow access from any other user. It can be "fixed" by allowing any other user access to that folder, e.g.

FROM gradle:jdk21-jammy

RUN chmod -R o+rwx /home/gradle
keeganwitt commented 9 months ago

The error stems from the fact that /home/gradle is owned by root

It is owned by the gradle user actually. I symlinked /root/gradle to that directory to handle when you run the container as root.

# ls -dl /home/gradle
drwxr-x--- 1 gradle gradle 4096 Dec 16 10:55 /home/gradle

The gradle user is uid 1000. I suspect it wasn't working for you because your running user is a different UID. But I'm a bit confused why it would try to use that home directory, if it's not that user.

I'm not even able to run a build with a user mapping even locally.

@pschyma if you're having a local failure, can you provide reproduction steps?

keeganwitt commented 9 months ago

Is running the container as root not an option?

DenWav commented 9 months ago

To be clear what I was trying to mount was /var/lib/jenkins/.gradle:/home/gradle/.gradle so that Gradle caches could be shared like normal across builds (/var/lib/jenkins/ being Jenkins' home directory).

I tried also mounting just the home directory as the volume with /var/lib/jenkins:/home/gradle but it gave the same error.

Seeing that the .gradle directory is a volume so will at least by shared by the container makes me less concerned about that so I just dropped trying to mount those directories entirely.

But unfortunately that did not fix the issue. What confuses me is I never had this issue at all using the jdk17 Docker image, only jdk21. I'm trying to work out what the difference could be but I don't really see anything.

DenWav commented 9 months ago

Yup, changing nothing other than the image from gradle:8.5.0-jdk21 to gradle:8.5.0-jdk21 does indeed fix the issue. Something about the jdk21 image is different and causing it to break in a way that jdk17 doesn't. This is the command Jenkins seems to be generating to execute the Docker build:

docker run -t -d -u 1098:1002 -w /var/lib/jenkins/workspace/project_workspace -v /var/lib/jenkins/workspace/project_workspace:/var/lib/jenkins/workspace/project_workspace:rw,z -v /var/lib/jenkins/workspace/project_workspace@tmp:/var/lib/jenkins/workspace/project_workspace@tmp:rw,z -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** gradle:8.5.0-jdk21 <command>

Exchange gradle:8.5.0-jdk21 in the above command with gradle:8.5.0-jdk17 and the error no longer occurs.

The environment variables simply being censored as they contain secrets / sensitive values (Jenkins actually censors them).

keeganwitt commented 9 months ago

Yup, changing nothing other than the image from gradle:8.5.0-jdk21 to gradle:8.5.0-jdk21 does indeed fix the issue.

I assume you meant to gradle:8.5.0-jdk17 here...

What confuses me is I never had this issue at all using the jdk17 Docker image, only jdk21. I'm trying to work out what the difference could be but I don't really see anything.

I'm also unaware of any difference between those two images. Literally the only difference between the two is the version of the base image of Eclipse Temurin.

However, I am able to reproduce this locally, so you are right (but I don't understand why).

PS> docker run --rm -u 1098:1002 -w /var/lib/jenkins/workspace/project_workspace -v "${pwd}:/var/lib/jenkins/workspace/project_workspace" gradle:jdk21 gradle --no-daemon clean

To honour the JVM settings for this build a single-use Daemon process will be forked. For more on this, please refer to https://docs.gradle.org/8.5/userguide/gradle_daemon.html#sec:disabling_the_daemon in the Gradle documentation.
Daemon will be stopped at the end of the build
> Task :lib:clean UP-TO-DATE

BUILD SUCCESSFUL in 6s
1 actionable task: 1 up-to-date

PS> docker run --rm -u 1098:1002 -w /var/lib/jenkins/workspace/project_workspace -v "${pwd}:/var/lib/jenkins/workspace/project_workspace" gradle:jdk21 gradle --no-daemon clean

FAILURE: Build failed with an exception.

* What went wrong:
Gradle could not start your build.
> Could not initialize native services.
   > Failed to load native library 'libnative-platform.so' for Linux amd64.

* Try:
> Run with --stacktrace option to get the stack trace.
> Run with --info or --debug option to get more log output.
> Run with --scan to get full insights.
> Get more help at https://help.gradle.org.
keeganwitt commented 9 months ago

I think chmodding the .gradle directory to be writable by other users makes sense for this use case. I've opened a PR for this.

The only other alternative I can think of would be to configure Gradle using the -g or --gradle-user-home command-line options or GRADLE_USER_HOME env var or gradle.user.home property to point to a newly created volume. I confirmed this works locally with

PS> docker run --rm -u 1098:1002 -w /var/lib/jenkins/workspace/project_workspace -v "${pwd}:/var/lib/jenkins/workspace/project_workspace" -v "$home\Desktop\newUser:/home/newUser" -e GRADLE_USER_HOME=/home/newUser/.gradle gradle:jdk21 gradle --no-daemon clean

But I'm still at a loss for the difference between the images. My guess is that something changed in the base image, but I'm not sure what.

DenWav commented 9 months ago

Thanks for the fix!

I checked the base Temurin Docker images and couldn't find any relevant differences. I also checked the JDK implementation of File.createNewFile, including the C++ code involved, and still couldn't find any differences. I have no idea what the difference is. Thanks for providing a workaround regardless, I appreciate it.