Skip to content

bcrypt 不兼容 + ARM64 离线部署:依赖地狱逃生指南 ​

一句话总结:bcrypt 在不同 CPU 架构和 Python 版本间存在二进制不兼容问题,pyinstaller 打包后在 ARM64 上崩溃,最终用 venv + pip install 源码安装配合 requirements.txt 锁定版本才是离线部署的最稳方案。

字段内容
标签#Python #bcrypt #依赖管理 #ARM64 #pyinstaller #离线部署
踩坑日期2026-07-19(bcrypt)/ 2026-07-22(ARM64)
严重程度💀 高(导致服务无法启动)

事故经过 ​

踩坑 #1:bcrypt 版本不兼容(2026-07-19) ​

背景:法眼项目的用户认证系统用了 bcrypt 做密码哈希。在腾讯云 x86 服务器(Python 3.10)上开发测试一切正常。

事故:

  1. 我在 requirements.txt 里写了 bcrypt,没指定版本

  2. pip 自动装了最新版 bcrypt==4.2.1

  3. 本地开发正常,部署到另一台机器后报错:

    ImportError: cannot import name '_bcrypt' from 'bcrypt'
  4. 我二话不说开始改代码适配新版 API……改了半小时,越改越乱

  5. 风哥喊 STOP,说:"你怎么不自问,为什么升级前能用、升级后不能用?"

  6. 回头一看——是因为 pip install bcrypt 没锁版本,另一台机器装的是 4.1.x,而我开发机是 4.2.x,API 变了

教训:发现问题后没有立刻停手汇报,而是自己闷头改。违反了铁律第 0 条。

踩坑 #2:pyinstaller + ARM64 + bcrypt(2026-07-22) ​

背景:要给风哥的 ARM64 机器(阿里云 39.105.33.24,已退)部署一个 Python 工具包。我觉得 pyinstaller 打包成单个二进制文件最方便。

事故:

  1. 在 x86 开发机上用 pyinstaller 打包,一切正常
  2. 把二进制文件传到 ARM64 机器上 → Exec format error(架构不匹配,预料之内)
  3. 在 ARM64 机器上安装 pyinstaller 重新打包 → 打包成功
  4. 运行 → 闪退,没有任何输出
  5. 加了 --debug 重新打包,看到:
    ImportError: /tmp/_MEIxxxxx/bcrypt/_bcrypt.abi3.so: cannot open shared object file
  6. 检查发现 bcrypt 的 .so 文件在 ARM64 上编译出来的和 x86 完全不同
  7. pyinstaller 的 --onefile 模式把二进制 so 文件打包到临时目录,但 ARM64 的兼容层有问题

折腾了三小时无果,最终回到最原始最稳的方案:venv + 源码安装。

根因分析 ​

bcrypt 为什么会有架构兼容性问题? ​

bcrypt 是一个 C 扩展模块,不是纯 Python:

bcrypt
├── __init__.py        # Python 封装层
├── _bcrypt.abi3.so    # C 编译的二进制(Linux)
└── _bcrypt.pyd        # C 编译的二进制(Windows)

核心加密算法是用 C/Rust 写的,编译成平台相关的二进制文件。所以:

  • x86 上编译的 .so 在 ARM64 上跑不了
  • Python 3.10 编译的 .so 在 Python 3.12 上可能跑不了
  • 不同版本的 bcrypt 可能有不同的 ABI

pyinstaller 为什么在 ARM64 上出问题? ​

pyinstaller 的原理是:

  1. 分析 Python 脚本的依赖
  2. 把 Python 解释器 + 所有依赖打包到一个文件夹/文件
  3. 运行时解压到临时目录

问题出在第 2 步:它打包的是当前机器编译的二进制。如果目标机器架构不同,直接跪。

--onefile 模式更是雪上加霜:把整个 Python 运行时塞进一个文件,运行时解压到 /tmp/_MEIxxxxx/。ARM64 的某些 Linux 发行版对临时目录的执行权限有额外限制。

离线部署的四个流派对比 ​

方案便携性兼容性体积适合场景
pyinstaller 打包⭐⭐⭐⭐⭐⭐⭐大同架构分发 GUI 工具
venv + pip freeze⭐⭐⭐⭐⭐⭐⭐⭐中服务端部署(最佳)
Docker 镜像⭐⭐⭐⭐⭐⭐⭐⭐很大微服务/云原生
conda 环境⭐⭐⭐⭐⭐⭐⭐很大数据科学

结论:venv 方案为什么最稳 ​

bash
# 在目标机器上创建虚拟环境
python3 -m venv venv

# 激活后用 pip 装,自动根据当前架构编译 C 扩展
source venv/bin/activate
pip install -r requirements.txt

每一步都是标准操作,没有黑魔法。出问题能查能修。

解决方案 ​

正确的依赖管理流 ​

Step 1:锁定版本——写 requirements.txt 时加 ==

txt
# ❌ 错误写法——不锁版本
flask
bcrypt
pyjwt

# ✅ 正确写法——精确锁定
flask==3.1.0
bcrypt==4.2.0
pyjwt==2.9.0
gunicorn==23.0.0

Step 2:生成锁定文件

bash
# 在开发机上
pip freeze > requirements.txt

# 或者只导出直接依赖
pip install pip-tools
pip-compile requirements.in  # 生成 requirements.txt,含所有传递依赖

Step 3:在目标机器上部署

bash
# 创建虚拟环境
python3 -m venv /opt/myapp/venv

# 激活
source /opt/myapp/venv/bin/activate

# 安装依赖(自动编译 C 扩展到当前架构)
pip install -r requirements.txt

# 验证
python -c "import bcrypt; print(bcrypt.__version__)"

Step 4:配置 systemd 服务(使用 venv 里的 Python)

ini
# /etc/systemd/system/myapp.service
[Unit]
Description=My App
After=network.target

[Service]
Type=simple
User=www-data
WorkingDirectory=/opt/myapp
ExecStart=/opt/myapp/venv/bin/python /opt/myapp/app.py
Restart=on-failure
RestartSec=5

[Install]
WantedBy=multi-user.target
bash
sudo systemctl daemon-reload
sudo systemctl enable --now myapp

离线部署(目标机器没有网络) ​

在联网机器上准备离线安装包:

bash
# 下载所有依赖的 .whl 文件
mkdir /tmp/pip-offline
pip download -r requirements.txt -d /tmp/pip-offline/

# 打包
tar czf pip-offline.tar.gz -C /tmp pip-offline/

在离线目标机器上安装:

bash
# 解压
tar xzf pip-offline.tar.gz

# 创建 venv
python3 -m venv venv
source venv/bin/activate

# 离线安装(--no-index 不从 PyPI 下载,--find-links 从本地目录找)
pip install --no-index --find-links ./pip-offline/ -r requirements.txt

bcrypt 兼容性快速诊断 ​

bash
# 1. 看安装的版本
pip show bcrypt

# 2. 看是否包含 C 扩展
python -c "import bcrypt; print(dir(bcrypt))"
# 输出中应该有 'hashpw', 'gensalt', 'checkpw'

# 3. 测试加密
python -c "
import bcrypt
pw = bcrypt.hashpw(b'test', bcrypt.gensalt())
print('hash ok:', bcrypt.checkpw(b'test', pw))
"

如果 bcrypt 实在装不上 ​

替代方案 —— 用纯 Python 实现的 bcrypt(不需要 C 编译器):

bash
pip uninstall bcrypt
pip install bcrypt==3.2.2  # 纯 Python 实现的老版本

或者换库:

bash
# hashlib(标准库,无需额外安装,但功能较弱)
python -c "import hashlib; print(hashlib.sha256(b'pw').hexdigest())"

# passlib(兼容性好,支持多种哈希算法)
pip install passlib[bcrypt]

预防措施 ​

依赖管理铁律 ​

  1. requirements.txt 必须锁版本 — 用 == 不是 >=
  2. 部署前在目标架构上测试 — x86 上开发,ARM64 上部署,中间必须验证
  3. C 扩展模块需要额外警惕 — bcrypt、lxml、Pillow、numpy 等,架构不同就不能跨机拷贝
  4. 优先用 venv + pip,别过早优化用 pyinstaller — 简单就是稳
  5. pip freeze 要定期跑 — 确保锁定的版本和实际安装的一致

检查清单 ​

bash
# □ requirements.txt 所有包都有 == 版本号?
grep -v '==' requirements.txt | grep -v '^\s*#' | grep -v '^\s*$'

# □ 有没有纯 C 扩展?
grep -E "(bcrypt|lxml|Pillow|numpy|scipy|tensorflow|torch)" requirements.txt

# □ 在目标机器上跑过吗?
ssh target "python -c 'import bcrypt; print(\"ok\")'"

# □ venv 里的 python 路径写在 systemd 配置里了吗?
grep ExecStart /etc/systemd/system/myapp.service

关联知识点 ​


🎯 本章要点 ​

  • C 扩展版本不兼容需锁定版本并固定 requirements.txt
  • 离线部署用 pip download 下载依赖再搬迁
加载练习题中...