使用 PyInstaller 可以将 Python 脚本打包为自带环境的可执行程序。如果需要在 glibc 版本差异较大的系统上执行 Python 程序,推荐构建一个专用打包镜像。
使用 PyInstaller
PyInstaller 使用起来非常简单:
$ mamba activate my-env
$ mamba install -y pyinstaller
$ pyinstaller my_script.py
$ pyinstaller --onefile my_script.py
|
执行打包命令后会在当前目录下创建两个目录和一个文件:
| 名称 |
内容 |
build/ |
临时构建文件 |
dist/ |
最终可执行文件或应用目录 |
*.spec |
打包配置文件 |
我们需要的可执行文件位于 dist 目录下。
容器内打包
选择与目标服务器版本相近的基础镜像打包 Python 脚本,可以避免一些兼容性问题。例如,当打包环境的 glibc 版本(glibc 2.39)比服务器环境(glibc 2.17)新时,PyInstaller 生成的可执行文件可能意外依赖高版本 glibc,该文件在服务器上运行就会报错。
启动打包容器
$ docker run -dit --name=centos7-build centos:7.9.2009 tail -f /dev/null
$ docker exec -it centos7-build bash
...
|
打包完成后,可以在宿主机上将容器内的打包产物复制出来:
$ docker cp centos7-build:/root/my_script/dist/my_script ./
|
查看 glibc 版本
$ ldd --version ldd (Ubuntu GLIBC 2.39-0ubuntu8.7) 2.39 ...
$ ldd --version ldd (GNU lilbc) 2.17 ...
$ objdump -T libstdc++.so.6.0.34 | grep GLIBC_ | sed 's/.*GLIBC_//' | sort -V | tail -1 2.17) secure_getenv
|
构建专用打包镜像
基础打包镜像
该基础打包镜像使用 CentOS 7 镜像作为基础,修复已经 EOL 的默认 YUM 源,安装 wget 下载工具,通过代理从 github 下载 miniforge 安装脚本,创建名为 py312 的 conda 环境(仅安装 python 3.12 和 pyinstaller 模块):
ARG HTTP_PROXY="" ARG HTTPS_PROXY=""
FROM centos:7.9.2009
ARG HTTP_PROXY ARG HTTPS_PROXY
RUN set -ex && \ sed -i 's/^mirrorlist/#mirrorlist/g' /etc/yum.repos.d/CentOS-*.repo && \ sed -i 's|^#baseurl=http://mirror.centos.org|baseurl=http://vault.centos.org|g' /etc/yum.repos.d/CentOS-*.repo && \ yum install -y wget && yum clean all && \ if [ -n "$HTTPS_PROXY" ]; then \ https_proxy="$HTTPS_PROXY" wget -O /tmp/Miniforge3.sh "https://github.com/conda-forge/miniforge/releases/latest/download/Miniforge3-$(uname)-$(uname -m).sh"; \ else \ wget -O /tmp/Miniforge3.sh "https://github.com/conda-forge/miniforge/releases/latest/download/Miniforge3-$(uname)-$(uname -m).sh"; \ fi && \ bash /tmp/Miniforge3.sh -b -p /root/conda && \ rm /tmp/Miniforge3.sh && \ export PATH="/root/conda/bin:$PATH" && \ conda create -n py312 python=3.12 pyinstaller -c conda-forge -y && \ conda clean --all -y
ENV PATH="/root/conda/envs/py312/bin:/root/conda/bin:$PATH" WORKDIR /workspace
|
制作基础打包镜像,可以通过命令行传入代理配置:
$ docker build --build-arg HTTPS_PROXY=$https_proxy -t pyinstaller-centos7 .
|
使用基础打包镜像进行打包,注意需要位于 Python 项目脚本文件 my_scritp.py 和依赖文件 environment.yml 所在目录:
$ docker run --rm -v $(pwd):/workspace pyinstaller-centos7 bash -c "\ conda env update -f /workspace/environment.yml -n py312 && \ pyinstaller --onefile my_scritp.py"
|
专用打包镜像
使用基础打包镜像时每次都需要下载安装 environment.yml 中的模块,如果 Python 项目使用的模块不变,可以为该项目制作一个专用打包镜像:
FROM pyinstaller-builder:latest
COPY environment.yml /tmp/environment.yml
RUN source /root/conda/etc/profile.d/conda.sh && \ conda activate py312 && \ conda env update -f /tmp/environment.yml -n py312 && \ conda clean --all -y
|
制作专用打包镜像,需要将 environment.yml 复制到 Dockerfile 同目录:
$ docker build -t my-script-builder .
|
使用专用打包镜像进行打包:
$ docker run --rm -v $(pwd):/workspace my-script-builder pyinstaller --onefile my_scritp.py
|
参考资料
CI 管线中安装 miniforge
PyInstaller 的功能和工作原理