MongoDB 仓库 s390x/ppc64le 架构 cryptography 预编译 manylinux Wheel 的构建与接入全指南
【免费下载链接】mongoThe MongoDB Database项目地址: https://gitcode.com/GitHub_Trending/mo/mongo
本篇技术指南聚焦 MongoDB 开源仓库中buildscripts/mongo_cryptography_builds/目录的完整职责:为上游 PyPI 不提供 wheel 的s390x、ppc64le架构预编译cryptographyPython 包,并把它接入 Bazel/uv 的依赖解析与 SSL 测试证书生成链路。读完本文,你将掌握该目录build_cryptography_manylinux.sh的构建原理、manylinux_2_28镜像选型依据、S3 上传与简易索引的维护方式,以及pyproject.toml中[[tool.uv.index]]与[tool.uv.sources]的接线方法,并了解版本升级与周边 workaround 清理的完整流程。
一、背景:为什么仓库必须为 s390x / ppc64le 自建 cryptography wheel
MongoDB 的构建/测试链路在每个架构上都需要cryptography,直接原因在于 Bazel 目标//x509:generate_main_certificates。x509/BUILD.bazel 中该目标通过 generate_certificates.bzl 与 main_certs_def.bzl 定义证书集合,最终调用 x509/mkcert.py 生成 SSL 测试证书;而 x509/BUILD.bazel 中mkcert这个py_binary的deps明确依赖cryptography(来自platform依赖组),以及ecdsa、asn1crypto(来自testing依赖组)。
缺少cryptography时该规则无法运行,进而导致两类测试在 s390x / ppc64le 上失败:
jstests/core/testing/certs_are_generated.js(校验生成的测试证书);- 整个
jstests/client_encrypt/目录(客户端字段级加密相关测试)。
而问题在于:cryptography41+ 附带 Rust 扩展,PyPI 只发布manylinux/musllinux的x86_64、aarch64、armv7lwheel,没有s390x与ppc64le的二进制 wheel。因此本目录的使命就是补齐这两份 wheel,上传至s3://mdb-build-public/cryptography_wheels/simple/,再由顶层 pyproject.toml 通过[[tool.uv.index]]与[tool.uv.sources]消费——这与buildscripts/mongo_rapidyaml_builds/处理rapidyaml的方式完全同构。
二、为什么选择预编译 wheel,而不是 sdist + Bazel 内 Rust
cryptography35+ 携带 Rust 扩展,PyPI 上的 sdist(如cryptography-44.0.2.tar.gz)通过maturin/setuptools-rust构建,要求构建后端具备 Rust ≥ 1.65。但在pip.parse的密闭(hermetic)构建沙箱(whl_libraryrepository rule)内很难引入 Rust:要么把树内 Rust 工具链接入rules_python,要么做穿透沙箱的环境变量透传——两条路都代价高昂且违背沙箱初衷。
从 build_cryptography_manylinux.sh 的注释可以看到失败时的典型报错:error: rustc not found或toolchain 'stable-<arch>-unknown-linux-gnu' is not installed。预先在带 rustup 的manylinux_2_28_<arch>容器中构建好 wheel,pip.parse便只需像下载普通包一样获取 wheel,完全绕开沙箱限制。这正是本目录与mongo_rapidyaml_builds一致的"以容器预构建替代沙箱内编译"思路。
三、为什么每架构只需一个 wheel:abi3 稳定 ABI
cryptography发布的是abi3wheel(cp37-abi3-…),即针对 Python 3.7 稳定 ABI 构建的 wheel 可被所有 Python 3.7+ 解释器复用。因此无需按 Python 版本逐个构建,全仓库总共只需要两个 wheel:
cryptography-<ver>-cp37-abi3-manylinux_2_28_s390x.whlcryptography-<ver>-cp37-abi3-manylinux_2_28_ppc64le.whl
这也是 build_cryptography_manylinux.sh 中"由 cp313 解释器构建的 wheel 可供所有 Python 3.7+ 消费者使用"注释的依据。脚本默认的PYTHON_TAG=cp313-cp313只决定容器内由哪个解释器调用构建,并不影响最终 wheel 的 ABI 标签。
四、为什么选 manylinux_2_28 而非 manylinux2014
仓库此前曾以manylinux2014(CentOS 7 基座,glibc 2.17)为目标,但在 s390x / ppc64le 上因两个原因失败:
- 无预编译 OpenSSL:
pypa/manylinux2014_s390x与pypa/manylinux2014_ppc64le不带预编译 OpenSSL(pypa CI 无法为这些架构干净地交叉编译),pkg-config openssl返回空,openssl-syscrate 随即以"Could not find OpenSSL installation"失败。 - 镜像源不可靠:s390x 镜像依赖
clefos-rhyum 源(mirrors.sinenomine.net),它是该架构唯一可用的 RHEL 7 extras 重建源,却经常缓慢或不可达,无法保证在容器内现装 OpenSSL。
manylinux_2_28_<arch>以 AlmaLinux 8(当前 RHEL 8 重建版)为基础,使用指向mirror.almalinux.org(稳定)的 dnf,可以秒级完成dnf install openssl-devel libffi-devel获取头文件。更重要的是,上游 cryptography 项目发布 PyPImanylinux_2_28_{x86_64,aarch64,armv7l}wheel 时使用的正是同一类镜像,本仓库是在复刻上游自身的构建环境。
代价与前提:manylinux_2_28要求消费方宿主 glibc ≥ 2.28。仓库相关 CI 发行版均满足:
| 发行版 | glibc |
|---|---|
| rhel83-zseries-small / rhel83-zseries-large | 2.28 |
| rhel81-power8-small / rhel81-power8-large | 2.28 |
| rhel9-zseries-_ / rhel9-power-_ | 2.34 |
五、构建脚本build_cryptography_manylinux.sh深度解析
仓库中脚本为 build_cryptography_manylinux.sh,适用于 Linux 的s390x、ppc64le(也可在x86_64/aarch64上重新托管运行),输出dist/cryptography-*.whl。
5.1 环境变量一览
| 变量 | 用途 | 默认值 |
|---|---|---|
CRYPTOGRAPHY_VERSION | 要构建的 wheel 版本,必填,且应与 pyproject.tomlplatform依赖组中的 pin 一致 | required |
RUST_VERSION | 容器内 rustup 工具链 pin(cryptography 44.x 要求 ≥ 1.65,此处 pin 以保证可复现) | 1.74.0 |
PYTHON_TAG | manylinux 的 Python 解释器标签(只影响由谁调用构建;wheel 本身是 abi3) | cp313-cp313 |
ARCH | 目标架构(s390x、ppc64le、x86_64、aarch64) | 宿主架构uname -m |
PLATFORM | Docker--platform覆盖(用于通过 QEMU 交叉构建非本机架构) | 未设置(宿主架构) |
OUT_DIR | 输出目录 | ./dist |
5.2 脚本关键步骤
从 build_cryptography_manylinux.sh 可见,脚本先把ARCH映射到对应的quay.io/pypa/manylinux_2_28_<arch>镜像与AUDITWHEEL_PLAT平台标签,并校验CRYPTOGRAPHY_VERSION非空。随后在容器内依次执行(build_cryptography_manylinux.sh):
- 校验 glibc 基线:
ldd --version打印首行确认镜像基线符合manylinux_2_28。 - 安装原生依赖:
dnf -y install openssl-devel libffi-devel pkgconfig——OpenSSL 头文件供openssl-syscrate 使用,libffi 供cffi构建使用;随后用pkg-config --modversion openssl验证。 - 安装 Rust 工具链:通过官方 rustup 脚本安装 pin 到
RUST_VERSION的 minimal profile,并将$CARGO_HOME/bin加入 PATH,输出rustc --version与cargo --version确认。 - 从 sdist 构建 wheel:用
$PYBIN -m pip wheel --no-binary cryptography --no-deps -w /tmp/wheelhouse "cryptography==${CRYPTOGRAPHY_VERSION}"强制从源码构建(s390x / ppc64le 在 PyPI 本就没有 wheel,此写法在 x86_64 / aarch64 上同样生效,使脚本全架构统一)。 - auditwheel 修复:
auditwheel show检查依赖,auditwheel repair --plat "$AUDITWHEEL_PLAT"把 OpenSSL、libffi 共享库 vendoring 进 wheel,保证产物在manylinux_2_28基线上无需外部运行时依赖;若已合规则跳过并直接拷贝。 - 冒烟测试:把修复后的 wheel 强制安装进一个全新的 venv,执行
from cryptography import x509、hashes、serialization、ec等导入并打印版本与架构,再输出sha256sum——确保不是装上了构建后端遗留的 egg 而误判成功。
六、前置条件与实操构建
6.1 前置条件
- Docker。
- 若要跨架构(QEMU)构建非本机架构,需在宿主机一次性启用 binfmt:
docker run --privileged --rm tonistiigi/binfmt --install all6.2 s390x 构建
CRYPTOGRAPHY_VERSION=44.0.2 ARCH=s390x PLATFORM=linux/s390x \ ./build_cryptography_manylinux.sh6.3 ppc64le 构建
CRYPTOGRAPHY_VERSION=44.0.2 ARCH=ppc64le PLATFORM=linux/ppc64le \ ./build_cryptography_manylinux.sh两个脚本均在容器内完成import cryptography; print(__version__)冒烟测试后才宣告成功,wheel 落盘到dist/。
七、上传 S3 并刷新索引
wheel 最终从s3://mdb-build-public/cryptography_wheels/simple/被消费,使用具备相应权限的 AWS 凭证上传:
aws s3 cp dist/cryptography-44.0.2-cp37-abi3-manylinux_2_28_s390x.whl \ s3://mdb-build-public/cryptography_wheels/cryptography-44.0.2-cp37-abi3-manylinux_2_28_s390x.whl aws s3 cp dist/cryptography-44.0.2-cp37-abi3-manylinux_2_28_ppc64le.whl \ s3://mdb-build-public/cryptography_wheels/cryptography-44.0.2-cp37-abi3-manylinux_2_28_ppc64le.whl上传后需刷新s3://mdb-build-public/cryptography_wheels/simple/index.html。现有rapidyaml_wheels/simple/的索引是一个静态 HTML,每个 wheel 一个<a>链接,cryptography 的索引照此生成即可。最小模板如下:
<!doctype html> <html> <body> <a href="https://mdb-build-public.s3.amazonaws.com/cryptography_wheels/cryptography-44.0.2-cp37-abi3-manylinux_2_28_s390x.whl" >cryptography-44.0.2-cp37-abi3-manylinux_2_28_s390x.whl</a ><br /> <a href="https://mdb-build-public.s3.amazonaws.com/cryptography_wheels/cryptography-44.0.2-cp37-abi3-manylinux_2_28_ppc64le.whl" >cryptography-44.0.2-cp37-abi3-manylinux_2_28_ppc64le.whl</a ><br /> </body> </html>八、在 pyproject.toml 中接线消费
8.1 依赖声明
当前仓库 pyproject.toml 的platform依赖组已把cryptographypin 为==44.0.2,注释明确说明:选择精确 pin 而非>=44.0.2,<45,是为了让各平台 uv 选中的版本与已上传 wheel 的版本严格一致,升级前必须先重建两个架构的 wheel。
8.2 自定义索引与 sources 映射
在文件底部的 uv 配置区(与 rapidyaml 条目相邻,见 pyproject.toml),当前实现为每条架构各一个 URL source,并以platform_machinemarker 匹配当前平台;其余架构回退到 PyPI 的标准cp37-abi3wheel:
[[tool.uv.index]] name = "mdb-build-public" url = "https://mdb-build-public.s3.amazonaws.com/rapidyaml_wheels/simple/" explicit = true [tool.uv.sources] cryptography = [ { url = "https://mdb-build-public.s3.amazonaws.com/cryptography_wheels/cryptography-44.0.2-cp37-abi3-manylinux_2_28_s390x.whl", marker = "platform_machine == 's390x'" }, { url = "https://mdb-build-public.s3.amazonaws.com/cryptography_wheels/cryptography-44.0.2-cp37-abi3-manylinux_2_28_ppc64le.whl", marker = "platform_machine == 'ppc64le'" }, ]README 还给出了另一种等效写法——把 URL source 换成自定义索引 + marker 形式(explicit = true使 uv 只为显式 opt-in 的包咨询该索引):
[[tool.uv.index]] name = "mongodb-cryptography" url = "https://mdb-build-public.s3.amazonaws.com/cryptography_wheels/simple/" explicit = true [tool.uv.sources] cryptography = [ { index = "mongodb-cryptography", marker = "platform_machine == 's390x' or platform_machine == 'ppc64le'" }, ]无论哪种写法,都必须在 wheel 上传成功之后再改配置——因为这一步才是重新启用//x509:generate_main_certificates的关键,提前修改会导致uv lock/pip.parse找不到 wheel 而失败。配置完成后刷新两个锁文件:
uv lock bazel run //bazel/uv:export九、升级 cryptography 版本的顺序
升级 pyproject.toml 中的 pin 时,必须先重建并重新上传两个 wheel,再 bump 版本。由于 wheel 文件名内嵌版本号,新旧 wheel 可以在同一个 bucket 中并存,这也保证了平滑升级。
十、同一提交中应移除的周边 workaround
当 wheel 已上传且pyproject.toml改动落地后,以下为绕开 s390x / ppc64le 缺包问题而存在的 workaround 即可一并清理:
BUILD.bazel:删除devcore、dist-test、dist-test-debug安装规则中root_files = select({...})里":linux_s390x": {}/":linux_ppc64le": {}分支(搜索注释 "omit x509 cert generation on s390x / ppc64le")。文件顶部的:linux_s390x与:linux_ppc64le两个config_setting可保留——虽然当前无人使用,但成本低且未来可能有用。jstests/core/testing/certs_are_generated.js:从@tags块移除incompatible_s390x与incompatible_ppc。etc/evergreen_yml_components/tasks/resmoke/server_divisions/clusters_and_integrations/tasks.yml:从client_encrypt任务的tags:列表移除"incompatible_s390x"与"incompatible_ppc"。evergreen/functions/venv_setup.sh:s390x | ppc64le)分支下的 rustup 工具链安装块,在 venv 侧也能拉取二进制 wheel 后即冗余,但保留无害(还能防御未来 sdist 依赖),可在清理阶段再议。
十一、总结
buildscripts/mongo_cryptography_builds/用"容器预构建 + S3 静态索引 + uv sources 覆盖"的组合拳,解决了cryptography在 s390x / ppc64le 上无 PyPI wheel、且密闭沙箱内无法编译 Rust 扩展的双重难题。其关键决策链条清晰:abi3 特性决定了每架构只需一个 wheel;manylinux_2_28(AlmaLinux 8)镜像提供了稳定的 OpenSSL/libffi 与可用的 dnf 源,并与上游构建环境保持一致;auditwheel repair保证产物自带运行依赖;构建后的冒烟测试确保不是假成功。这套模式与mongo_rapidyaml_builds完全同构,可作为仓库内"为小众架构预编译 Python 扩展 wheel"的通用范式参考。
【免费下载链接】mongoThe MongoDB Database项目地址: https://gitcode.com/GitHub_Trending/mo/mongo
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考