Python 虚拟环境:venv、pip 锁定和那些版本地狱

米阳 2025-10-11 6 10/11

「本地能跑,服务器跑不了」这个问题,九成是依赖版本不一致。虚拟环境是基础操作,但细节没做好照样出问题。

建环境

python3 -m venv .venv
source .venv/bin/activate        # Linux/macOS
.venv\Scripts\activate           # Windows

不要用 virtualenv 了,标准库的 venv 够用。目录名用 .venv,多数工具(VSCode、PyCharm)会自动识别。

装依赖

pip install -r requirements.txt

关键是 requirements.txt 怎么来。三种做法差别很大:

做法一:手写顶层依赖

requests>=2.28
fastapi

干净,但下次装可能拿到不同的次版本,出问题不好复现。

做法二:pip freeze

pip freeze > requirements.txt

记录所有包(含间接依赖)的精确版本,可复现。缺点是分不清哪些是直接依赖,升级时不知道该动哪个。

做法三:分层

# requirements.in(手写)
fastapi
requests

# 生成 requirements.txt
pip install pip-tools
pip-compile requirements.in

pip-compile 生成的文件带注释,标明每个包是谁引入的。改依赖改 .in,重新编译。这是我最推荐的方式。

系统和项目分开装

Ubuntu 上 pip install 到系统环境,迟早会遇到 error: externally-managed-environment。Python 3.11 之后 PEP 668 强制了这一点。

正确做法就是 venv,别用 --break-system-packages 绕过去。

服务器上的路径

宝塔这类面板自带的 Python 环境和系统的、项目的都不一样。部署脚本里一定要写绝对路径:

/www/blog-ai/.venv/bin/python app/serve.py

systemd 的 ExecStart 同理,不要依赖 PATH。

编译依赖

mysqlclient、lxml、Pillow 这些要编译,需要系统装开发包:

apt-get install -y python3-dev default-libmysqlclient-dev build-essential libxml2-dev libjpeg-dev

少一个就会报 fatal error: xxx.h: No such file or directory。用 slim 镜像时尤其容易漏。

版本不一致的排查

python -c "import sys; print(sys.executable, sys.version)"
pip list | head -20

先确认用的是哪个 Python。which python 在激活 venv 之后应该是 .venv/bin/python,如果不是,说明激活失败或者 shell 缓存了路径(hash -r 清一下)。

锁文件的意义

requirements.txt 是约定,锁文件是保证。有 pip-compile 生成的 hash 校验,才敢说「装出来的包一定一样」。生产环境部署前,先在一个干净容器里装一遍,能装成功再上。

一句话

虚拟环境隔离的是「包」,锁文件保证的是「版本」。两个都做好,才不会再遇到「本地能跑线上不行」。

- THE END -

米阳

10月11日16:55

最后修改:2025年10月11日
0

非特殊说明,本博所有文章均为博主原创。

共有 0 条评论