V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐学习书目
Learn Python the Hard Way
Python Sites
PyPI - Python Package Index
http://diveintopython.org/toc/index.html
Pocoo
值得关注的项目
PyPy
Celery
Jinja2
Read the Docs
gevent
pyenv
virtualenv
Stackless Python
Beautiful Soup
结巴中文分词
Green Unicorn
Sentry
Shovel
Pyflakes
pytest
Python 编程
pep8 Checker
Styles
PEP 8
Google Python Style Guide
Code Style from The Hitchhiker's Guide
xgq89757
V2EX  ›  Python

关于 Python 环境可复现性请教

  •  
  •   xgq89757 · 2025 年 8 月 15 日 · 4619 次点击
    这是一个创建于 155 天前的主题,其中的信息可能已经有所发展或是发生改变。
    公司 toB 项目 python 三方依赖有 150+,有没有比较优雅的方式能严格按照 requirements.txt (可能存在版本冲突)复现依赖环境,pip install -r requirements.txt 有个问题就是没准过段时间构建出来的环境就和 requirements.txt 中的不一致了

    目前的做法是将环境做成了基础镜像,每次交付都基于这个镜像打包。不同版本有增量依赖或版本调整时在 Dockerfile 中单独 pip install ,但这也存在基础镜像管理和基础镜像复现的问题。
    57 条回复    2025-09-09 11:48:03 +08:00
    dzdh
        1
    dzdh  
       2025 年 8 月 15 日
    用 uv 。lock 版本。
    JoeJoeJoe
        2
    JoeJoeJoe  
    PRO
       2025 年 8 月 15 日
    可以给项目加个虚拟环境, python 应该有很多这样的库, pyenv, venv 之类的.

    参考链接:
    1. https://www.cnblogs.com/doublexi/p/15783355.html
    2. https://www.cnblogs.com/doublexi/p/15786911.html
    wombat
        3
    wombat  
       2025 年 8 月 15 日
    venv + requirement ,还有约定指定的 python 版本。 当然,即使这样,python 构建出的成果也有可能有问题。 我曾经在不同的电脑完全一模一样 venv ,依赖版本一样的环境,用 pyinstaller 打包的结果,不一样。😂
    xgq89757
        4
    xgq89757  
    OP
       2025 年 8 月 15 日
    @JoeJoeJoe 虚拟环境是有的,目前用的 miniforge ,主要是环境复现的问题,最近在尝试 pixi
    iorilu
        5
    iorilu  
       2025 年 8 月 15 日
    复杂的项目直接打包部署镜像是最好的
    JoeJoeJoe
        6
    JoeJoeJoe  
    PRO
       2025 年 8 月 15 日
    @xgq89757 环境复现是个啥场景啊, requirements.txt 指定版本号的话, install 下来的每次不应该都是相同的吗😂 没太理解
    Cooky
        7
    Cooky  
       2025 年 8 月 15 日
    @JoeJoeJoe 除了 pip 包剩下的应该就是依赖的二进制库了,二进制库都要一直那就得上 docker 了
    xgq89757
        8
    xgq89757  
    OP
       2025 年 8 月 15 日
    @dzdh 用 ai 对比了 uv 和 pixi ,ai 指出 uv 对需要二进制编译的包支持不太友好,项目中算法和机器学习库不少,最近在尝试 pixi ,仍遇到安装环境出现版本冲突的问题,可能 toml 没有编排好的问题。后边准备尝试下 uv ,验证下 ai 的结论。
    xgq89757
        9
    xgq89757  
    OP
       2025 年 8 月 15 日
    @JoeJoeJoe 没准过几个月 pip install -r 下来发现有些包就不一样了😂
    xgq89757
        10
    xgq89757  
    OP
       2025 年 8 月 15 日
    @blackshadow 还有就是项目是在某些版本冲突下稳定运行的,-r 构建就会报错退出,只能先注释掉冲突的包,最后在单独 install
    JoeJoeJoe
        11
    JoeJoeJoe  
    PRO
       2025 年 8 月 15 日
    @xgq89757 #9 还有这种情况呢? 我倒是一直没注意过, 我一直是能跑就行.

    ps: 要是怕这个的话, 直接把三方依赖的包放到共享文件夹吧, 指定一下 python 包路径
    xgq89757
        12
    xgq89757  
    OP
       2025 年 8 月 15 日
    @JoeJoeJoe 所以暂时的方法是打成了基础镜像。
    killva4624
        13
    killva4624  
       2025 年 8 月 15 日
    @xgq89757 #9 pip install -r 为啥不会一样,没指定版本号吗?
    maocat
        14
    maocat  
       2025 年 8 月 15 日 via Android
    合理怀疑 requirements.txt 里面没有吧对应的的包的关联依赖包拉进来, 试一试 pip freeze 全量输出
    xgq89757
        15
    xgq89757  
    OP
       2025 年 8 月 15 日
    @killva4624 全指定了版本号的,清单是从当前稳定运行的环境中 pip list --format=freeze 拉下来的,
    后续就是想通过这个清单复现环境,但是有些包会出现版本冲突或者构建下来的环境和清单不一致。
    xgq89757
        16
    xgq89757  
    OP
       2025 年 8 月 15 日
    @iorilu 目前就是这样的,运行环境打包成基础镜像,交付打包时再将混淆的工程打包进镜像。但是遇到不允许容器化部署的客户就比较麻烦了,要根据客户的环境做离线包,客户是金融、银行之类的基本都没有公网。
    wombat
        17
    wombat  
       2025 年 8 月 15 日
    @xgq89757 感觉最好的办法就是你上面的了,一是容器化部署;二是全部的依赖包都本地保存离线版,环境里全部离线安装。 感觉你这个要求和我们之前很像,我们之前给客户装环境完全内网,rpm 服务都是自己搭建,找个大硬盘里面装了所有需要的离线包。
    Mithril
        18
    Mithril  
       2025 年 8 月 15 日   ❤️ 3
    虽说也推荐你用 uv ,但你这个环境和要求,光靠 uv 是没法彻底解决的。你要考虑的是依赖的可信与供应链管理,而不简单的一个 lock 。

    正常做法是,内网搭建 pip 的镜像服务,并且配置多个 pip 仓库。至少三个,dev ,integration ,release 。只有 dev 是联通外网的 mirror ,其他两个是本地库。

    然后你开发的时候 pip 指向 dev 库,发版的时候,QA 和测试用 integration 库。这时候把你需要的指定版本的所有依赖从 dev 提升复制到 integration 库内。

    当 QA 和测试完成,再次把这些依赖到 release 库内。作为最终 release 的二进制依赖,同时对依赖进行漏洞检查和制做 SBOM 。

    当然你可以根据你们的要求自己调整一下,可能也用不到这么严格的流程。但本质上为了避免 FOSS 的供应链风险,你应该自己保留所有依赖的二进制及其代码以供审查,并且可以完全从本地构建你的产品。

    别忘了之前 npm 下毒的事。
    xgq89757
        19
    xgq89757  
    OP
       2025 年 8 月 15 日
    @JoeJoeJoe 默认行为:pip 会安装满足条件的最新版本,比如某个三方依赖的子依赖要求是> 1.0 ,这个子依赖当时的最新版本是 1.5 ,当时安装的会是 1.5 ,过一段时间这个子依赖的版本迭代到到了 1.7 ,在这时它会安装 1.7 ,这个好解决,在 requirements.txt 中强制子依赖==1.5 ,但因为项目迭代比较久了,后续有人增加或修改了某些依赖,这个操作是直接在环境中单独安装的(这时候不会暴露冲突问题),然后归档依赖版本到 requirements.txt ,但是项目就是在这样的冲突下稳定运行的,时间久了你想在其他服务器复现环境再通过 requirements.txt 构建的时候就会发现有的包有版本冲突,这时候冲突的依赖只能单独安装,然后刷一遍版本。
    momocraft
        20
    momocraft  
       2025 年 8 月 15 日
    pip freeze 却不能重现有点怪

    涉及到全局安装的包,或者嵌套的 venv 吗?
    zhoudaiyu
        21
    zhoudaiyu  
    PRO
       2025 年 8 月 15 日
    直接 docker 不就完事了
    jasonchen168
        22
    jasonchen168  
       2025 年 8 月 15 日
    我也遇到了,头大。而且 Mac 和 Windows 还有些库不一样
    clemente
        23
    clemente  
       2025 年 8 月 15 日
    pip install -r requirements.txt --target /home/local_env


    下载好 然后上报保存

    下次指定 pythonpath 到这个目录就好
    clemente
        24
    clemente  
       2025 年 8 月 15 日
    @momocraft

    1. 有些包 是 现场编译的 没有预编译 的情况
    2. 有些包 指定的依赖 他没有指定版本. 时间久了就会出依赖冲突
    irory
        25
    irory  
       2025 年 8 月 15 日
    pip 安装可以离线包的,所有依赖都下载本地目录。
    tomczhen
        26
    tomczhen  
       2025 年 8 月 15 日
    不涉及编译安装的话还是好解决的,如果想规避这个问题,以我目前的看法是只能选择提供编译好的二进制仓库源才行,这样就只剩下 conda 可以选了。考虑到商用的话,这个问题确实没那么容易。
    xgq89757
        27
    xgq89757  
    OP
       2025 年 8 月 15 日
    @Mithril 我们确实没这么严格的流程,但是很赞,mark 。
    moudy
        28
    moudy  
       2025 年 8 月 15 日 via iPhone
    @blackshadow 如果出现要打安全性补丁就完蛋了
    sazima
        29
    sazima  
       2025 年 8 月 15 日
    使用 pip download 把包下载下来
    xgq89757
        30
    xgq89757  
    OP
       2025 年 8 月 15 日
    还有种方式就是用脚本去逐个安装 requirements.txt 中的库了,绕过-r 的冲突检查。冲突的库单独装只会有 warnning ,但不会终止安装,最后再扫一遍版本。
    BingoW
        31
    BingoW  
       2025 年 8 月 15 日
    我记得 pip 是有一个原生的方法的,直接将你本地环境所有依赖达成一个大的 zip 包,然后在目标服务器直接安装就好了,目标服务器不需要联网,所有版本都跟本地服务器一致。
    xgq89757
        32
    xgq89757  
    OP
       2025 年 8 月 15 日
    @BingoW 之前公司内网的开发、测试、试用环境就是类似方法,直接压缩 miniforge 的 env 下对应工程的虚拟环境进行迁移。后来让我全部改成容器化部署了,省去了很多工作量。测试基于镜像测试,交付时直接交付镜像文件。
    xgq89757
        33
    xgq89757  
    OP
       2025 年 8 月 15 日
    @moudy 目前是基于基础镜像在工程的 Dockerfile 中打补丁。
    BingoW
        34
    BingoW  
       2025 年 8 月 15 日
    @xgq89757 嗯 这个是很好的办法
    SunDoge
        35
    SunDoge  
       2025 年 8 月 15 日
    @xgq89757 #8 大部分算法库都提供了预编译二进制包,用 uv 很少会出现不友好的地方。pypi 的 python 库比 conda 、conda-forge 还是多的,用 pixi 会遇到部分依赖在 pypi ,部分依赖在 conda 的问题,解决依赖冲突会比较麻烦。
    misoomang
        36
    misoomang  
       2025 年 8 月 15 日
    即使 requirment.txt 的包指定了版本,每个包对应支持的 Python 版本的范围会不一样,如果传统部署的 Python 版本不固定,即使指定了 pip 包的版本,在不同的 Python 版本下安装也会出现差异
    Hopetree
        37
    Hopetree  
       2025 年 8 月 15 日
    如果是容器肯定是搞成基础镜像是最优解啊,特别是机器学习应该会设计到一些需要编译使用的第三方库,这种库的安装不仅仅是 pip 能解决的,还跟服务器环境有关,很难保证依赖顺利安装,所以基础镜像就可以忽略这些问题
    h404bi
        38
    h404bi  
       2025 年 8 月 15 日
    我看你说在用 pixi 了,那有试过用 pixi-pack 直接打包环境么?

    https://github.com/Quantco/pixi-pack
    SmiteChow
        39
    SmiteChow  
       2025 年 8 月 15 日
    十年前的工程实践是把所有包下载下来放到 git 里面去,现在依然是这样,没办法。
    lovepocky
        40
    lovepocky  
       2025 年 8 月 15 日
    poetry
    1018ji
        41
    1018ji  
       2025 年 8 月 15 日
    把所有的库都捞下来,库还依赖库,只搞几个必然起飞
    xgq89757
        42
    xgq89757  
    OP
       2025 年 8 月 15 日
    @Hopetree 是的,我们的项目就是模型平台,会有数据分析和建模场景,不少库需要编译。客户环境也不统一,有 arm 的有 x86 的,还有的会有信创要求。
    xgq89757
        43
    xgq89757  
    OP
       2025 年 8 月 15 日
    @SunDoge 目前就有 conda 、pypi 混用的情况,有些库 pypi 装起来不是缺这个就是缺那个,然后就直接用 conda 装了。
    xgq89757
        44
    xgq89757  
    OP
       2025 年 8 月 15 日
    @SmiteChow 考虑跨平台还是不太合适,现在的一个交付镜像就得两个过 g 大小。
    xgq89757
        45
    xgq89757  
    OP
       2025 年 8 月 15 日
    @h404bi 打包和环境迁移到不是问题,主要是想寻求种环境从 0 复现的优雅方式。
    fightff
        46
    fightff  
       2025 年 8 月 15 日
    以前我们 python 项目的管理是集中维护 requirement txt, 所有人开发都用统一指定的固定版本。依赖需要升级或者切版本的话要 review 之后再修改。可以减少一定的环境问题。
    fbichijing
        47
    fbichijing  
       2025 年 8 月 16 日
    > pip install -r requirements.txt 有个问题就是没准过段时间构建出来的环境就和 requirements.txt 中的不一致了

    ??这个“没准”是个啥意思?莫须有?
    noparking188
        48
    noparking188  
       2025 年 8 月 16 日
    你这个场景不可能吧,150+个依赖谁知道里面又衍生依赖了几百个,有没有二进制编译的依赖。镜像是对的,离线安装 docker ,各大 Linux 发行版挨个支持一下离线安装。
    我之前给银行交付,就是写脚本离线安装 docker 起容器。
    给未知的几百个 python 包依赖做各个系统兼容,不如把已知的 docker 各系统安装兼容做好。
    不要交付 Dockerfile ,交付编译打包好的镜像 tar 包。
    再优化下把 pip install -r requirements.txt 换成 uv 。
    AkinoKaedeChan
        49
    AkinoKaedeChan  
       2025 年 8 月 16 日 via iPhone
    用 Nix Flake ,lock 不动 100% 复现
    dododada
        50
    dododada  
       2025 年 8 月 18 日
    48 楼的方法是最好的,docker 镜像包直接运行,设备只要能跑 docker ,就不要担心环境问题,另外镜像包交付还可以加密
    xgq89757
        51
    xgq89757  
    OP
       2025 年 8 月 18 日
    @fbichijing 那种 toml 中只指定了最小版本要求的未来迭代情况谁说的准呢,可以看下 19 楼的回复。
    xgq89757
        52
    xgq89757  
    OP
       2025 年 8 月 18 日
    @noparking188 这 150+已经是 pip freeze 导出的所有依赖,有部分需要二进制编译的依赖。交付过程跟你这差不多,都是离线包安装中间件,然后部署各应用服务。
    enrolls
        53
    enrolls  
       2025 年 8 月 19 日
    开源方案没有,楼主愿意付费的话,我给你开发一个 pip 包管理的工具,顺带可以审计安装的包的安全性。
    wenrouxiaozhu
        54
    wenrouxiaozhu  
       2025 年 8 月 19 日
    https://github.com/astral-sh/rye 试试

    absl-py==2.1.0
    # via tensorboard
    # via tensorflow
    amqp==5.2.0
    # via kombu
    appnope==0.1.4 ; sys_platform == 'darwin'
    # via ipython
    asgiref==3.8.1
    # via django
    # via django-cors-headers
    tomkliyes
        55
    tomkliyes  
       2025 年 8 月 21 日
    pip-tools ,会把 requirements 里的包和他下面的依赖全部找出来,固定所有包的版本,重新生成 requirements.txt
    hi9527
        56
    hi9527  
       2025 年 8 月 28 日
    pip install -r aaa.txt -c bbb.txt
    aaa.txt 里面存的是你项目里面直接用到的依赖的包的信息
    bbb.txt 里面存的是你的虚拟环境里面所有的包的信息

    这样的话应该就不会出现由于依赖的依赖变化了导致环境变化了,遇到过这样的坑
    sswfive
        57
    sswfive  
       2025 年 9 月 9 日
    关于   ·   帮助文档   ·   自助推广系统   ·   博客   ·   API   ·   FAQ   ·   Solana   ·   2900 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 28ms · UTC 06:49 · PVG 14:49 · LAX 22:49 · JFK 01:49
    ♥ Do have faith in what you're doing.