目前,TensorFlow 依赖于 glibc 2.17 以上的版本。但在有些超级计算机集群上,操作系统比较老旧,glibc 的版本达不到要求,但一般用户又没有权限升级。在一般情况下,用户可以把这种被依赖的软件安装到自己的 home 目录下,并把软件的安装目录添加到 LD_LIBRARY_PATH
环境变量中。但 glibc 是非常底层的库,贸然把新版本加入 LD_LIBRARY_PATH
,容易导致与系统的其它组件不兼容。不兼容的后果可能是十分严重的,比如导致简单的 ls
命令都失效。为了绕过对 glibc 的要求,也可以从 TensorFlow 的源码开始手工编译、安装,但这个过程也费时费力,且成功率低。
前几天,我成功地研究出了在 glibc < 2.17 的系统上安装并运行 TensorFlow 的方法,在此分享一下,希望能帮到遇到类似困难的朋友。
我的 Linux 系统为 CentOS 6.5,64 位,glibc 版本为 2.12。我安装的 TensorFlow 是带 GPU 支持的,如果你不需要 GPU,则需要自行修改某些步骤。
一、安装 TensorFlow
第一步,是先假装 glibc 的版本问题不存在,暴力安装 TensorFlow。为此,你需要首先安装 Python,我推荐 Anaconda 或 Miniconda 版本。
安装好 Python 后,执行如下命令安装最新版的 TensorFlow:
pip install --upgrade tensorflow-gpu
如果中途遇到「找不到 easy_install」的错误,那么请先执行如下命令安装 setuptools,再安装 TensorFlow:
pip install --upgrade setuptools
安装完 TensorFlow 后,在 Python 里面 import tensorflow as tf
,应该会出现将近一屏的报错信息,其中有如下字句:
ImportError: /lib64/libc.so.6: version `GLIBC_2.16' not found
虽然这里只说需要 glibc 2.16,但实际上是需要 2.17 的。下面,我们就来安装 glibc 2.17。
二、安装 glibc 2.17
glibc 的各个版本都可以从这里下载。我安装的是最低限的 2.17 版,你也可以安装更高的版本,但我没有测试过。
首先新建一个你希望下载、安装 glibc 的目录,并 cd
到那里去。然后执行下列命令:
wget http://mirror.rit.edu/gnu/libc/glibc-2.17.tar.gz
tar zxvf glibc-2.17.tar.gz
mkdir glibc-2.17-build glibc-2.17-install
cd glibc-2.17-build
../glibc-2.17/configure --prefix=`readlink -f ../glibc-2.17-install`
make -j 8
make -j 8 install
这段命令会下载 glibc 2.17 的源码,并解压到 glibc-2.17 目录中。然后,它会在 glibc-2.17-build 目录中编译 glibc,并安装到 glibc-2.17-install 目录中。glibc 要求编译不能直接在源码目录中进行,所以我才新建了 glibc-2.17-build 目录;文章开头说过 glibc 容易与系统的其它组件不兼容,所以我专门创建了 glibc-2.17-install 这个安装目录,而没有把它安装到 $HOME/usr/local
里去。
glibc 的编译安装可能花费半个小时至一个小时,请耐心等待。安装完毕之后,你会在 glibc-2.17-install 目录下看到 bin、lib 等目录。把 lib 目录定义成一个环境变量 GLIBC_DIR
,备用:
export GLIBC_DIR=<...>/glibc-2.17-install/lib
这行代码最好也加到~/.bashrc
里去,这样就永远可以使用GLIBC_DIR
这个环境变量了。
三、调用新版 glibc
注:这不是一个必须的步骤,它主要是为了解释第四步中命令的原理;你也可以用它来确认一下 glibc 已经正确安装。
调用新版 glibc 并不是一件简单的事情。经过重重摸索,我终于找到了调用它的正确姿势:
$GLIBC_DIR/ld-2.17.so --library-path $GLIBC_DIR:/lib64:$LD_LIBRARY_PATH <command>
我来解释一下这行命令。$GLIBC_DIR/ld-2.17.so
是个可执行程序(对,虽然它带着 .so 扩展名,看起来仿佛是个库),由它去执行后面的 <command>
命令。--library-path
指明了执行命令时可以调用的库,它包括三部分:
$GLIBC_DIR
:刚刚安装的新版 glibc;/lib64
:包含一些系统核心的库;$LD_LIBRARY_PATH
:你本来能调用的、自定义的库。
一个不方便的地方是,<command>
必须是可执行文件的完整路径;换句话说,ld-2.17.so
不会去 $PATH
里查找。例如,你直接用 ls
代替 <command>
是不行的,必须写 /bin/ls
。但有一个投机取巧的方法是用 `which ls`
代替 <command>
,由 which
命令来查找ls
所在的位置。
上面这条命令中的--library-path
参数,也可以用环境变量来替代:
LD_LIBRARY_PATH=$GLIBC_DIR:/lib64:$LD_LIBRARY_PATH $GLIBC_DIR/ld-2.17.so <command>
用这条命令执行ls
,可以达到同样的效果。但这种用法有一个潜在的问题:如果被执行的<command>
命令又调用了其它程序,那么环境变量LD_LIBRARY_PATH
会继续起作用,但ld-2.17.so
则不起作用了,导致不兼容。若使用本节开头的用法,那么<command>
命令调用的其它程序则不受这两者中任一者的影响,可以调用老版 glibc 正常运行。
本节开头的命令中的任何一部分都是必不可少的,缺少则会导致各种错误(也许你已经见过了呢)。例如:
- 不使用
$GLIBC_DIR/ld-2.17.so
,仅设置LD_LIBRARY_PATH
环境变量,会导致如下错误: error while loading shared libraries: __vdso_time: invalid mode for dlopen(): Invalid argument
;- 省略
--library-path
中的$GLIBC_DIR
,会导致 segmentation fault; - 省略
--library-path
中的/lib64
,会导致如下错误: error while loading shared libraries: libselinux.so.1: cannot open shared object file: No such file or directory
。
四、在新版 glibc 下运行 TensorFlow
在~/.bashrc
中定义一个 alias tfpython
:
alias tfpython="$GLIBC_DIR/ld-2.17.so --library-path $GLIBC_DIR:/lib64:$LD_LIBRARY_PATH `which python`"
然后执行之:
CUDA_VISIBLE_DEVICES=<gpuid> tfpython
注意我多设置了一个环境变量CUDA_VISIBLE_DEVICES
,这是为了告诉 Tensorflow 可以使用几号 GPU。
进入 Python 后,执行如下的命令来测试 TensorFlow:
import tensorflow as tf
tf.Session().run(tf.constant('Hello World!'))
如果一切正常,你应该看到几条提示信息,以及运行结果Hello World!
。
如果遇到「找不到 cuDNN 6.0」的报错信息,那么你需要到 NVidia 网站下载 cuDNN 6.0(需要注册),把它解压到某个地方(会解压出一个名为 cuda 的目录),然后在~/.bashrc
中设置如下的环境变量:
export CUDNN_INCLUDE_DIR="<...>/cuda/include"
export CUDNN_LIBRARY_PATH="<...>/cuda/lib64"
export CUDNN_LIBRARY="$CUDNN_LIBRARY_PATH/libcudnn.so.6"
export LD_LIBRARY_PATH="$CUDNN_LIBRARY_PATH:$LD_LIBRARY_PATH"
如果遇到「找不到 libcuda」的报错信息,那么你需要用locate libcuda
命令找到 libcuda 所在的目录(例如/usr/lib64
),并在~/.bashrc
中把这个目录添加到LD_LIBRARY_PATH
中去:
export LD_LIBRARY_PATH="/usr/lib64:$LD_LIBRARY_PATH"
确认 TensorFlow 正常运行之后,我们还要确认一下使用了新版 glibc 的 Python 仍然能够调用其它程序:
import os
os.system('ls')
如果在 Python 中执行上述命令能够看到当前目录的内容,则说明 Python 仍能正常调用其它程序。这些其它程序是完全在老版 glibc 下运行的,不受新版 glibc 影响。