set linker in docker image made by jib java

1k views Asked by At

Ok, I encountered linking problems while creating jib docker image. I copy the files i want into container

jib {
    allowInsecureRegistries = true
    extraDirectories{
        paths{
            path{
                from = file('jnetpcap/jib')
                into = '/native'
            }
        }   
    }
.
.
.

and in other task, i point to those libraries

task cmdScript(type: CreateStartScripts) {
    mainClassName = "cic.cs.unb.ca.ifm.Cmd"
    applicationName = "cfm"
    outputDir = new File(project.buildDir, 'scripts')
    classpath = jar.outputs.files + project.configurations.runtime
    defaultJvmOpts = ["-Djava.library.path=/native"]
}

I checked, and those libraries are added correctly to container. It is not a problem with copying libs, but setting up linker.

cmdScript sets correct linker if i build project with distTar, but i don't know how to set up linker when building it with jibDockerBuild. I couldn't find anwer to my issue here so decided to seek help on SO.

UPDATE

I have found some clues here. I have updated my jib task by adding

jib {
    allowInsecureRegistries = true
    extraDirectories{
        paths{
            path{
                from = file('jnetpcap/jib')
                into = '/native'
            }
        }   
    }

container.jvmFlags = ["-Djava.library.path=/native/*"]

But I keep getting the same error.

error message is

exception in thread main java.lang.unsatisfiedlinkerror 'long com.slytechs.library.NativeLibrary.dlopen(java.lang.String)'
1

There are 1 answers

1
Chanseok Oh On BEST ANSWER

The issue is largely unrelated to Jib. The root cause is missing required libraries inside the container environment.

First things first, it should be container.jvmFlags = ["-Djava.library.path=/native"] (not /native/* with the asterisk).

Now, jNetPcap is a Java wrapper around Libpcap and WinPcap libraries found on various Unix and Windows platforms. That is, on Linux (which is the OS of the container you are building), it depends on Libpcap and requires it to be installed on the system. Most OpenJDK container images (including the one Jib uses as a base image) does not come with Libpcap pre-installed, and I suspect the first problem being that you are not installing Libpcap into the container.

jNetPcap also requires loading other native libraries. In the case of my example below, they were the two .so shared object files that come with the jNetPcap package: libjnetpcap-pcap100.so and libjnetpcap.so.

For explanation, below is the complete example that creates a working container image.

  • Dockerfile
# This Dockerfile is only for demonstration.

FROM adoptopenjdk/openjdk11

# "libpcap-dev" includes the following files:
# - /usr/lib/x86_64-linux-gnu/libpcap.a
# - /usr/lib/x86_64-linux-gnu/libpcap.so -> libpcap.so.0.8
# - /usr/lib/x86_64-linux-gnu/libpcap.so.0.8 -> libpcap.so.1.8.1
# - /usr/lib/x86_64-linux-gnu/libpcap.so.1.8.1
RUN apt-get update && apt-get install -y libpcap-dev

# My machine is x86_64 running Linux.
RUN curl -o jnetpcap.tgz https://master.dl.sourceforge.net/project/jnetpcap/jnetpcap/1.4/jnetpcap-1.4.r1300-1.linux.x86_64.tgz
# The tar includes the following files:
# - jnetpcap-1.4.r1300/jnetpcap.jar
# - jnetpcap-1.4.r1300/libjnetpcap-pcap100.so
# - jnetpcap-1.4.r1300/libjnetpcap.so
RUN tar -zxvf jnetpcap.tgz

# .class file compiled with "javac -cp jnetpcap.jar MyMain.java"
COPY MyMain.class /my-app/

ENTRYPOINT ["java", "-cp", "/my-app:/jnetpcap-1.4.r1300/jnetpcap.jar", "-Djava.library.path=/jnetpcap-1.4.r1300", "MyMain"]
  • MyMain.java
import java.util.*;
import org.jnetpcap.*;

public class MyMain {
    public static void main(String[] args) {
        Pcap.findAllDevs(new ArrayList<>(), new StringBuilder());
        System.out.println("SUCCESS!");
    }
}
$ docker build -t test .
$ docker run --rm test
SUCCESS!

Therefore, as long as you copy necessary dependent libraries and have correct configuration, you should be able to make it work with Jib.

For installing Libpcap, I can think of a couple options:

  • Prepare a custom base image (for example, apt-get install libpcap-dev as above) and configure jib.from.image to use it.
  • Manually download and copy the libpcap.so file into, say, /usr/lib, using the extraDirectories feature. (You can even make your Gradle project dynamically download the file when building your project.)

For copying jNetPcap native libraries (libjnetpcap-pcap100.so and libjnetpcap.so), it's the same story. However, looks like you already manually downloaded and attempted copying them using the extraDirectories feature, so I guess you can continue doing so. But still, preparing a custom base image is another viable option. Note that in the example above, I configured -Djava.library.path=... for jNetPcap (BTW, there are many other ways to have Linux and JVM load shared libraries in an arbitrary directory), but if you copy the .so files into some standard locations (for example, /usr/lib), you wouldn't even need to set -Djava.library.path.

For all of the native libraries (.so files) above, make sure to download the right binaries compatible with the container architecture and OS (probably amd64 and Linux in your case).