设置在使用 Jib 构建的 Java Docker 镜像中的链接器。

huangapple go评论85阅读模式
英文:

set linker in docker image made by jib java

问题

好的,以下是您要求的翻译部分:

我在创建 Jib Docker 镜像时遇到了链接问题。
我将想要的文件复制到了容器中。

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

在另一个任务中,我指向了那些库文件。

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"]
}

我检查过,这些库文件已正确添加到容器中。这不是复制库文件的问题,而是设置链接器的问题。

当我使用 distTar 构建项目时,cmdScript 会设置正确的链接器,但我不知道如何在使用 jibDockerBuild 构建时设置链接器。
我在这里 1 找不到解决方案,所以决定在 Stack Overflow 上寻求帮助。

更新

我在这里 2 找到了一些线索。
我已通过添加以下内容来更新我的 jib 任务:

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

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

但我仍然收到相同的错误。

错误信息如下:

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

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

得分: 2

问题与Jib基本无关。根本原因是容器环境中缺少所需的库。

首先,应该是 container.jvmFlags = ["-Djava.library.path=/native"] (而不是带有星号的 /native/*)。

现在,jNetPcap是围绕各种Unix和Windows平台上的LibpcapWinPcap库的Java封装。也就是说,在Linux上(这是您正在构建的容器的操作系统),它依赖于Libpcap,并且需要在系统上安装它。大多数OpenJDK容器映像(包括Jib使用作为基础映像的映像)不会预安装Libpcap,我怀疑第一个问题是您没有将Libpcap安装到容器中。

jNetPcap还需要加载其他本机库。在下面的示例中,它们是随jNetPcap软件包一起提供的两个 .so 共享对象文件:libjnetpcap-pcap100.solibjnetpcap.so

以下是创建工作容器映像的完整示例说明。

  • Dockerfile
# 此Dockerfile仅用于演示。

FROM adoptopenjdk/openjdk11

# "libpcap-dev" 包括以下文件:
# - /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

# 我的机器是运行 Linux 的 x86_64 架构。
RUN curl -o jnetpcap.tgz https://master.dl.sourceforge.net/project/jnetpcap/jnetpcap/1.4/jnetpcap-1.4.r1300-1.linux.x86_64.tgz
# 解压后包含以下文件:
# - jnetpcap-1.4.r1300/jnetpcap.jar
# - jnetpcap-1.4.r1300/libjnetpcap-pcap100.so
# - jnetpcap-1.4.r1300/libjnetpcap.so
RUN tar -zxvf jnetpcap.tgz

# 使用 "javac -cp jnetpcap.jar MyMain.java" 编译的 .class 文件
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!

因此,只要复制必要的依赖库并进行正确的配置,您应该能够在Jib中使其正常工作。

关于安装Libpcap,我可以想到几个选项:

  • 准备一个自定义的基础映像(例如,像上面那样安装 apt-get install libpcap-dev),并配置 jib.from.image 使用它。
  • 手动下载并将 libpcap.so 文件复制到例如 /usr/lib,使用 extraDirectories 功能。(您甚至可以在构建项目时动态下载文件。)

对于复制jNetPcap 本地库(libjnetpcap-pcap100.solibjnetpcap.so),情况类似。然而,看起来您已经手动下载并尝试使用 extraDirectories 特性进行复制,所以我猜您可以继续这样做。但仍然,准备一个自定义的基础映像是另一个可行的选项。请注意,在上面的示例中,我为 jNetPcap 配置了 -Djava.library.path=...(顺便说一下,还有许多其他方法可以让Linux和JVM加载任意目录中的共享库),但如果您将 .so 文件复制到一些标准位置(例如 /usr/lib),甚至不需要设置 -Djava.library.path

对于上述所有本地库(.so 文件),请确保下载适用于容器架构和操作系统的正确二进制文件(可能是您的情况下的amd64和Linux)。

英文:

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 = [&quot;-Djava.library.path=/native&quot;] (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

# &quot;libpcap-dev&quot; includes the following files:
# - /usr/lib/x86_64-linux-gnu/libpcap.a
# - /usr/lib/x86_64-linux-gnu/libpcap.so -&gt; libpcap.so.0.8
# - /usr/lib/x86_64-linux-gnu/libpcap.so.0.8 -&gt; libpcap.so.1.8.1
# - /usr/lib/x86_64-linux-gnu/libpcap.so.1.8.1
RUN apt-get update &amp;&amp; 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 &quot;javac -cp jnetpcap.jar MyMain.java&quot;
COPY MyMain.class /my-app/

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

public class MyMain {
    public static void main(String[] args) {
        Pcap.findAllDevs(new ArrayList&lt;&gt;(), new StringBuilder());
        System.out.println(&quot;SUCCESS!&quot;);
    }
}
$ 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).

huangapple
  • 本文由 发表于 2020年10月8日 14:34:11
  • 转载请务必保留本文链接:https://go.coder-hub.com/64256979.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定