在CentOS Linux上如何从Tomcat访问Tensorflow

6
我有一个Java演示程序, 它使用Tensorflow进行图像分类。在Windows上运行得很好,但现在我想将它作为Java Tomcat Web服务器的Web服务运行。
我已经将所有Tensorflow JAR文件添加到Tomcat的lib目录中,但Tensorflow有一个jni依赖项。我不知道如何安装和链接它,以便Tensorflow可以在CentOS Linux服务器上运行。 我已经阅读了这篇文章, 但我不需要在服务器上运行Python,只需从Java访问Tensorflow。
更新:** 好的,为了让它在Windows上的Tomcat上工作,我按照以下步骤操作,
从以下网址下载libtensorflow.jar, https://storage.googleapis.com/tensorflow/libtensorflow/libtensorflow-1.6.0.jar

然后从https://storage.googleapis.com/tensorflow/libtensorflow/libtensorflow_jni-cpu-windows-x86_64-1.6.0.zip下载dll(解压缩zip文件以获取dll)

请查看https://www.tensorflow.org/install/install_java

将jar文件放入我的tomcat lib中,并创建一个tomcat dll目录并将dll放入其中

编辑我的setenv.bat文件并添加以下行:

SET CATALINA_OPTS=-Xmx4g -XX:PermSize=128m -XX:MaxPermSize=512m -Djava.library.path=D:\Engineering\apache-tomcat-7.0.50\dll

这适用于Windows。

对于Linux、CentOS 6,我做同样的事情,但是不是下载dll文件,而是从以下链接下载so文件:

https://storage.googleapis.com/tensorflow/libtensorflow/libtensorflow_jni-cpu-linux-x86_64-1.6.0.tar.gz

然后编辑我的setenv.sh文件并添加以下行:

export LD_LIBRARY_PATH="$LD_LIBRARY_PATH:/usr/local/tomcat8/so"
export JAVA_OPTS="-server -Xmx38g -Djava.library.path=/usr/local/tomcat8/so"
export CATALINA_OPTS="-Djava.library.path=/usr/local/tomcat8/so"

但是这些方法似乎都不起作用,我总是会收到错误信息:

无法找到适用于操作系统:linux,架构:x86_64的TensorFlow本地库。请参见https://github.com/tensorflow/tensorflow/tree/master/tensorflow/java/README.md以获取可能的解决方案(例如从源代码构建库)。通过将org.tensorflow.NativeLibrary.DEBUG=1添加到JVM的系统属性中,可以获取有关查找本地库尝试的其他信息。

我发现还有另一种部署选项,只需将jar文件添加到lib中,它就会神奇地找到正确的so文件。

https://mvnrepository.com/artifact/org.tensorflow/libtensorflow_jni

当我尝试此选项时,似乎找到了so文件,但是出现了以下错误:

/usr/local/tomcat8/temp/tensorflow_native_libraries-1522357321965-0/libtensorflow_jni.so:/lib64/libc.so.6:版本“GLIBC_2.16”未找到(需要/usr/local/tomcat8/temp/tensorflow_native_libraries-1522357321965-0/libtensorflow_jni.so)

看起来TensorFlow只支持非常特定的操作系统和版本?

我找到了这个链接:
Error while importing Tensorflow in python2.7 in Ubuntu 12.04. 'GLIBC_2.17 not found'

但是还没有尝试任何选项。对于生产系统来说并不是很有前途。

看一下GLIBC是什么,它是为GPU设计的,但我没有也不需要使用GPU,为什么需要这个库呢?

** 更新 所以...我尝试在Centos6上构建glibc 1.6.0,以便我可以按照以下步骤使用它,

https://unix.stackexchange.com/questions/176489/how-to-update-glibc-to-2-14-in-centos-6-5

这些步骤有效,但在尝试运行Tensorflow时出现了此错误,似乎它依赖于另一个库...

加载共享库时出错:__vdso_time:dlopen()的无效模式

此时我已经准备放弃,并尝试安装Centos7,但这条路需要我们升级12个生产服务器...

@James,你的项目是Maven项目吗? - hovanessyan
不使用Maven,运行Tomcat时,应用程序部署到Tomcat的Webapps目录下,所有JAR包都在Tomcat的lib目录下,因此文件位于Tomcat的so目录下。问题是Tomcat似乎无法识别so路径,或者TensorFlow不喜欢这些so文件。 - James
@James 我已更新答案,包括在Linux上托管的专用Tomcat Web服务器的设置。 - hovanessyan
请在您的CentOS服务器上运行“ldd --version”命令 - 这是glibc软件包的一部分,将告诉您glibc版本。无论是CPU还是GPU配置,tensorflow都需要glibc。从错误消息来看,您的glibc版本很可能早于2.16。请告诉我当前的glibc版本,我会考虑可选方案。 - hovanessyan
Centos 6使用2.12版本。 - James
显示剩余7条评论
4个回答

3
嗯,我对Java中的TensorFlow知之甚少。然而,我做了一些研究,并相信我找到了解决您问题的结论。
当然,如果您阅读@Igor的解决方案,您在此处发布的解决方案实际上可以缓解问题。
为了更好地理解问题:TensorFlow在Java中的工作方式是,您的包调用Python库,该库在底层实际上调用C代码,这是库的主要核心和功能所在。因此,您可以将Java包视为Python TensorFlow的包装器,后者是C库的包装器。
请回想一下,Linux操作系统是用C构建的,并且几乎总是预装了Glibc作为系统要求,如此处的前几行所述。也就是说,您面临的问题是TensorFlow需要的GLibc是最新版本,而不是运行您的操作系统的相同版本。
如果您在此处阅读问题,您将看到类似的问题,其中运行的操作系统是与您相同的Cent OS。那个特定问题的唯一区别是,有问题的人使用Python而不是Java,但问题是相同的。
因此,您有几种可能的方法来解决它。
  1. 在另一个基于Linux的操作系统上运行代码,其中Glibc与TensorFlow兼容(或可以轻松更新)。
  2. 全局升级系统GLIBC。如果您在服务器上运行此操作,则非常痛苦,并且已在此处得到了很好的记录。(从我理解的内容来看,您可能只需安装Python即可解决此问题。抱歉,我没有完全阅读文章)。
  3. 向系统添加第二个GLIBC(风险较高)。
  4. 从源代码编译Glibc和Bazel。(在第一个选项之后,这听起来是最有可能的解释)。
  5. 根据@Igor在此帖子中建议的方式,从源代码编译TensorFlow以在当前glibc上工作。我不知道这是否有效,因为我不确定TensorFlow库可能调用哪些C功能。
希望这个回答至少有点帮助。干杯!

我认为除了升级操作系统之外,没有其他好的选择。Centos 7于2014年发布,从6.x系列转移是一个好主意,迟早都得这么做。为了使软件与旧版GLIBC兼容,使用旧版GCC几乎是必须的,但很可能无法编译现代/最新的Tensorflow代码。 - user158037
我同意,在未来几个月内,tensorflow可能需要越来越多的更新。目前,Centos 7.4使用glibc 2.17,可能是最快的解决方案。您仍然需要执行已经在Centos 6上工作过的步骤,但这比重新设计所有内容要好。 - Haris Nadeem

2
这个问题与Java无关,它涉及到古老的C语言和本地库链接。简而言之:
1. Tensorflow的Java库通过JNI(Java Native Interface)调用本地库。
2. 这个本地库(位于.jar文件中的.so文件)是在比Centos6更新的Linux发行版下编译的,可能是Ubuntu LTS,因此它与更新版本的glibc库相关联。
没有简单的方法手动更新glibc并保持系统稳定,最好的办法是升级到CentOS 7,它已经预装了所需版本的glibc: https://rpmfind.net/linux/rpm2html/search.php?query=libc.so.6%28GLIBC_2.16%29%2864bit%29&submit=Search+...&system=centos&arch=

1

免责声明

请注意,本答案较长,因为它回答了最初发布的问题以及在评论和讨论中提供更多信息时出现的其他问题。

更新2:

提供的新信息表明,CentOS6服务器上的glibc版本比tensorflow二进制文件编译的glibc版本要旧。要将CentOS6服务器上的glibc版本更新为更新版本,您可以尝试按照此升级脚本(原始链接)中描述的步骤进行操作。

我建议升级整个服务器,而不仅仅是glibc

您当前服务器上的许多其他命令都是根据当前的glibc版本编译的。如果您升级该库,则可能会遇到兼容性问题,这可能导致服务器完全崩溃。

#! /bin/sh

# update glibc to 2.17 for CentOS 6

wget http://copr-be.cloud.fedoraproject.org/results/mosquito/myrepo-el6/epel-6-x86_64/glibc-2.17-55.fc20/glibc-2.17-55.el6.x86_64.rpm
wget http://copr-be.cloud.fedoraproject.org/results/mosquito/myrepo-el6/epel-6-x86_64/glibc-2.17-55.fc20/glibc-common-2.17-55.el6.x86_64.rpm
wget http://copr-be.cloud.fedoraproject.org/results/mosquito/myrepo-el6/epel-6-x86_64/glibc-2.17-55.fc20/glibc-devel-2.17-55.el6.x86_64.rpm
wget http://copr-be.cloud.fedoraproject.org/results/mosquito/myrepo-el6/epel-6-x86_64/glibc-2.17-55.fc20/glibc-headers-2.17-55.el6.x86_64.rpm

sudo rpm -Uvh glibc-2.17-55.el6.x86_64.rpm \
glibc-common-2.17-55.el6.x86_64.rpm \
glibc-devel-2.17-55.el6.x86_64.rpm \
glibc-headers-2.17-55.el6.x86_64.rpm

翻译后的回答:

有一个包含tensorflow JNI分发的jar文件。

您可以使用与tensorflow.jar匹配的版本1.6.0简单地下载tensorflow_jni.jar,并将其打包到您的应用程序旁边。 JNI jar将位于类路径上,并将自动捡起。

您也可以将tensorflow_jni.jar复制粘贴到Tomcat的lib文件夹中。

tensorflow_jni.jar已配置为使用CPU,如果您想使用GPU,则可以下载 tensorflow_jni_gpu.jar

Demo:

我制作了一个演示应用程序,将其部署为war包到专用的Tomcat 8.5.29上,其中包含一个单一的rest端点,打印tensorflow版本。我可以确认提供tensorflow.jartensorflow_jni.jar两个文件即可正常工作,无需任何额外的配置或调整。

我已经将测试应用程序上传到我的github账户。您可以查看它,将其打包为war文件(使用mvn package或其他工具),并在Tomcat中部署它。

要按照所述方式进行打包,需要使用maven,但在这种情况下,maven的主要目的是下载pom文件中声明的必要依赖项。

如果您不想使用maven,可以手动从上面提供的链接下载依赖项,并将它们合并到应用程序设置中。

更新-在专用Tomcat中配置本地库

以下是我在专用Tomcat8中进行设置的方法,其中所有tensorflow依赖项都在Web服务器中进行配置,而不是随部署的应用程序一起提供。

1) 这是我的 war 依赖项情况 - 它没有任何 tensorflow 依赖项:

war dependencies

为了在链接的项目中生成它,您只需要在 pom.xml 文件中将 tensorflow 依赖项标记为 provided 即可:
<dependency>
    <groupId>org.tensorflow</groupId>
    <artifactId>tensorflow</artifactId>
    <version>1.6.0</version>
    <scope>provided</scope> <!-- add this line -->
</dependency>

请从目标目录中获取demo-tensorflow-0.0.1-SNAPSHOT.war.original文件(部署到tomcat之前请删除 .original 后缀)。

2) 这是文件系统上SO文件的路径,反映了您指定的路径:

so location

3) Tomcat 的 lib 文件夹:

tomcat libs

4) 如果我在Tomcat中部署war包并尝试访问REST端点,我将会得到与您所遇到的相同的错误:

error jni

5) 我已经在 CATALINA_BASE 中创建了 setenv.sh 文件(我仅在 CATALINA_OPTS 中添加了库路径以便更清晰明了)。

export LD_LIBRARY_PATH="$LD_LIBRARY_PATH:/usr/local/tomcat8/so"
export JAVA_OPTS="-server -Xmx38g"
export CATALINA_OPTS="-Djava.library.path=/usr/local/tomcat8/so"

然后

chmod u+x setenv.sh

6) 运行Tomcat时,我可以在日志消息中看到配置已被拾取:

enter image description here

7) 这次访问应用程序成功:

enter image description here


TensorFlow根据您需要的GPU或CPU支持具有不同的库依赖项/so文件,这个库jar使用哪一个? - James
我已经更新了我的回答 - tensorflow_jni.jar是CPU配置的,tensorflow_jni_gpu.jar是GPU配置的。 - hovanessyan
在使用CentOS 6时,TensorFlow只能与特定的操作系统和版本配合使用吗? - James
我也在使用Tomcat 7。 - James
当我们使用预构建的二进制文件时,它是针对特定版本的其他库进行构建的。因此,当您运行预构建的tensorflow jni二进制文件时,它期望使用与其构建的库的特定版本。您必须提供这些库的版本(或兼容版本),或者在您自己的环境中使用所需的库构建tensorflow源代码(因此使用您在CentOS服务器上拥有的版本)。我认为解决这个问题最快的方法是设置一个新的服务器,并安装更高版本的glibc库。 - hovanessyan
显示剩余3条评论

1
我刚刚仔细看了一下。
只需将依赖项添加到您喜欢的构建工具中的org.tensorflow:tensorflow:1.4.0-rc0(或您喜欢的任何版本)即可。
这将引入对org.tensorflow:libtensorflow_jni:1.4.0-rc0的依赖。这将包括以下内容:
blafasel@localhost:~$ unzip -t .m2/repository/org/tensorflow/libtensorflow_jni/1.4.0-rc0/libtensorflow_jni-1.4.0-rc0.jar
Archive:  .m2/repository/org/tensorflow/libtensorflow_jni/1.4.0-rc0/libtensorflow_jni-1.4.0-rc0.jar
    testing: META-INF/                OK
    testing: META-INF/MANIFEST.MF     OK
    testing: org/                     OK
    testing: org/tensorflow/          OK
    testing: org/tensorflow/native/   OK
    testing: org/tensorflow/native/darwin-x86_64/   OK
    testing: org/tensorflow/native/linux-x86_64/   OK
    testing: org/tensorflow/native/windows-x86_64/   OK
    testing: org/tensorflow/native/darwin-x86_64/libtensorflow_framework.so   OK
    testing: org/tensorflow/native/darwin-x86_64/LICENSE   OK
    testing: org/tensorflow/native/darwin-x86_64/libtensorflow_jni.dylib   OK
    testing: org/tensorflow/native/linux-x86_64/libtensorflow_framework.so   OK
    testing: org/tensorflow/native/linux-x86_64/libtensorflow_jni.so   OK
    testing: org/tensorflow/native/linux-x86_64/LICENSE   OK
    testing: org/tensorflow/native/windows-x86_64/tensorflow_jni.dll   OK
    testing: org/tensorflow/native/windows-x86_64/LICENSE   OK
    testing: META-INF/maven/          OK
    testing: META-INF/maven/org.tensorflow/   OK
    testing: META-INF/maven/org.tensorflow/libtensorflow_jni/   OK
    testing: META-INF/maven/org.tensorflow/libtensorflow_jni/pom.xml   OK
    testing: META-INF/maven/org.tensorflow/libtensorflow_jni/pom.properties   OK
No errors detected in compressed data of .m2/repository/org/tensorflow/libtensorflow_jni/1.4.0-rc0/libtensorflow_jni-1.4.0-rc0.jar.

正如您所看到的,这已经包含了所有必要的二进制文件,以在所有官方支持的平台上使JNI工作。 这包括x86_64上的任何Linux。

只要您不尝试在 树莓派 或32位CentOS上使用它,并且使用适当的构建工具,您就应该是安全的。

唯一的风险在于这些库对其他系统库的依赖关系。 对 libtensorflow_framework.so 进行 ldd 调用显示:

blafasel@localhost:~$ ldd org/tensorflow/native/linux-x86_64/libtensorflow_framework.so
    linux-vdso.so.1 =>  (0x00007ffffaa62000)
    libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007f07c6494000)
    libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007f07c6290000)
    libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007f07c6073000)
    libstdc++.so.6 => /usr/lib/x86_64-linux-gnu/libstdc++.so.6 (0x00007f07c5cf0000)
    libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007f07c5ada000)
    libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f07c5710000)
    /lib64/ld-linux-x86-64.so.2 (0x000056525c661000)

如果您在系统上找不到这些传递依赖项,可能应该尝试较旧版本的tensorflow或更新的CentOs。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接