正在构建tensorflow 2.2.0 pip wheel文件,用于CentOS系统(旧版本的libc)



简介:

我必须创建一个Tensorflow 2.2.0的pip轮,并动态链接cuda库(特别是cudart.so)。为了实现这一点,我目前正在使用Tensorflow dev-doker镜像。我能够构建tf轮子文件,并且能够在构建容器中安装和使用它。

问题:

问题是,在一个CentOS服务器中导入生成的轮子文件,我得到以下错误:

ImportError: /lib64/libm.so.6: version `GLIBC_2.27' not found (required by /home1/private/mavridis/Vineyard/tensorflowshared/test/lib64/python3.6/site-packages/tensorflow/python/_pywrap_tensorflow_internal.so)

环顾四周,问题是由使用较新libc:的构建容器引起的

ldd --version
ldd (Ubuntu GLIBC 2.27-3ubuntu1) 2.27

与CentOS旧版本相比:

ldd --version
ldd (GNU libc) 2.17

预期行为:

已经尝试了"香草"tenorflow 2.2.0版本,没有任何问题,使用pip:安装

pip install tensorflow==2.2.0

我希望我自己的构建也能工作。

因此,我认为有一些配置选项或docker配置可以让我在CentOS设置中使用docker构建的轮子文件,就像pip安装的版本一样。由于此轮子文件旨在部署到我无法控制的设置中,因此涉及备用操作系统和/或libc替换的解决方案不适用。

构建配置:

在构建过程中,我使用以下配置/命令行:

export TF_NEED_CUDA=1
export TF_USE_XLA=0
export TF_SET_ANDROID_WORKSPACE=0
export TF_NEED_OPENCL_SYCL=0
export TF_NEED_ROCM=0
bazel build --config=opt --config=cuda --output_filter=DONT_MATCH_ANYTHING --linkopt=-L/usr/local/cuda/lib64 --linkopt=-lcudart --linkopt=-static-libstdc++ //tensorflow/tools/pip_package:build_pip_package

关于使用的选项:
--output_filter=DONT_MATCH_ANYTHING:静音警告
--linkopt=-L/usr/local/cuda/lib64--linkopt=-lculdart:库的动态链接。因此
-linkopt=-static libstdc++:作为libstc++的静态链接libstcC++也导致了libc错误,但这对于libm 来说是不可能的

我希望我自己的构建也能工作。

这种期望(显然)是不正确的。程序或库需要GLIBC提供的符号取决于您调用的函数。

考虑以下程序:

int main() { exit(0); }

在GLIBC-2.30系统上编译/链接时,此程序仅依赖于GLIBC_2.2.5(因为它不调用任何更新的符号)。

现在稍微更改一下程序:

int main() { gettid(); exit(0); }

再次编译/链接它,突然之间,这个程序现在需要GLIBC_2.30(因为gettid()就是在这里添加到GLIBC的),并且不会在任何具有旧GLIBC系统上工作。

所以我假设有一些配置选项或docker配置

当然:您的Docker镜像必须具有不比目标系统更新的GLIBC,即GLIBC-17。您当前的图像包含GLIBC-2.27(或更新版本)。

你需要一个不同的Docker镜像,你可能必须自己构建它,因为GLIBC-21.7已经有7年多的历史了,比TensorFlow早了很多年。

更新:

我不明白的是,pip-tensorflow包(我认为它是用我正在使用的docker映像构建的)是如何在CentOS下工作的?

它在上运行是偶然的,就像我的第一个程序可以在CentOS上运行一样,但第二个程序不能。

简而言之,我想生成一个可以在"任何"linux/libc版本上工作的pip包

这是一个不可能实现的目标:Linux早于GLIBC,不可能构建一个在不包含GLIBC的Linux发行版和包含GLIBC的发行版上运行的包。

你必须在某个地方划一条线。tensorflow dev docker镜像的开发者在GLIBC-27上画了一条线。基于此映像构建的程序包应在2.27或更高版本的任何系统上运行,可能(但根本不能保证)在较旧的系统上运行。

就像pip安装的版本一样。

您声称pip安装的版本没有";仅GLIBC-xx或更高版本";要求,但事实并非如此。我99.9%确信它至少需要GLIBC-14。

要查找包所需的GLIBC版本,请运行以下命令:

readelf -WV _pywrap_tensorflow_internal.so | grep GLIBC_

我假设,pip安装版本是使用公开的tensorflow-devel docker镜像构建的。

很有可能。正如我所说,它恰好可以在CentOS上工作,但微小的更改可能会使它不再工作。

更新2:

因此,按照您的建议运行readelf命令,确实显示了最新的所需版本:-pip版本:GLIBC_2.12-mine:GLIBC_2.27因此,据我所知,pip版本使用的是旧版本,甚至可以在CentOS上使用,这解释了它工作的原因。

它不"使用";旧版本,它使用任何可用的版本。

要求最低版本2.12,而您的构建要求最低版本2.27。

他们是如何做到这一点的?他们是否使用了具有旧libc的不同映像?如果是,我在哪里可以买到?还是他们使用了公众形象,但使用了一些巴泽尔旗帜,将符号"限制"在libc 2.12之前?

你仍然没有得到它。

程序所需的版本取决于您调用的函数。在我的示例程序中,如果我只调用exit,我的程序需要版本2.2.5,但如果我也调用gettid,那么我的程序要求版本2.30。注意:这两个程序是在相同的系统上构建的,带有同一标志。

所以没有:他们(很可能)没有使用不同的Docker映像,也没有使用";魔术;巴泽尔旗。他们只是碰巧没有调用任何需要GLIBC版本>2.12,你做到了。

附言:你可以找到哪些符号导致了";坏的";构建中的依赖关系:

readelf -Ws  _pywrap_tensorflow_internal.so | egrep 'GLIBC_2.2[0-9]'
readelf -Ws  _pywrap_tensorflow_internal.so | egrep 'GLIBC_2.1[89]'

这将产生类似于(使用我的第二个程序)的输出:

readelf -Ws a.out | egrep 'GLIBC_2.[23][0-9]'
2: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND gettid@GLIBC_2.30 (2)
48: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND gettid@@GLIBC_2.30

上面的输出显示,我的二进制文件从GLIBC 2.20或更高版本中唯一需要的符号是gettid

为了反驳Employed Russian所写的内容:

程序所需的版本取决于您调用的函数。在我的示例程序中,如果我只调用exit,我的程序需要2.2.5版本,但如果我也调用gettid,那么我的程序要求2.30版本注意:这两个程序是在具有相同标志的同一系统上构建的。

我认为这不太准确。我的理解得到了https://github.com/wheybags/glibc_version_header,事情是这样运作的吗(引用那个项目,重点是我的):

Glibc使用一种称为符号版本控制的东西。这意味着,当您在程序中使用例如malloc时,链接器实际链接的符号是malloc@GLIBC_YOUR_INSTALLED_VERSION(实际上,它将链接到最新版本的glibc中的malloc,该版本更改了malloc的实现,但您已经明白了)

所以我的猜测(我没有检查)是Tensorflow版本是基于旧的glibc构建的(可能是通过在目标Linux发行版的旧版本上构建的方式)。

最新更新