Jetson TX1上安装Tensorflow Serving遇到的问题总结

本文的目的是分享在TX1上安装Tensorflow Serving时遇到的主要问题,避免重复踩坑。

Jetson TX1是一块带GPU的板子,预装了ubuntu系统,ARM架构,详情可参考NVidia官网。对于Tensorflow训练出来的模型,工程部署一般都采用Tensorflow Serving。目前在网络上暂时没找到Jetson系列板子上成功安装Tensorflow Serving的案例,而本人在安装过程中遇到的很多问题都搜不到解决方法,只能自己摸索,最终成功安装并运行。当前TX1环境是用的JetPack 3.2.1(CUDA9 + cuDNN7)。

这里安装步骤主要参考官网的指导:https://www.tensorflow.org/serving/setup,但有些细节会不一样,比如安装命令是bazel build -c opt --config=cuda tensorflow_serving/... --action_env=PYTHON_BIN_PATH=/usr/bin/python,这里config=cuda是必要的,否则即使在bazel.rc中指定了gpu,生成出的Tensorflow Serving也不能用GPU。

原本想尝试交叉编译的,因为板子上编译很慢,但考虑板子上编译坑会少点,所以这里只尝试了在板子上编译的方式。安装遇到的问题这里只列出主要的。

问题1:

ERROR: no such target '@org_tensorflow//third_party/gpus/crosstool:crosstool': target 'crosstool' not declared in package 'third_party/gpus/crosstool' defined by /data/rootcache/bazel/_bazel_root/46688ad2577b25fcaed4521437622fa6/external/org_tensorflow/third_party/gpus/crosstool/BUILD

分析与解决:

这个报错的含义是:crosstool的定义找不到,而根据bazel.rc中的配置,crosstool期望在下面列出的那个BUILD文件里定义,而那个BUILD文件里没定义crosstool。

事实上,这个BUILD文件是空的。网上能搜到的解决方案比较多,下面的方案至少是验证可行的

(1) 修改serving/tools/bazel.rc文件,将@org_tensorflow//third_party/gpus/crosstool替换成@local_config_cuda//crosstool:toolchain。

(2) 执行bazel clean --expunge && export TF_NEED_CUDA=1

(3) 再执行bazel query 'kind(rule, @local_config_cuda//...)',这样相当于在定义local_config_cuda这个rule

问题2:

磁盘满

分析与解决:

嵌入式设备存储往往并不大,而Tensorflow安装过程需要的存储空间非常大,很容易遇到磁盘空间不够。目前能想到的办法就是用NFS来扩展存储空间,至于NFS具体操作不难搜到。这里使用NFS时会出现如下告警

root@tegra-ubuntu:/data/serving# /data/bazel/output/bazel build -c opt --config=cuda tensorflow_serving/... --action_env=PYTHON_BIN_PATH=/usr/bin/python

WARNING: Output base '/data/rootcache/bazel/_bazel_root/46688ad2577b25fcaed4521437622fa6' is on NFS. This may lead to surprising failures and undetermined behavior.

曾经在网上搜过这个问题,但能找到的答复大意是“既然明确提示了,就不要用NFS”。因为没有别的选择,所以我还是用的NFS,并安装成功了,且能跑通GoogLeNet和Resnet,说明NFS并非不能用。实际可以在编译完成后,把需要的可执行文件保留,其它中间文件都可以删掉,这样就不需要太多存储(放NFS上运行也没问题,就是加载会很慢)。

问题3:

提示大意是编译失败,cc1被kill了

分析与解决:

其实被这个问题难到了一段时间,后来偶然在串口终端上发现了内存不足并kill进程的打印,于是立即就明白了问题的原因,就是内存不足导致编译进程被kill。

(1) 一般直接重试就好,会继续编译,而不是老失败在一个地方

(2) 如果经常编译没多久就出现这个问题,说明内存很不够用,可以考虑加swap,我加的2GB(TX1本身有4GB的内存)。这个加太多也不好,因为大量的swap IO会导致编译很慢

(3) 多次尝试发现这个问题都出在同一个地方时,可以在bazel编译时加个--jbos=2的选项,来限制同时编译任务数量,默认是用CPU核数(TX1上是4)。建议挺过那几个比较耗内存的编译任务后,再停掉编译并重新用之前的方式编译,这样会快一些,不然一天一夜都编译不完。

问题4:

找不到cudnn的报错;提示找不到nccl

分析与解决:

这个两个问题其实是独立的,但比较类似,所以放一起了。cudnn的问题只需要编译前执行下面的命令

export CUDNN_INSTALL_PATH=/usr/lib/aarch64-linux-gnu

nccl的问题需要先安装nccl,然后在编译前执行下面的命令

export TF_NCCL_VERSION='1.3'

export NCCL_INSTALL_PATH=/data/nccl/build

需要说明的是,安装nccl只能用源码安装,因为安装包没有编译aarch64架构的。源码可从github上下载。

问题5:

unrecognized command line option '-mfpu=neon'

分析与解决:

修改报错时提示的那个BUILD文件,将里面包含'-mfpu=neon'的代码行都删掉。

这个问题不难找到解决方法,只是主要针对gcc而不是bazel的,所以这里还是给出针对bazel的解决方法。

问题6:

ERROR: /data/serving/tensorflow_serving/model_servers/BUILD:308:1: Linking of rule '//tensorflow_serving/model_servers:tensorflow_model_server' failed (Exit 1)

bazel-out/arm-opt/bin/external/aws/libaws.a(ClientConfiguration.o): In function `Aws::Client::ComputeUserAgentString()':

ClientConfiguration.cpp:(.text._ZN3Aws6ClientL22ComputeUserAgentStringEv+0x170): undefined reference to `Aws::OSVersionInfo::ComputeOSVersionString[abi:cxx11]()'

bazel-out/arm-opt/bin/external/aws/libaws.a(DateTimeCommon.o): In function `Aws::Utils::DateTime::ToLocalTimeString[abi:cxx11](char const*) const':

大量AWS相关的错误

分析与解决:

先给解决方法:修改_bazel_root/46688ad2577b25fcaed4521437622fa6/external/aws/BUILD.bazel,把conditions:default后面的内容换成glob([ "aws-cpp-sdk-core/source/platform/linux-shared/*.cpp", ])。

这里也给下分析的过程。

分析直接原因,是链接tensorflow_model_server用到了libaws.a,此库里确实没定义ComputeOSVersionString,期望要定义。

进一步分析,libaws.a对应的目标文件在目录_bazel_root/46688ad2577b25fcaed4521437622fa6/execroot/tf_serving/bazel-out/arm-opt/bin/external/aws/_objs/aws/external/aws/aws-cpp-sdk-core/source中,而此目录中找不到OSVersionInfo.o。而ComputeOSVersionString这个函数就是在OSVersionInfo.cpp中定义的。

再分析进一步原因,对于aws组件,aws官方提供的是cmake编译,编译只输出动态库,不会出现libaws.a。这个.a文件其实是_bazel_root/46688ad2577b25fcaed4521437622fa6/external/aws/BUILD.bazel控制生成的。分析BUILD.bazel源码发现,有对平台的判断,针对不同平台会拷贝不同目录的代码进行编译,而平台里不包含aarch64相关的,从而不会拷相应的代码。这些没拷的代码就包含了OSVersionInfo.cpp,这就是问题的根因。

问题7:

链接tensorflow_model_server、testing等目标时,出现 external symbol `__stack_chk_guard@@GLIBC_2.17' can not be used when making a shared object; recompile with -fPIC 的错误

ERROR: /data/serving/tensorflow_serving/util/net_http/socket/testing/BUILD:21:1: Linking of rule '//tensorflow_serving/util/net_http/socket/testing:ev_fetch_client' failed (Exit 1)

/usr/bin/ld: bazel-out/arm-opt/genfiles/external/com_github_libevent_libevent/libevent/lib/libevent.a(buffer.o): relocation R_AARCH64_ADR_PREL_PG_HI21 against external symbol `__stack_chk_guard@@GLIBC_2.17' can not be used when making a shared object; recompile with -fPIC

/usr/bin/ld: bazel-out/arm-opt/genfiles/external/com_github_libevent_libevent/libevent/lib/libevent.a(buffer.o)(.text+0x14): unresolvable R_AARCH64_ADR_PREL_PG_HI21 relocation against symbol `__stack_chk_guard@@GLIBC_2.17'

/usr/bin/ld: final link failed: Bad value

分析与解决:

这个问题是解决时间最长的。尝试过各种加-fPIC,还尝试fno-stack-protector等方法,发现都没用。后来发现,其实不是没用,而是没生效。因为一重新编译,之前改的Makefile之类的就又被刷回来了。

这时大致解释一下bazel编译的运作机制,在执行bazel编译后,会执行BUILD文件里定义的目标;这里我们关注的是生成libevent.a的目标,即third_party/libevent.BUILD中的libevent目标;这个目标里会执行多条shell命令,大致是先将libevent工程拷到/tmp/libevent.xxx目录,然后再在/tmp/libevent.xxx目录里编译,并将结果install到bazel-genfiles/external/com_github_libevent_libevent/libevent目录,最后删除/tmp/libevent.xxx目录;在libevent编译时,Makefile等文件是动态生成的,这也是修改Makefile等文件不生效的原因;bazel在执行每个目标(如libevent)前,会先把此目标的输出(如libevent.a)都删除,然后在执行后,再检测输出的文件是否存在(如libevent.a),如果不存在是会报错的。

所以解决方法如下

(1) 修改third_party/libevent.BUILD中的libevent目标,删除删临时文件的那一行,避免编译完后中间文件被删。然后重编译,当然,仍会失败。

(2) 进到临时文件的目录,在/tmp/libevent.*这样的目录中,修改Makefile,找到CFLAGS的定义,追加一个-fPIC选项,再make install

(3) 此时,生成的文件会输出到bazel-genfiles/external/com_github_libevent_libevent/libevent目录,通过cp -r bazel-genfiles/external/com_github_libevent_libevent/libevent /data命令将输出文件暂存一下,避免下次尝试编译时被删

(4) 然后再次修改third_party/libevent.BUILD中的libevent目标,将cmd全删掉,再加一条cp -r命令将暂存的libevent内容拷回原输出目录(注意用绝对路径),再重新编译就成功了

当然这里也可能有更简单的方法,但这里重点还是弄清楚问题原因和bazel的运作机制。

原创声明,本文系作者授权云+社区发表,未经许可,不得转载。

如有侵权,请联系 yunjia_community@tencent.com 删除。

编辑于

ericzli

1 篇文章1 人订阅

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏Golang语言社区

LINUX环境并发服务器的三种实现模型

服务器设计技术有很多,按使用的协议来分有TCP服务器和UDP服务器。按处理方式来分有循环服务器和并发服务器。 1 循环服务器与并发服务器模型 在网络程序里面,一...

2514
来自专栏前端人人

React多页面应用4(webpack自动化生成多入口页面)

本教程总共7篇,每日更新一篇,请关注我们!你可以进入历史消息查看以往文章,也敬请期待我们的新文章! 1.React多页面应用1(webpack开发环境搭建,包...

5145
来自专栏云计算教程系列

如何在Ubuntu上使用Firefox,Siege和Sproxy对网站进行基准测试

Siege是一种可配置的基准测试和测试工具,适用于通过发出网页请求来测试Web服务器的网站。Siege请求的每秒页数可以设置为从每秒几页到网站可以处理的最大数量...

872
来自专栏王小雷

Oracle数据仓库创建教程

Oracle数据仓库创建教程。如何创建一个数据仓库,创建实例,以为毕业设计要求,最近开始Oracle的数仓建模实践,详细记录了图形界面下的 Oracle dat...

2305
来自专栏游戏杂谈

使用hta操作nginx停止、重启

新上线的webgame需要做一个官网,做好了并上线了(切割、程序、后台,后台使用是java版本的jeecms),但仅仅是自己家的官网做好了,现在上面的想法是需要...

261
来自专栏云计算教程系列

如何在CentOS 7上配置Apache内容缓存

缓存是一种通过允许更快访问的方式临时存储共同请求的内容来提高服务器性能的方法。通过减少一些资源密集型操作来加速处理和交付。

500
来自专栏GreenLeaves

邮件发送功能开发

作为一名.Net开发,"邮件发送"功能的开发和使用是必须要掌握的,因为这个功能作为“消息推送”的一种手段经常出现在各种.Net系统中,所以本文将对.Net平台下...

2387
来自专栏北京马哥教育

11条nginx优化方法助力你的运维生涯

云豆贴心提醒,本文阅读时间5分钟 隐藏nginx header里版本号信息 1.查看版本号 ? 2.隐藏版本号 在nginx配置文件的http标签内加入“s...

3989
来自专栏大魏分享(微信公众号:david-share)

干货巨献:Openshift3.9的网络管理大全.加长篇---Openshift3.9学习系列第二篇

OpenShift的OVS网络组件有三种模式:ovs-subnet、ovs-multitenant、ovs-networkpolicy。

1815
来自专栏北京马哥教育

Linux 下如何实现 MySQL 数据库定时自动备份?

概述:备份是容灾的基础,是指为防止系统出现操作失误或系统故障导致数据丢失,而将全部或部分数据集合从应用主机的硬盘或阵列复制到其它的存储介质的过程。而对于一些网站...

4159

扫码关注云+社区