如何构造一个兼容好的python打包程序
失败的尝试
pyinstaller的兼容性
其优点是能运行,就能打包成功。兼容性依赖于打包环境
举例说明:
比如docker-compose这个命令,早期使用python编写(现已经用golang重写)
用pyinstaller打包,在一些低版本的linux发行版中出现很多 GLIBC_xxxx not found
的问题 issue链接
其兼容性依赖于GLIBC的版本,但GLIBC通常大家都不敢升级
pyoxidizer的兼容性
pyoxidizer尝试通过编译的方式解决兼容性问题
它的原理如下:
- 其编译了静态库https://github.com/indygreg/python-build-standalone
- 可以通过rust的工具链
x86_64-unknown-linux-musl
来静态链接 - 打包python代理,各类资源
这样就没有任何依赖了。
参考链接https://gregoryszorc.com/docs/pyoxidizer/0.24.0/pyoxidizer_packaging_static_linking.html
但也有很大缺点,甚至几乎无法使用,如果应用包含C实现python模块就会失败
原因是
- 静态程序是无法加载动态库(.so文件)
- 即使能加载,这些动态库也可能依赖GLIBC导致兼容性问题
- 要解决1,2只能把这些动态库,也静态链接进来
这就意味着所有C实现python模块都需要重新编译,工程量巨大
GLIBC的兼容性
根据pyoxidizer的文档(链接)
Nearly every binary built on Linux will require linking against libc and will require a symbol provided by glibc. glibc versions it symbols. And when the linker resolves those symbols at link time, it usually uses the version of glibc being linked against. For example, if you link on a machine with glibc 2.19, the symbol versions in the produced binary will be against version 2.19 and the binary will load against glibc versions >=2.19. But if you link on a machine with glibc 2.29, symbol versions are against version 2.29 and you can only load against versions >= 2.29.
This means that to ensure maximum portability, you want to link against old glibc symbol versions. While it is possible to use old symbol versions when a more modern glibc is present, the path of least resistance is to build in an environment that has an older glibc.
其推荐使用debian 8作为基础环境,可以看到debian的GLIBC版本为2.19
1 | root@3174dcfb96c4:~ |
根据 glibc wiki,2.19为2014-02-07发布,非常古老
编译Python
解决apt源问题
1 | echo "deb http://deb.freexian.com/extended-lts jessie main contrib non-free" > /etc/apt/sources.list |
安装编译环境和依赖库
1 | apt update |
编译openssl
Python3.9依赖openssl 1.1.1
1 | tar -xzvf openssl-1.1.1v.tar.gz |
编译python
1 | tar -xzvf Python-3.9.18.tgz |
安装pyinstaller
1 | pip3 install pyinstaller |
构建镜像
打包依赖的
1 | cd /usr |
通过Dockerfile构建镜像
1 | FROM debian:8 |
此时我就成功构建一个兼容性较好的pyinstaller的打包环境