PyInstaller打包python-keystoneclient在Docker中运行失败求助
PyInstaller打包python-keystoneclient依赖应用的Docker部署问题
本地运行PyInstaller编译后的resourceuploader二进制文件正常,但部署到debian:trixie-slim容器时出现版本元数据找不到的错误,使用Python 3.10 + venv环境,尝试多种自定义Hook组合无效。
自定义Hook
# -*- mode: python ; coding: utf-8 -*- from PyInstaller.utils.hooks import collect_submodules from PyInstaller.utils.hooks import copy_metadata from PyInstaller.utils.hooks import collect_data_files import sys from os import path site_packages = next(p for p in sys.path if 'site-packages' in p) hiddenimports = collect_submodules("python-swiftclient") datas = collect_data_files('os_service_types',subdir='data') datas += collect_data_files('keystoneauth1') datas += collect_data_files('keystoneclient') added_files = [ (path.join(site_packages,'python_keystoneclient-5.3.0.dist-info'), 'python_keystoneclient-5.3.0.dist-info'), ] added_files += copy_metadata('python-keystoneclient') added_files += copy_metadata('keystoneauth1') added_files += copy_metadata('fixtures')
打包命令
pyinstaller --add-data 'bootstrap.yml:.' \ --additional-hooks-dir='custom_hooks' \ --python .venv/bin/python \ --clean \ --onefile resourceuploader.py
Dockerfile
FROM debian:trixie-slim ENV PYTHONUNBUFFERED=1 RUN mkdir /mnt/FTP ADD dist/resourceuploader /resourceuploader ADD dist/dev_config.yml /dev_config.yml ENTRYPOINT ["/bin/bash"] # ENTRYPOINT ["/resourceuploader"]
错误信息
Traceback (most recent call last): File "pbr/version.py", line 475, in _get_version_from_importlib_metadata File "importlib/metadata/__init__.py", line 969, in distribution File "importlib/metadata/__init__.py", line 548, in from_name importlib.metadata.PackageNotFoundError: No package metadata was found for python-keystoneclient During handling of the above exception, another exception occurred: Traceback (most recent call last): File "resourceuploader.py", line 14, in <module> File "<frozen importlib._bootstrap>", line 1027, in _find_and_load File "<frozen importlib._bootstrap>", line 1006, in _find_and_load_unlocked File "<frozen importlib._bootstrap>", line 688, in _load_unlocked File "PyInstaller/loader/pyimod02_importers.py", line 419, in exec_module File "resourceuploader_handler.py", line 9, in <module> File "<frozen importlib._bootstrap>", line 1027, in _find_and_load File "<frozen importlib._bootstrap>", line 1006, in _find_and_load_unlocked File "<frozen importlib._bootstrap>", line 688, in _load_unlocked File "PyInstaller/loader/pyimod02_importers.py", line 419, in exec_module File "swiftclient/__init__.py", line 19, in <module> File "<frozen importlib._bootstrap>", line 1027, in _find_and_load File "<frozen importlib._bootstrap>", line 1006, in _find_and_load_unlocked File "<frozen importlib._bootstrap>", line 688, in _load_unlocked File "PyInstaller/loader/pyimod02_importers.py", line 419, in exec_module File "swiftclient/client.py", line 51, in <module> File "<frozen importlib._bootstrap>", line 1027, in _find_and_load File "<frozen importlib._bootstrap>", line 1006, in _find_and_load_unlocked File "<frozen importlib._bootstrap>", line 688, in _load_unlocked File "PyInstaller/loader/pyimod02_importers.py", line 419, in exec_module File "keystoneclient/__init__.py", line 36, in <module> File "pbr/version.py", line 505, in version_string File "pbr/version.py", line 498, in semantic_version File "pbr/version.py", line 482, in _get_version_from_importlib_metadata File "pbr/packaging.py", line 866, in get_version Exception: Versioning for this project requires either an sdist tarball, or access to an upstream git repository. It's also possible that there is a mismatch between the package name in setup.cfg and the argument given to pbr.version.VersionInfo. Project name python-keystoneclient was given, but was not able to be found. [8] Failed to execute script 'resourceuploader' due to unhandled exception!
解决建议
1. 修复Hook的元数据映射错误
你定义的added_files变量未被PyInstaller识别,PyInstaller只会读取Hook中的datas、hiddenimports等预设变量。同时手动指定dist-info路径容易出现版本兼容问题,修改Hook如下:
# -*- mode: python ; coding: utf-8 -*- from PyInstaller.utils.hooks import collect_submodules, copy_metadata, collect_data_files hiddenimports = collect_submodules("python-swiftclient") hiddenimports.extend(collect_submodules('pbr')) # 新增:确保pbr模块被导入 datas = collect_data_files('os_service_types',subdir='data') datas += collect_data_files('keystoneauth1') datas += collect_data_files('keystoneclient') # 合并元数据到datas,替换原added_files代码 datas += copy_metadata('python-keystoneclient') datas += copy_metadata('keystoneauth1') datas += copy_metadata('fixtures') datas += copy_metadata('pbr') # 新增:pbr自身也需要元数据
2. 在容器内完成打包(推荐)
本地环境与debian容器的系统库可能存在差异,直接在目标容器环境内打包可避免兼容性问题:
- 修改Dockerfile,先安装Python 3.10、venv、gcc等编译依赖;
- 复制代码到容器,创建venv并安装所有依赖;
- 执行PyInstaller打包命令;
- 最后基于打包好的二进制文件构建轻量镜像。
3. 跳过pbr版本检测(终极方案)
如果上述方法无效,可通过mock跳过pbr的版本检测逻辑,在resourceuploader.py最顶部添加:
import sys from unittest.mock import patch # Mock pbr版本检测,返回已知版本号 with patch('pbr.version.VersionInfo.version_string') as mock_version: mock_version.return_value = '5.3.0' # 提前导入依赖模块 import swiftclient import keystoneclient # 后续正常编写业务代码
内容的提问来源于stack exchange,提问作者Cristian Diaz
相关产品推荐
相关产品推荐

