原文链接:centos6使用高版本python | Akkuman 的技术博客

背景

需要能在 centos 6.3 上跑的 python 3.10 3.12

并且需要支持 pandas/numpy 科学库,能使用 nuitka 打包

官方版本肯定是跑不起来的

根据之前的 ansible-playbook 独立二进制编译实录 中提到的参考来看

笔记

64 位系统

首先找个 centos 6.3 镜像

1
2
docker run --name centos6 -itd matrim/centos6.3:latest bash
docker exec -it centos6 bash

然后换一下镜像 centos-vault镜像_centos-vault下载地址_centos-vault安装教程-阿里巴巴开源镜像站

由于 centos 6.3 的源中的 nss 版本过低,会导致 curl 出现 curl: (35) SSL connect error 这种问题,并且很多和 https 相关的比如 git 都会出问题,查了下 centos 6.3 和 6.10 的 glibc 版本都是 2.12,所以直接用 6.10 的源即可

1
2
3
4
5
minorver=6.10
sed -e "s|^mirrorlist=|#mirrorlist=|g" \
         -e "s|^#baseurl=http://mirror.centos.org/centos/\$releasever|baseurl=http://mirrors.aliyun.com/centos-vault/$minorver|g" \
         -i.bak \
         /etc/yum.repos.d/CentOS-*.repo

然后安装 pyenv,设置 pyenv 拉取镜像,安装 python 编译依赖

1
2
3
4
5
6
7
8
yum install git
# pyenv 相关环境变量
export PYTHON_BUILD_MIRROR_URL_SKIP_CHECKSUM=1
export PYTHON_BUILD_MIRROR_URL="https://registry.npmmirror.com/-/binary/python"
export PATH="/root/.pyenv/bin:$PATH"
# 换成 github 镜像拉取
export GITHUB_MIRROR="https://ghfast.top/https://github.com/"
curl -L https://files.m.daocloud.io/raw.githubusercontent.com/yyuu/pyenv-installer/master/bin/pyenv-installer | sed 's|-c advice.detachedHead=0 -c core.autocrlf=false||g' | sed 's|GITHUB="https://github.com/"|GITHUB="'$GITHUB_MIRROR'"|g' | bash

其中因为 git clone​ 使用了 -c advice.detachedHead=0 -c core.autocrlf=false​,而低版本 git 不支持,所以我们需要去除,然后使用了 github 镜像来拉取 pyenv 相关的 git 仓库

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
# 安装 python 编译依赖
yum groupinstall -y "Development Tools"
yum install -y \
    bzip2-devel \
    ncurses-devel \
    libffi-devel \
    openssl-devel \
    readline-devel \
    sqlite-devel \
    zlib-devel \
    gdbm-devel \
    xz-devel \
    tk-devel

我们先试试编译安装 python 3.9

1
pyenv install 3.9

报错如下

1
2
3
4
5
6
Traceback (most recent call last):
  File "<string>", line 1, in <module>
  File "/root/.pyenv/versions/3.9.23/lib/python3.9/ssl.py", line 99, in <module>
    import _ssl             # if we can't import it, let the error propagate
ModuleNotFoundError: No module named '_ssl'
ERROR: The Python ssl extension was not compiled. Missing the OpenSSL lib?

查询了一下, CentOS 6 默认的 OpenSSL 1.0.1e 太旧,Python 3.9+ 需要 OpenSSL 1.1.1+

1
2
bash-4.1# openssl version
OpenSSL 1.0.1e-fips 11 Feb 2013

尝试手动编译 openssl

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
# 下载最新 OpenSSL 1.1.1
curl -o openssl-1.1.1w.tar.gz https://files.m.daocloud.io/github.com/openssl/openssl/releases/download/OpenSSL_1_1_1w/openssl-1.1.1w.tar.gz
tar -xf openssl-1.1.1w.tar.gz
cd openssl-1.1.1w

# 编译安装到 /usr/local/openssl
./config --prefix=/usr/local/openssl --openssldir=/usr/local/openssl shared zlib
make -j$(nproc)
make install

# 更新动态库链接
echo "/usr/local/openssl/lib" > /etc/ld.so.conf.d/openssl.conf
ldconfig

# 验证新版本
/usr/local/openssl/bin/openssl version
# 应输出 OpenSSL 1.1.1w

然后再编译

1
2
# https://github.com/pyenv/pyenv/wiki/common-build-problems#1-openssl-is-installed-to-an-uncommon-location
PYTHON_CONFIGURE_OPTS="--with-openssl=/usr/local/openssl" pyenv install 3.9

此时已经编译出产物了,但是还是有出现类似的报错

1
2
3
4
5
6
7
8
Traceback (most recent call last):
  File "<string>", line 1, in <module>
  File "/root/.pyenv/versions/3.9.23/lib/python3.9/sqlite3/__init__.py", line 57, in <module>
    from sqlite3.dbapi2 import *
  File "/root/.pyenv/versions/3.9.23/lib/python3.9/sqlite3/dbapi2.py", line 27, in <module>
    from _sqlite3 import *
ModuleNotFoundError: No module named '_sqlite3'
WARNING: The Python sqlite3 extension was not compiled. Missing the SQLite3 lib?

手动编译 sqlite3

1
2
3
4
5
6
wget https://www.sqlite.org/2023/sqlite-autoconf-3420000.tar.gz
tar -xf sqlite-autoconf-3420000.tar.gz
cd sqlite-autoconf-3420000
./configure --prefix=/usr
make -j$(nproc)
make install

然后重新编译安装

1
PYTHON_CONFIGURE_OPTS="--enable-loadable-sqlite-extensions --with-openssl=/usr/local/openssl"  pyenv install 3.10

可以了,python 3.12 也没问题

但是安装 pandas 时报错如下

1
../meson.build:28:4: ERROR: Problem encountered: NumPy requires GCC >= 8.4

手动编译 gcc 时间可太久了,用 clang 试试呢

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
# 安装 epel 源
rpm -Uvh http://mirrors.aliyun.com/epel-archive/6/x86_64/epel-release-6-8.noarch.rpm
sed -e 's!^mirrorlist=!#mirrorlist=!g' \
    -e 's!^#baseurl=!baseurl=!g' \
    -e 's!https\?://download\.fedoraproject\.org/pub/epel!http://mirrors.aliyun.com/epel-archive!g' \
    -e 's!https\?://download\.example/pub/epel!http://mirrors.aliyun.com/epel-archive!g' \
    -i /etc/yum.repos.d/epel{,-testing}.repo
# 安装 clang
yum install clang
# 使用 clang 编译
CC=clang CXX=clang++ ~/.pyenv/versions/3.10.18/bin/pip install pandas -i http://mirrors.aliyun.com/pypi/simple/ --trusted-host mirrors.aliyun.com

还是报错

1
ERROR: C++ Compiler does not support -std=c++17

经过查阅,numpy​ 的编译失败,原因是 C++ 编译器不支持 C++17 标准ERROR: C++ Compiler does not support -std=c++17​)。此外,你的 clang​ 版本是 3.4.2,而 numpy​ 2.2.6 需要支持 C++17 的编译器(如 gcc>=8​ 或 clang>=7​)

无论是手动编译 gcc,还是手动编译 clang,耗时都太长了,看看有没有人有编译好的东西

找到了两个

我们拿 centos6.9-build 来测试

依旧是安装 pyenv,然后是安装 python 编译依赖(不要安装 openssl-devel,以免覆盖掉 openssl 1.1.1i ),然后编译 sqlite3。然后继续使用 pyenv 编译

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
yum install -y \
    bzip2-devel \
    ncurses-devel \
    libffi-devel \
    readline-devel \
    zlib-devel \
    gdbm-devel \
    xz-devel \
    tk-devel
wget https://www.sqlite.org/2023/sqlite-autoconf-3420000.tar.gz
tar -xf sqlite-autoconf-3420000.tar.gz
cd sqlite-autoconf-3420000
./configure --prefix=/usr/local/sqlite3
make -j$(nproc)
make install
echo "/usr/local/sqlite3/lib" > /etc/ld.so.conf.d/sqlite3.conf
ldconfig
pyenv install 3.12

测试安装 pandas 没问题

32 位系统

按照 ispras/centos6.9-build-docker: CentOS 6.9 build Docker environment to distribute portable Linux binaries 中的 Dockerfile,安装 gcc 9.3

然后按照上面的方式编译安装 python

如果打包出来的东西你想放到其他机器上运行,你可能需要使用 LDFLAGS="-static-libgcc -static-libstdc++"​ 来编译这些东西

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
# 镜像源
minorver=6.10
sed -e "s|^mirrorlist=|#mirrorlist=|g" \
         -e "s|^#baseurl=http://mirror.centos.org/centos/\$releasever|baseurl=http://mirrors.aliyun.com/centos-vault/$minorver|g" \
         -i.bak \
         /etc/yum.repos.d/CentOS-*.repo
rpm -Uvh http://mirrors.aliyun.com/epel-archive/6/x86_64/epel-release-6-8.noarch.rpm
sed -e 's!^mirrorlist=!#mirrorlist=!g' \
    -e 's!^#baseurl=!baseurl=!g' \
    -e 's!https\?://download\.fedoraproject\.org/pub/epel!http://mirrors.aliyun.com/epel-archive!g' \
    -e 's!https\?://download\.example/pub/epel!http://mirrors.aliyun.com/epel-archive!g' \
    -i /etc/yum.repos.d/epel{,-testing}.repo
# 安装基础依赖
yum -y install gcc gcc-c++ glibc-devel.i686 glibc-devel \
                   libstdc++-devel.i686 libstdc++-devel make zlib-devel \
                   python-devel git wget unzip xz bzip2 lzop re2c \
                   texi2html texinfo libffi-devel m4 glibc-static ccache
# 编译安装 openssl 1.1.1(高版本 python 依赖)
curl -O -L https://files.m.daocloud.io/github.com/openssl/openssl/releases/download/OpenSSL_1_1_1i/openssl-1.1.1i.tar.gz && \
    tar xf openssl-1.1.1i.tar.gz && rm -rf openssl-1.1.1i.tar.gz && \
    cd openssl-1.1.1i && ./config --prefix=/usr && \
    sed -i '/^CFLAG/s/$/ -fPIC/' Makefile && \
    make -j$(nproc) && make install && cd .. && rm -rf openssl-1.1.1i
# 编译安装 sqlite3 高版本(高版本 python 依赖)
wget https://www.sqlite.org/2023/sqlite-autoconf-3420000.tar.gz && \
	tar -xf sqlite-autoconf-3420000.tar.gz && rm -rf sqlite-autoconf-3420000.tar.gz && \
	cd sqlite-autoconf-3420000 && \
	./configure --prefix=/usr && \
	make -j$(nproc) && make install && cd .. && rm -rf sqlite-autoconf-3420000
# 安装 cmake (目前没看到高版本 cmake 的要求,如果后续出现,可能需要自行编译)
yum install -y cmake
# 编译安装 binutils
curl -O -L https://mirrors.aliyun.com/gnu/binutils/binutils-2.34.tar.xz && \
    tar xf binutils-2.34.tar.xz && rm -rf binutils-2.34.tar.xz && \
    cd binutils-2.34 && \
    ./configure --prefix=/usr && make -j$(nproc) && make install && \
    cd .. && rm -rf binutils-2.34
# 编译安装 gcc 9.3(numpy 编译安装要求 c++17,或者 gcc >= 8.4)
curl -O -L https://mirrors.aliyun.com/gnu/gcc/gcc-9.3.0/gcc-9.3.0.tar.xz && \
    tar xf gcc-9.3.0.tar.xz && rm -rf gcc-9.3.0.tar.xz && \
    cd gcc-9.3.0 && \
    contrib/download_prerequisites && \
    mkdir ../gcc-build && cd ../gcc-build && \
    ../gcc-9.3.0/configure --prefix=/usr --enable-languages=c,c++ --enable-multilib && \
    make -j$(nproc) && make install && \
    cd .. && rm -rf gcc-9.3.0 gcc-build
# 安装 pyenv
yum install -y git curl
echo 'export PYTHON_BUILD_MIRROR_URL_SKIP_CHECKSUM=1' >> ~/.bashrc
echo 'export PYTHON_BUILD_MIRROR_URL="https://registry.npmmirror.com/-/binary/python"' >> ~/.bashrc
export GITHUB_MIRROR="https://ghfast.top/https://github.com/"
curl -L https://files.m.daocloud.io/raw.githubusercontent.com/yyuu/pyenv-installer/master/bin/pyenv-installer | sed 's|-c advice.detachedHead=0 -c core.autocrlf=false||g' | sed 's|GITHUB="https://github.com/"|GITHUB="'$GITHUB_MIRROR'"|g' | bash
cat >> ~/.bashrc <<EOF
export PYENV_ROOT="$HOME/.pyenv"
[[ -d $PYENV_ROOT/bin ]] && export PATH="$PYENV_ROOT/bin:$PATH"
eval "$(pyenv init - bash)"

# Restart your shell for the changes to take effect.

# Load pyenv-virtualenv automatically by adding
# the following to ~/.bashrc:

eval "$(pyenv virtualenv-init -)"
EOF
source ~/.bashrc

此时就可以安装高版本 python 了

需要注意的是,经过测试,numpy 编译时,如果不使用 -static-libgcc -static-libstdc++​,打包出来的将会报错如下类似于依赖 GCC_7.0.0

1
2
3
4
5
6
7
# 预配置,尽量 -static-libgcc -static-libstdc++
echo 'export LDFLAGS="$LDFLAGS -static-libgcc -static-libstdc++"' >> ~/.bashrc
# 安装 python 3.12
pyenv install 3.12
# 如果你想安装静态编译版的 python
# PYTHON_CONFIGURE_OPTS="--disable-shared" pyenv install 3.12
# 会生成 libpython.a 静态库,可以使用 nuitka --standalone --static-libpython=yes

然后就可以安装 numpy,requests 等库尽情玩耍了

如果你使用了 numpy 库,并且想使用 nuitka 打包,需要使用如下命令(注意上面的 LDFLAGS 依旧)

1
 ~/venv/bin/nuitka --standalone --include-data-files=/lib/libz.so.1.2.3=libz.so.1 --include-data-files=/usr/lib/libstdc++.so.6.0.28=libstdc++.so.6 --include-data-files=/usr/lib/libgcc_s.so.1=libgcc_s.so.1 main.py

其中的 --include-data-files​ 需要注意,一般是拷贝到其他机器在运行时报错缺少 libz.so.x​ 这种,就需要从编译机上弄一份进去

另外,如果是在此处 32 位系统上编译出来的 exe(依赖 glibc),拷贝到 64 位系统上需要注意:64 位系统上需要先安装 x86 的 glibc(现代很多系统安装软件过程中就已经安装了,如果没有,需要使用 yum install glibc.i686​ 这样的命令安装,apt 系的系统类似)