我们需要在一台离线的CentOS主机内执行一个Python程序,该程序依赖Python3独有的一些特性,而这台机器上只安装了Python2。下面是解决这个问题的一些尝试思路:

  1. 将该程序修改为兼容Python2的版本:操作起来比较繁琐,弃用。
  2. 将该程序使用CPython编译为ELF二进制文件:看起来可行,然而手头只有Ubuntu系统,只搞定了动态链接,没有搞定静态链接,直接把二进制程序和libpython动态链接库复制到CentOS上没法运行。
  3. 为前述CentOS离线安装Python3。

第二个方法治标,虽然这次没用上,但以后也许用得上,这里记录一下,参考了一个gist,相关代码如下:

import subprocess
import sys
import tempfile
from Cython.Compiler import Main, CmdLine, Options

in_file_name = sys.argv[1]
source = open(in_file_name).read()
out_file_name = in_file_name.replace('.py', '.out')

temp_py_file = tempfile.NamedTemporaryFile(suffix='.py', delete=False)
temp_py_file.write(source.encode())
temp_py_file.flush()

Main.Options.embed = 'main'
res = Main.compile_single(temp_py_file.name, Main.CompilationOptions(), '')

gcc_cmd = 'gcc -fPIC -O2 %s -I/usr/include/python3.6 -L/usr/lib/python3.6 -lpython3.6m -o %s' % (res.c_file, out_file_name)

print(gcc_cmd)
assert 0 == subprocess.check_call(gcc_cmd.split(' '))

第三个方法治本,参考了一篇博客,这里记录下实践过程,并将该过程自动化。

docker pull centos:7
docker run -it -d --name compile /bin/bash
docker exec -it compile-python /bin/bash

# in container
yum update
yum install -y epel-release
yum install -y gcc openssl-devel bzip2-devel libffi libffi-devel make wget
wget https://www.python.org/ftp/python/3.7.4/Python-3.7.4.tgz
tar -xzvf Python-3.7.4.tgz && cd Python-3.7.4
./configure --enable-optimizations && make

在容器内make完后,打包一下:

cd ..
rm -f Python-3.7.4.tgz
tar -czf Python-3.7.4.tgz Python-3.7.4/

# copy tgz from container to host
docker cp compile:/Python-3.7.4.tgz ./

上传到前述离线CentOS中,解压,安装:

tar -xzf Python-3.7.4.tgz
cd Python-3.7.4/
make altinstall

之后可以执行/usr/local/bin/python3.7来愉快地玩耍了,添加个PATH也可以。