Python 项目换台机器就跑不起来:从虚拟环境到系统依赖的排查顺序

举报
yd_217417492 发表于 2026/11/06 09:37:04 2026/11/06
【摘要】 同一份代码在开发机正常,换到服务器或同事电脑就报模块找不到、版本冲突。问题往往不在代码,而在解释器、虚拟环境、锁文件和系统库。本文按从内到外的顺序整理一套排查清单。

先别改代码,确认解释器是谁

“在我电脑上是好的”通常不是一句玩笑,而是环境不一致的真实写照。第一步不是重装依赖,而是确认当前命令到底用了哪个 Python:

which -a python python3
python -V
python -c "import sys; print(sys.executable); print(sys.version)"

which -a 会把 PATH 里所有同名命令都列出来。常见情况是 /usr/bin/python3 和 /usr/local/bin/python3 同时存在,python 指向其中一个,pip 却指向另一个。用 pip -V 看它对应的 site-packages 路径,如果和 sys.executable 不在同一个前缀下,后面装什么都会错位。

虚拟环境有没有真正激活

激活虚拟环境本质上只是修改 PATH 和几个环境变量,它不会跟着代码走。IDE 的运行配置、systemd 服务、cron 任务、Dockerfile 里的 RUN,都可能在一个“没有激活”的 shell 里执行。先看三个值:

echo "$VIRTUAL_ENV"
python -c "import sys; print(sys.prefix)"
pip -V

正常激活时,sys.prefix 应该指向项目下的 .venv,而不是 /usr。在服务和定时任务里,不要依赖 source activate,直接写虚拟环境解释器的绝对路径更稳:

/opt/app/.venv/bin/python /opt/app/main.py

Windows 下对应 .venv\Scripts\python.exe。如果项目用 Poetry、PDM 或 uv,优先用 poetry run、pdm run、uv run,它们会自动找到项目环境,不依赖当前 shell 是否激活。

依赖是不是按锁文件装的

只写 pip install -r requirements.txt 还不够,关键是 requirements 里有没有固定版本和传递依赖。开发机上可能早就装过某个包,pip install 直接跳过了;新机器从零开始,解析出来的版本就可能不同。先做一次一致性检查:

pip check
pip list --outdated
python -m pip debug --verbose | head -n 30

pip check 会报告依赖冲突;pip debug 能看到当前解释器支持的 wheel 标签。团队项目建议提交 poetry.lock、pdm.lock 或 uv.lock,安装时用 frozen 模式,例如 uv sync --frozen。只有 requirements.txt 时,至少把直接依赖和关键传递依赖都固定版本,并区分开发依赖与运行依赖。

另一个容易忽略的点是平台标记。某些包只在 Windows 安装,某些包只有 Linux 轮子:

pywin32==306; sys_platform == "win32"
uvloop==0.21.0; sys_platform != "win32"

如果开发机是 macOS、服务器是 Linux,这类差异必须提前处理,否则锁文件看起来一致,装出来的东西却完全不同。

ModuleNotFoundError 和 ImportError 不是一回事

前者通常是包没装或装到了别的解释器;后者更可能是包在,但内部的二进制扩展加载失败。先用一条命令确认模块的真实位置和版本:

python -c "import numpy; print(numpy.__file__, numpy.__version__)"

如果报 undefined symbol、GLIBC not found 或 cannot open shared object file,问题就在编译扩展和系统库,而不是 pip 源。可以继续查依赖:

ldd $(python -c "import numpy, os; print(os.path.dirname(numpy.__file__))")/_multiarray_umath*.so | grep "not found"

这类错误常见于跨发行版复制虚拟环境、在 Alpine 上装 glibc 轮子、或者升级 Python 小版本后继续用旧环境的 .so。虚拟环境不要跨机器打包复制,应该在目标机器上重建。

系统库和编译工具补齐了吗

很多 Python 包只是系统库的封装。服务器是最小化镜像时,pip 找不到预编译轮子,就会退化成源码编译,然后因为缺头文件失败:

  • psycopg2 需要 libpq-dev 和 pg_config;
  • mysqlclient 需要 MySQL 客户端开发库;
  • Pillow 需要 libjpeg、zlib;
  • cryptography 需要 OpenSSL 开发库;
  • lxml 需要 libxml2、libxslt。

先看能不能只装预编译轮子:

python -m pip install --only-binary=:all: -r requirements.txt

如果这条命令失败,说明某个包没有当前平台的 wheel,需要补系统包或编译工具。不要在生产镜像里长期保留 build-essential,可以分阶段构建,把编译产物复制到运行镜像。

环境变量和配置文件也要对比

代码能导入、依赖也齐了,仍然可能启动失败,因为配置没有跟着过去。.env 是否被加载、变量名大小写是否一致、systemd 有没有写 EnvironmentFile、Docker 有没有传 --env-file,这些都要逐项确认。排查时只打印键名,不要把密钥值打到日志里:

env | cut -d= -f1 | sort
python -c "import os; print(sorted(k for k in os.environ if k.isupper()))"

一套可复用的排查顺序

  1. 确认 python、pip 指向同一个解释器;
  2. 确认 sys.prefix 是项目虚拟环境,而不是系统 Python;
  3. 按锁文件重装依赖,跑一次 pip check;
  4. 区分模块缺失和二进制扩展加载失败;
  5. 补齐系统库和编译工具,优先使用预编译 wheel;
  6. 对比环境变量和配置文件,确认服务进程也能读到。

最后,把可运行环境固化成 Dockerfile 或 devcontainer,至少把 Python 版本、锁文件和系统包写进仓库。环境这件事,文档写十遍不如一条能重复执行的构建命令。

小结

换机器跑不起来,绝大多数时候不是代码写错了,而是解释器、虚拟环境、依赖版本、系统库这四层里有一层没对齐。按顺序排查,比一上来就删环境重装更快,也更不容易把问题越修越乱。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。