Ubuntu 22.04下PyInstaller打包含SciPy的Python 3.12应用失败(Ubuntu 24.04可正常运行)
我之前也踩过类似跨Ubuntu版本打包SciPy应用的兼容坑,结合你的描述和实际排查经验,给你整理下问题原因和可行解决方案:
问题核心原因
首先明确:Ubuntu 22.04的glibc版本是2.35,而Ubuntu 24.04是2.39。SciPy针对Python 3.12的官方预编译wheel,是基于较新的Linux发行版(比如Ubuntu 24.04、Fedora 39等)构建的,这些wheel自带的OpenBLAS依赖(就是报错的libscipy_openblas-*.so)会调用glibc的新符号,或者使用了Ubuntu 22.04不支持的ELF加载特性,最终导致运行时出现 relocation/符号查找/加载地址偏移错误。
简单说:预编译SciPy 3.12 wheel的依赖环境,比Ubuntu 22.04的系统库版本要新,所以向下兼容失败。
推荐解决方案
方案1:强制SciPy使用Ubuntu 22.04系统级OpenBLAS(最省心)
这个方案不需要复杂编译,直接让SciPy放弃自带的OpenBLAS,改用系统匹配版本:
- 先卸载当前的预编译SciPy:
pip uninstall -y scipy
- 安装Ubuntu 22.04的系统OpenBLAS开发包:
sudo apt update && sudo apt install -y build-essential gfortran python3-dev libopenblas-dev libopenblas0-pthread
- 设置环境变量,强制SciPy编译时链接系统OpenBLAS,再重新安装:
export SCIPY_USE_SYSTEM_OPENBLAS=1 pip install scipy --no-binary :all:
(--no-binary :all:会让pip从源码编译SciPy,但因为我们已经装了系统依赖,编译会自动适配Ubuntu 22.04,耗时大概10-20分钟)
方案2:降级SciPy到兼容Ubuntu 22.04的版本
如果你的代码能兼容旧版SciPy,可以直接降级到1.11.x系列——这个版本的预编译wheel是针对glibc 2.27+构建的,完美适配Ubuntu 22.04:
pip uninstall -y scipy pip install scipy==1.11.4
注意:要确认你的代码没有用到SciPy 1.12+的新API。
方案3:修改PyInstaller配置,替换自带OpenBLAS为系统版
适合不想编译SciPy的场景,手动替换打包的依赖库:
- 找到系统OpenBLAS的路径:
find /usr/lib -name libopenblas.so.0
通常输出是/usr/lib/x86_64-linux-gnu/libopenblas.so.0
2. 修改你的myapp.spec文件,添加系统OpenBLAS的打包规则,排除SciPy自带的OpenBLAS:
a = Analysis( ['myapp.py'], pathex=[], # 把系统OpenBLAS打包进二进制 binaries=[('/usr/lib/x86_64-linux-gnu/libopenblas.so.0', '.')], datas=[], hiddenimports=['scipy', 'numpy'], # 排除SciPy自带的OpenBLAS模块 excludes=['scipy._lib.openblas'], hookspath=[], runtime_hooks=[], excludes=[], win_no_prefer_redirects=False, win_private_assemblies=False, cipher=None, noarchive=False )
- 在你的应用代码开头添加环境变量设置,让程序优先找打包的系统OpenBLAS:
import os import sys if getattr(sys, 'frozen', False): # 设置LD_LIBRARY_PATH指向当前可执行文件所在目录 os.environ['LD_LIBRARY_PATH'] = f"{os.path.dirname(sys.executable)}:{os.environ.get('LD_LIBRARY_PATH', '')}"
- 重新打包并运行:
pyinstaller --onefile myapp.spec ./dist/myapp
方案4:用Ubuntu 22.04 Docker容器打包(保证环境一致性)
如果需要经常跨版本打包,用Docker在纯净的Ubuntu 22.04环境构建,生成的二进制绝对兼容:
- 创建
Dockerfile:
FROM ubuntu:22.04 # 安装基础依赖 RUN apt update && apt install -y python3.12 python3.12-dev python3-pip build-essential gfortran libopenblas-dev RUN pip3 install virtualenv # 创建虚拟环境 RUN virtualenv -p python3.12 /venv # 复制你的项目文件 COPY requirements.txt /app/ WORKDIR /app # 安装依赖(会自动编译适配的SciPy) RUN /venv/bin/pip install -r requirements.txt COPY myapp.py myapp.spec /app/ # 打包应用 RUN /venv/bin/pyinstaller --onefile myapp.spec # 导出打包好的二进制 CMD ["cp", "./dist/myapp", "/output/"]
- 构建并导出二进制:
mkdir -p output docker build -t myapp-build . docker run --rm -v $(pwd)/output:/app/output myapp-build
最终output/myapp就是完全兼容Ubuntu 22.04的可执行文件。
对你已尝试方案的补充说明
你之前安装libopenblas-dev没效果,是因为SciPy的预编译wheel默认会优先使用自己打包的OpenBLAS,不会自动切换到系统库——必须通过环境变量或编译参数强制它使用系统版本。
另外,从源码编译SciPy时,必须安装build-essential gfortran python3-dev这些编译链工具,否则会报错失败。
验证方法
打包完成后,你可以用以下命令确认兼容性:
- 检查二进制依赖的glibc版本:
readelf -d ./dist/myapp | grep GLIBC
如果输出里的GLIBC版本都是GLIBC_2.35及以下,说明适配Ubuntu 22.04。
2. 检查OpenBLAS的依赖来源:
ldd ./dist/myapp | grep openblas
如果指向系统路径或打包的本地库,说明配置生效。




