创建和使用 wheelhouse 实现 Python 离线安装
Wheelhouse 是在联网构建机上创建的预构建 wheel 文件目录,用于在隔离的目标机上进行离线安装。通过锁定 requirements 文件并运行 pip wheel,将依赖打包为 tar 归档,最后在目标机上使用 --no-index --no-deps --force-reinstall 参数完成安装。
本文内容
简明答案
Wheelhouse 是一个预构建 wheel 文件的目录,你在具有网络访问权限的机器上创建它,然后将其传输到隔离的目标机器上。构建机读取一个锁定的 requirements 文件,运行带有 --wheel-dir 参数的 pip wheel 命令来编译所有依赖项,并使用 tar 将该目录打包。在目标机器上,你解压该归档文件,并运行带有 --no-index、--no-deps 和 --force-reinstall 参数的 pip install 命令,这样 pip 就永远不会联系 PyPI,而仅安装随附的 wheel 文件。通过在 requirements 文件中包含 --hash 条目,可以增加哈希检查,从而在构建步骤中验证包的完整性。当目标机没有网络访问权限、你希望避免在目标机上重新编译且能够控制操作系统和架构时,此方法非常适用。它不能替代私有索引,因为归档文件是一个静态快照,通常与特定的操作系统和架构绑定,且不提供持续的可用性或访问控制。
使用 pip wheel 构建 wheelhouse
核心工作流始于一个锁定的 requirements 文件。运行 python -m pip wheel -r requirements.txt --wheel-dir=/tmp/wheelhouse 将每个依赖项编译为 wheel 并存储在 wheelhouse 目录中。由于构建机执行编译操作,因此目标机不需要安装编译器或构建工具。命令完成后,wheelhouse 目录将包含每个包的一个 wheel 文件,包括所有传递依赖项。
接下来的步骤是将该目录归档以便传输。在现代 Unix 系统上,运行 tar -cjvf wheelhouse.tar.bz2 -C /tmp/wheelhouse . 来创建单个压缩归档文件。这个归档文件就是你需要发送到隔离环境的内容。requirements 文件是此步骤的输入,因此在构建之前必须确保其准确且完整。
mkdir -p /tmp/wheelhouse && python -m pip wheel -r requirements.txt --wheel-dir=/tmp/wheelhouse && tar -cjvf wheelhouse.tar.bz2 -C /tmp/wheelhouse .实用建议
在构建机上使用 pip freeze 生成 requirements 文件,然后在运行 pip wheel 之前验证该锁定文件是否包含所有传递依赖项。这可以防止因缺失包而导致离线安装失败。
在无网络环境下从 wheelhouse 安装
在目标机器上,将归档文件解压到临时目录,然后运行带有三个重要标志的 pip install 命令。--no-index 告诉 pip 不要联系任何包索引,因此它无法回退到 PyPI。--no-deps 防止 pip 在捆绑包之外解析或安装依赖项,由于 requirements 文件已经列出了所有包,这样做是安全的。--force-reinstall 确保即使已经存在匹配的版本,也会安装捆绑的 wheel 文件。
运行 python -m pip install --force-reinstall --no-index --no-deps /tmp/wheelhouse/* 将安装解压目录中的所有 wheel 文件。这一序列实现了真正的离线安装,因为在安装步骤期间不会发出任何网络请求。requirements 文件必须已经包含所有传递依赖项,否则 --no-deps 将导致环境不完整。
tar -xvf wheelhouse.tar.bz2 -C /tmp/wheelhouse && python -m pip install --force-reinstall --no-index --no-deps /tmp/wheelhouse/*准备锁定的 requirements 文件
Wheelhouse 是基于具有精确版本锁定的 requirements 文件构建的。你可以使用 pip freeze 生成此文件,它会记录当前环境中安装的包,包括传递依赖项,而不仅仅是顶层包。每行使用 == 运算符来要求特定版本,例如 SomePackage == 1.2.3。
锁定版本可以防止新发布版本中的 bug 或不兼容性,因为 pip 在构建或安装步骤期间不会升级任何内容。requirements 文件是 wheel 构建步骤的输入,因此在运行 pip wheel 之前必须完整且准确。如果文件中缺失传递依赖项,离线安装将失败,因为 --no-deps 阻止 pip 从索引中获取该包。在运行 pip wheel 之前,请检查生成的文件以确保其包含所有传递依赖项。
为传输归档 wheelhouse
一旦 wheel 目录填充完毕,请使用 tar 将其转换为单个便携式归档文件。命令 tar -cjvf wheelhouse.tar.bz2 -C /tmp/wheelhouse . 创建一个包含所有 wheel 文件的压缩 tar 归档。这个归档文件将被传输到隔离环境。
该归档是构建机状态的静态快照,因此它保留了精确的编译包及其版本。它不包含构建脚本或源代码,仅包含准备好安装的 wheel 文件。在开始安装之前,必须将归档传输到目标机。由于归档是单个文件,因此很容易在机器之间移动,但它在不同的操作系统或 CPU 架构之间不具备可移植性。
在 wheelhouse 工作流中添加哈希验证
哈希检查可以与 wheelhouse 方法结合使用,因为两者都使用 requirements 文件。你可以在 requirements 文件中添加 --hash 条目,例如 FooProject == 1.2 --hash=sha256:2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824。
当 pip wheel 下载包时会验证这些哈希值,这可以防止源索引或 HTTPS 证书链被篡改。它还能防止在允许更改的索引中,包在版本号未改变的情况下被修改。这是在离线捆绑包之上增加的完整性层。哈希值必须有效并与归档中的包匹配。如果哈希不匹配,pip 将拒绝使用该包,从而保护构建过程免受静默更改的影响。哈希检查模式是完整性层,而不是运行私有索引服务器的省力替代方案,因为它不提供私有索引或供应商库的可用性优势。
在 pyproject.toml 中声明构建依赖
pyproject.toml 中的 [build-system] 表声明了运行项目构建系统必须安装的 Python 级依赖项。requires 键列出了这些依赖项,例如 requires = [setuptools]。
这些构建时需求会影响 pip wheel 编译的内容,因为构建后端需要它们来创建 wheel。理解此表有助于确保 wheelhouse 捕获所有必要的构建工具依赖,以便在构建机上成功编译。如果构建系统需要额外的包,应将其列在 requires 中,以便构建机在运行 pip wheel 之前安装它们。如果没有 pyproject.toml 文件,将适用默认语义,但显式定义表可以使构建需求清晰且可重复。
可移植性限制以及 wheelhouse 不足的情况
Wheelhouse 包含编译后的包,这些包通常与操作系统和架构相关,因此归档文件在不同机器之间不一定可移植。同一个 wheel 文件在不重新编译的情况下无法在不同的操作系统或 CPU 架构上运行。
此方法还需要一台具有网络访问权限的构建机;如果没有机器能访问 PyPI,则此方法无法提供帮助。当你需要跨平台支持或需要静态归档之外的动态审计追踪时,wheelhouse 不能替代私有索引。哈希检查仅提供完整性而非可用性,而 wheelhouse 仅为其包含的特定包和平台提供可用性。如果你需要支持多个平台,必须为每个目标环境构建单独的 wheelhouse。归档是一个快照而非服务,因此无法提供持续的可用性或访问控制。
何时选择 wheelhouse 作为正确工具
当目标环境无法通过网络访问 PyPI、你希望避免在目标机上进行耗时的重新编译,且能够控制操作系统和架构时,wheelhouse 是正确的工具。它将所有编译工作打包到单个归档中,这与单纯的版本锁定或仅使用哈希检查截然不同。
锁定版本可防止新版本的 bug,哈希检查增加了完整性,但两者都无法提供 wheelhouse 的可用性或离线安装的便利性。Wheelhouse 是一组预构建的 wheel 捆绑包,目标机在安装时无需联系任何索引。只有在构建机和目标机共享相同平台时,这种方法才适用于不同的操作系统和架构。对于网络访问不可用或受限的隔离部署,这是一个切实可行的解决方案。
检查清单
- requirements 文件必须使用 == 锁定每个包,并包含所有传递依赖项,而不仅仅是顶层包。
- 构建机必须具有网络访问权限,以便在归档前下载并编译所有依赖项。
- 目标机必须使用与构建机相同的操作系统和 CPU 架构,以确保 wheel 文件的兼容性。
- 安装命令必须包含 --no-index、--no-deps 和 --force-reinstall,以防止网络访问和外部依赖解析。
- 归档必须在传输前使用 tar 从 wheel 目录创建,并在安装前解压。
- requirements 文件中的哈希检查条目必须有效且与归档中的包匹配。
- wheelhouse 包含编译后的包,不能在不同的操作系统或架构之间移植。
- wheelhouse 不能在持续可用性、访问控制或审计管理方面替代私有索引。
适用范围
Wheelhouse 包含编译后的包,通常与操作系统和架构相关,因此归档文件不一定在机器之间可移植。该方法需要一台具有网络访问权限的构建机;如果没有任何机器能访问 PyPI,则无法解决问题。编译后的 wheel 可能包含平台特定的二进制代码,因此仅凭此方法不支持交叉编译。归档是一个静态快照,不提供像私有索引服务器那样的持续可用性或 ACL 管理。使用 --no-deps 意味着操作员必须确保 requirements 文件已经列出了所有传递依赖项。