尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

统信UOS与麒麟Kylin下用pyenv实现Python多版本管理实战

统信UOS与麒麟Kylin下用pyenv实现Python多版本管理实战 我最近在统信UOS上把一个维护了快两年的Python项目从3.6升到3.12光是版本环境就折腾了一天半。痛定思痛之后我把团队的开发机全部统一成了pyenv方案。现在不管是信创环境下的统信UOS还是麒麟Kylin OS只要是接手的国产化Linux机器我第一件事就是装pyenv把多版本Python开发环境彻底管起来。这篇文章就从一个实际开发者的角度把统信UOS/麒麟Kylin OS下利用pyenv搭建Python多版本开发环境的过程完完整整记录下来。先说清楚这篇文章能帮你解决什么问题你在国产Linux系统上开发Python项目系统自带版本太老不敢动手头几个项目分别要Python 3.6、3.9、3.12环境换来换去装个包动不动就Permission deniedpip install把系统搞到依赖错乱。这些问题pyenv都能解决。它会让你像切微信账号一样随时切Python版本每个项目又能独立隔离依赖适合信创替代阶段的开发人员、运维转开发的同行以及所有在国产系统上被Python版本搞到头秃的朋友。1. 为什么要在国产系统上用pyenv管Python1.1 系统自带的Python到底卡在哪统信UOS和麒麟Kylin OS默认都预装了Python但版本普遍较老。统信UOS 20系列通常带的是Python 3.7.x或3.8.x麒麟Kylin V10部分版本带的是3.6.x甚至更旧的2.7。我在实际项目里碰到过的最典型场景系统自带的Python 3.7跑老项目没问题但新项目用了functools.cache、zoneinfo这类3.9才有的标准库特性一跑就报错而如果去动系统自带的Python又担心把桌面环境的底层脚本搞坏。更深层的问题在于系统自带的Python往往被包管理工具和系统服务绑定。你在统信UOS的软件源里执行sudo apt install python3-pip它装的是给系统用的pip装了哪些包、升级到哪个版本都会直接影响系统组件。我之前在一台麒麟机器上执行了sudo pip3 install --upgrade pip结果系统自带的某个管理脚本因为依赖不兼容直接罢工最后只能重装系统组件非常折腾。所以在国产系统上做开发第一步就是隔离你自己的开发环境绝对不能和系统Python混在一起。1.2 多版本需求不是矫情是真痛点有的人觉得一个系统装一个Python就够用了。但实际开发中的需求非常现实政府项目、银行项目、老系统的维护往往绑定Python 3.6/3.7新开发的服务又想用Python 3.11/3.12的新特性还有做算法测试的同学要在一台机器上对比不同Python版本下的性能差异。我最近给一个客户做信创适配对方的要求是在麒麟Kylin OS上验证同一个算法在Python 3.8和Python 3.12下的运行效率如果没有版本管理工具这活根本没法干。还有一点容易忽略不同Python版本编译出来的第三方扩展包也不一样比如numpy、pandas在Python 3.8和3.12下的二进制wheel包是分开的。如果强行用一个版本跑所有项目最终结果就是某些包装不上某些包不敢升级。pyenv的价值就在于它可以为每个项目定义一个Python版本切换项目目录后python命令自动指向对应版本从源头上消灭这种混乱。1.3 为什么选pyenv而不是venv、conda或Docker我经常被问到Python官方不是有venv吗不是有conda吗为什么还要用pyenv这里我直接说结论它们解决的问题不一样。venv解决的是“同一个Python版本下不同项目的依赖隔离”它不管你系统里装的是哪个Pythonconda解决的是“Python和C库的二进制分发”它自带一套包管理器但体积大、环境逻辑重而pyenv解决的是“我要在多个完整的Python版本之间切换”这是前面两者做不了的事。用一张表看更清楚工具核心定位是否管理Python版本典型使用场景pyenv版本管理是编译/安装任意版本一台机器跑多个Python版本venv依赖隔离否项目/环境依赖隔离conda发行版依赖管理是但自带一套生态科学计算、C库依赖复杂的项目Docker环境隔离间接整套环境打包交付在我个人的工作习惯里pyenv和venv或者pyenv-virtualenv是配合使用的pyenv负责“装哪个版本的Python”venv/pyenv-virtualenv负责“这个项目用哪些包”。至于Docker适合做交付、做集群部署但在日常开发机上频繁改代码Docker的体验相对笨重。所以这篇文章的重点就是pyenv配合它的插件pyenv-virtualenv把版本管理和依赖隔离一次性打通。1.4 pyenv的工作原理一条无形的路径拦截pyenv的原理听起来抽象但弄懂了之后遇到问题排查会非常顺手。它本质上是在PATH环境变量的最前面插入了一个目录~/.pyenv/shims这个目录里放了一堆“替身”文件名字和Python相关命令完全一样python、pip、python3等等。当你执行python命令时系统先找到shims目录下的替身替身再根据当前目录的.python-version文件或者PYENV_VERSION环境变量去判断究竟该调用哪个真实版本的Python。生活化类比就像一个公司前台访客到了先找前台前台查一下访客要去哪个部门再把他带过去。你要切换Python版本就是告诉前台换一个部门前台之后的所有带路工作都是自动完成的。这个设计有一个好处它不修改系统里的Python文件所有版本都静静躺在~/.pyenv/versions目录里想卸就卸想换就换不会对系统造成任何伤害。2. 环境准备依赖装齐后面少踩90%的坑2.1 先确认系统版本与CPU架构在统信UOS和麒麟Kylin OS上动手之前务必先看清自己机器的“底细”。打开终端执行cat /etc/os-release uname -m第一行命令会显示系统版本信息比如统信UOS 20或麒麟Kylin V10第二行显示CPU架构通常是x86_64也可能是aarch64飞腾、鲲鹏等平台。这个信息很重要因为后面安装依赖包时不同版本、不同架构的软件源可能会有差异我见过有同事在aarch64机器上照搬x86的安装命令结果报了一堆“无法定位软件包”的错误。2.2 安装pyenv所需的系统依赖包pyenv的核心功能其实是“下载Python源码并在本地编译”所以它依赖系统的编译工具链和各种开发库。这一步如果偷懒省略到了编译阶段就会疯狂报错。我的建议是直接一次性装齐省得分步排查sudo apt update sudo apt install -y git curl build-essential libssl-dev zlib1g-dev \ libbz2-dev libreadline-dev libsqlite3-dev libffi-dev liblzma-dev \ tk-dev libncurses5-dev libncursesw5-dev这里每个包都不是多余的我逐个说明它们的作用build-essential提供gcc、make等编译工具Python源码编译的必需品。libssl-dev提供OpenSSL开发头文件缺少它装好的Python连pip install访问HTTPS源都会失败。zlib1g-devzlib压缩库Python的zipfile、gzip等模块依赖它。libsqlite3-devSQLite开发库Python内置的sqlite3模块依赖它缺了之后Django等ORM框架会报No module named _sqlite3。libreadline-dev提供交互式命令行编辑支持没有它python交互式模式下按上下方向键会很痛苦。libffi-dev提供libffi库Python的ctypes模块、cffi包重度依赖它。liblzma-dev提供lzma压缩支持pandas、numpy等科学计算包的底层依赖。tk-devTKinter GUI工具包虽然日常开发用不到但有些库会隐式依赖。用一句话概括这些依赖包覆盖了Python标准库的编译选项。装齐它们编译出来的Python功能才完整否则后面缺啥补啥会非常崩溃。2.3 用git安装pyenv依赖装好之后接下来安装pyenv本体。推荐用git clone方式方便后续更新git clone https://github.com/pyenv/pyenv.git ~/.pyenv如果你所在环境访问GitHub较慢或者超时可以考虑使用国内镜像或者代理环境。安装完成后还需要配置shell环境变量。以我现在常用的bash为例echo export PYENV_ROOT$HOME/.pyenv ~/.bashrc echo export PATH$PYENV_ROOT/bin:$PATH ~/.bashrc echo eval $(pyenv init -) ~/.bashrc source ~/.bashrc如果你用的是zsh把~/.bashrc替换成~/.zshrc即可。配置完成后执行pyenv --version能看到版本号就说明安装成功。我在麒麟系统上配置完之后偶尔碰到pyenv: command not found十有八九是环境变量没生效——检查一下echo $PATH有没有包含~/.pyenv/bin以及当前终端是否重新source过配置文件。2.4 检查当前Python环境在真正开始安装新版本之前先做一次摸底which python python --version pyenv versions正常情况下which python指向的是系统自带的Python路径比如/usr/bin/python3pyenv versions只会显示system。这说明pyenv还没有接管Python命令。接下来我们就要开始安装实际要用的版本了。3. 用pyenv安装和管理Python版本3.1 查看有哪些版本可以安装执行下面命令就可以列出pyenv支持安装的所有Python版本pyenv install --list输出结果非常长因为它包含了CPython、Anaconda、PyPy、Miniconda等各类发行版。平时我们可以加个过滤只看CPython的特定版本pyenv install --list | grep 3\.12这个命令会列出所有3.12.x的小版本比如3.12.0、3.12.1一直到最新。我在写这篇文章时团队新项目用的就是3.12.7这个版本修复了不少已知问题跑主流Web框架都比较稳定。如果你维护的是老项目也可以在这个列表里找到3.6.8、3.8.10等历史版本pyenv对老版本的兼容性做得相当好。3.2 安装指定版本以Python 3.12.7为例确定版本之后直接执行pyenv install 3.12.7安装过程会经历下载源码、解压、配置编译选项、编译、安装五个阶段。第一次执行时输出会滚动很长一段日志期间你的终端看起来像卡住了其实它正在编译。总共耗时取决于机器性能一般x86的台式机8到12分钟aarch64的飞腾机器可能要15到20分钟期间不要关闭终端。如果编译过程中缺少依赖日志里会明确报错比如ERROR: The Python ssl extension was not compiled. Missing the OpenSSL lib?那就说明libssl-dev没装好。解决办法就是补装2.2节提到的依赖包然后执行pyenv uninstall 3.12.7清理掉半成品再重新安装。安装完成后执行pyenv versions可以看到* system (set by /root/.pyenv/version) 3.12.7当前版本还是system我们用pyenv global切换过去pyenv global 3.12.7 python --version此时python --version就会输出Python 3.12.7which python指向的路径变成了/root/.pyenv/shims/python。这代表pyenv已经正式接管了Python命令。3.3 global、local、shell三种作用域怎么选pyenv提供了三种版本设置方式它们的优先级从上到下递减命令作用范围优先级pyenv shell当前终端会话最高pyenv local当前目录及子目录中pyenv global整个用户环境低日常使用中我的建议是global只在第一次配置机器时用一次设成主力版本之后到每个项目目录里用pyenv local指定项目专属版本。local命令会在当前目录生成一个.python-version文件把这个文件提交到Git仓库后团队成员clone代码就能自动切换到对应版本这一点在信创环境的多机协作中特别实用。举个例子我现在同时维护两个项目cd ~/work/legacy_project pyenv local 3.6.8 python --version # Python 3.6.8 cd ~/work/new_service pyenv local 3.12.7 python --version # Python 3.12.7 cd ~ python --version # 回到global设置的3.12.7或者原来的system整个过程不需要反复安装卸载切换是即时的。我在给客户做环境演示的时候这个操作每次都能让人眼前一亮原来国产系统上也可以这么顺滑地切换Python版本。3.4 多版本切换的实战演示除了正常版本切换还有一类场景也值得注意同一个项目需要验证多个Python版本的兼容性。比如你的目标项目要同时支持Python 3.8和3.12传统做法是准备两台机器或者反复重装。用pyenv只需要两步pyenv install 3.8.18 cd ~/work/compat_test pyenv local 3.8.18 python -m pytest pyenv local 3.12.7 python -m pytest测试做完再把.python-version删掉即可。实测下来这种切换成本几乎为零它把“多版本兼容性测试”从一种痛苦变成了一种顺手就能做的事。这也是我坚持在所有国产化开发环境里首推pyenv的核心原因。4. pyenv-virtualenv版本切换还不够依赖隔离要跟上4.1 依赖冲突是怎么发生的版本切换解决的是“用哪个Python解释器”的问题但一个Python解释器下还共存着大量第三方包。如果所有项目都往同一个Python环境里pip install迟早会出乱子。我踩过一个很真实的坑项目A需要requests2.25项目B需要requests2.31两个项目切换着开发时每次都先卸载再重装requests有一天忘了重装项目B上线后请求接口就报SSL证书错误。这就是典型的“版本隔离”缺失。官方推荐的venv能解决一部分问题但配套pyenv用起来最顺手的是pyenv-virtualenv插件。4.2 安装pyenv-virtualenv插件pyenv本身提供了插件机制pyenv-virtualenv是官方推荐的虚拟环境插件。安装很简单还是在~/.pyenv目录下操作git clone https://github.com/pyenv/pyenv-virtualenv.git ~/.pyenv/plugins/pyenv-virtualenv然后往~/.bashrc里追加一行echo eval $(pyenv virtualenv-init -) ~/.bashrc source ~/.bashrc配置好之后重启一个新的终端窗口执行pyenv virtualenv --version能看到版本号就说明插件生效了。4.3 创建虚拟环境并关联项目利用虚拟环境先把一个干净的Python环境“拷”出来pyenv virtualenv 3.12.7 new_service_env这条命令会基于Python 3.12.7创建一个名为new_service_env的隔离环境。注意new_service_env就像一个独立的“小系统”你在里面装任何包都不会影响其他环境。查看一下当前有哪些虚拟环境pyenv virtualenvs输出会同时显示Python 3.12.7和基于它创建的new_service_env。现在有两种方式使用这个环境方式一手动激活pyenv activate new_service_env方式二设置当前目录的本地版本更推荐cd ~/work/new_service pyenv local new_service_env方式二的好处是它同样会生成.python-version文件记录内容为new_service_env以后进入这个目录就自动激活对应虚拟环境退出项目目录自动恢复完全不用手动操作。然后就可以愉快地安装依赖了pip install flask gunicorn requests pip freeze requirements.txt我的习惯是每个项目的第一件事就是创建对应的虚拟环境然后顺带生成requirements.txt。这样即使整台机器出问题重新搭建环境也就是pip install -r requirements.txt一条命令的事。4.4 多环境管理的小技巧随着项目越来越多虚拟环境也会越来越多我给自己的环境命名定了一套规则项目名_环境用途_日期比如data_platform_dev_20240115。这样在pyenv virtualenvs列表里一眼就能看出哪个环境是哪个项目的。另外虚拟环境建议定期清理。有些环境用了一次就再也没用过占着好几GB磁盘空间。清理命令是pyenv uninstall some_old_env清完之后再执行pyenv rehash刷新命令映射保证下次which python拿到的是最新路径。5. 常见问题与排查技巧实录5.1 编译阶段报错的应对清单在给不同机器装pyenv的过程中我积累了下面这些高频报错每一类都对应固定的解决办法报错关键词缺少的依赖解决方法Missing the OpenSSL liblibsslsudo apt install libssl-dev_zlib module build failedzlibsudo apt install zlib1g-devNo module named _sqlite3SQLitesudo apt install libsqlite3-devNo module named _ctypeslibffisudo apt install libffi-devNo module named readlinereadlinesudo apt install libreadline-devCompression module failedbzip2sudo apt install libbz2-dev我的排查步骤是固定的先根据报错关键词判断缺哪个库补装对应依赖然后pyenv uninstall对应版本再重新pyenv install。这里有一个容易忽略的点补装依赖之后必须重新安装Python版本因为编译是发生在安装阶段不是运行阶段。已经编译好的Python不会因为你后来装了系统库就自动补齐模块。5.2 切换版本不生效的排查最常遇到的三类问题是命令找不到、版本号没变、pip指向错乱。第一种pyenv: command not found。原因通常是~/.bashrc配置没有生效。排查顺序先cat ~/.bashrc | grep pyenv看看配置行是否存在再echo $PATH看看有没有包含/root/.pyenv/bin都没有问题就bash开一个新终端试一次。第二种python --version显示的还是旧版本。这可能是因为你在某个项目目录下执行过pyenv local而该目录的.python-version文件优先级高于global。执行cat .python-version看看内容如果不需要局部版本就rm .python-version删掉。还有可能是PYENV_VERSION环境变量被设置过直接unset PYENV_VERSION清理。第三种python版本切过去了但pip还是指向老版本。先确认pip确实来自~/.pyenv/shims目录然后执行pyenv rehash强制刷新一下命令映射。如果还不行检查是不是曾经用sudo pip装过包导致系统级的pip在shims之前被解析到了。5.3 安装速度慢的优化办法pyenv install默认从Python官网下载源码在部分网络环境下速度很不理想。我实测下来统信UOS和麒麟系统上最常见的是下载阶段卡住编译本身倒还好。优化办法有两个第一设置下载超时时间export PYENV_INSTALLER_DOWNLOAD_TIMEOUT600 pyenv install 3.12.7第二使用国内镜像源加速下载。pyenv支持从镜像站下载Python源码包把~/.pyenv目录下的源码下载地址指向国内镜像比如华为云、清华TUNA等开源镜像站。具体配置方法网上资料很多我这里不展开了。配置好之后下载速度能从几十KB/s跑到几MB/s整个安装过程会缩短一大半。5.4 使用安全与规范建议在日常使用pyenv时有几条红线我始终挂在嘴边第一永远不要用sudo去执行pyenv相关的命令否则会造成~/.pyenv目录权限混乱后续所有操作都会变得诡异第二不要试图用pyenv去覆盖或删除系统自带的Python系统的/usr/bin/python3是桌面环境和服务的基础动它等于自找麻烦第三pip install之前先确认当前处于哪个虚拟环境避免包装到错误的环境里而不自知。这三点看上去简单实际翻车的人非常多。6. 写在最后过了适应期你会回来感谢pyenv第一次在国产Linux平台上搭建pyenv环境确实比在普通Ubuntu上要多花一点心思主要原因是不同统信UOS、麒麟版本预装的软件源和依赖库有差异偶发的小问题需要自己排查。但跨过这道坎之后收益是长期且稳定的团队新成员入职clone项目后执行两行命令就能获得一致的开发环境项目需要升级Python大版本再也不用重装机器多个项目的依赖彻底隔离互相之间井水不犯河水。就我个人的实际使用感受而言pyenv和pyenv-virtualenv的组合是国产系统上管理Python多版本开发环境成本最低、最可控的方案。如果你刚开始接触建议拿一台不重要的开发机按这篇文章的步骤走一遍把Python 3.6、3.9、3.12各装一个然后随便建两个虚拟环境体验一下切换过程。等熟练之后你会发现这套方法不仅适用于统信UOS和麒麟Kylin OS放到任何Linux发行版上都是通用的一次学习长期受用。最后再分享一个小习惯把.python-version文件纳入版本管理并在项目README里写清楚要求的最低pyenv版本和Python版本这个细节能让你的项目在国产化机器上被任何人接手时都顺滑落地。
返回列表