资讯详情

资讯详情

CentOS 7上用SCL安装Python 3.8,不碰系统Python 2.7

前阵子在一台CentOS 7服务器上部署新项目代码要求Python 3可系统还停留在Python 2.7时代。直接替换系统Python想想yum、firewalld、还有那一堆用#!/usr/bin/python写的老脚本我实在没这个胆量。后来用SCLSoftware Collections解决了问题不用动系统Python就能装上一个完整的Python 3.8还能和旧环境共存。这篇文章就把我实际操作的完整流程、SCL的环境变量机制、以及那些坑全部记录下来希望能帮到正被CentOS 7 Python版本问题卡住的人。1. 为什么CentOS 7上装Python 3这么麻烦SCL的设计初衷1.1 系统Python就是承重墙不能随便拆CentOS 7默认的Python版本是2.7.5但它不只是给开发者用的。yum、firewalld、setroubleshoot甚至一部分系统管理服务都直接调用/usr/bin/python。我在测试环境做过一次“大胆实验”把/usr/bin/python软链接改成Python 3结果执行yum repolist直接冒出一大片语法报错连systemctl也受到了牵连。这个教训告诉我在生产环境系统自带的Python 2.7就是承重墙看着碍眼但绝不能乱动。很多人觉得“我就装个Python 3不动系统Python不行吗”。问题是你一旦把/usr/local/bin加进PATH很多脚本会优先找到新Python系统的行为变得不可控。真正干净的方案不是“让某个Python赢”而是让不同版本完全隔离互不影响。SCL的出现就是为了解决这个问题。1.2 SCL到底是什么一套另起炉灶的软件集合SCL全称Software Collections是Red Hat主导的软件多版本共存机制。它的核心思路很简单把某个软件的一个版本完整安装到独立目录比如rh-python38就装在/opt/rh/rh-python38然后通过scl enable切换环境变量来使用整个过程不覆盖系统默认软件。你可以把系统Python 2.7想象成原来的厨房SCL是在旁边另搭了一个临时厨房。你要用哪个厨房就打开哪个的门用完了关门原来的厨房一切都还是老样子。这种设计在Red Hat系服务器上有长期维护记录比网上随便找的“编译安装脚本”让人放心得多。CentOS 7官方源里默认没有SCL需要先配置SCL仓库才能使用。1.3 和源码编译、pyenv相比SCL赢在可维护性当时我也考虑过直接源码编译Python到/usr/local但有几个痛点。第一configure参数、openssl版本、头文件依赖都要自己处理升级Python版本时还要重来一遍。第二编译安装的Python没有yum包管理时间一长就忘了当初装过哪些依赖和配置。第三系统PATH里同时存在多个Python很容易出现“pip装错环境”的诡异问题。pyenv虽然灵活但对root用户、对systemd服务、对多人共享的服务器来说不够系统化。SCL的优势在于安装、更新、卸载都用yum管理启用逻辑完全透明运维同事接手时看一眼/opt/rh下有哪些目录就明白服务器上装了哪些Python版本。对于企业和生产环境这种可维护性比性能上的那点差异重要得多。2. 动手前的仓库配置先把SCL源准备好2.1 安装centos-release-scl和scl-utils缺一不可在CentOS 7上第一步不是直接装Python而是先把SCL源准备好。执行两条命令yum install -y centos-release-scl yum install -y scl-utilscentos-release-scl会向/etc/yum.repos.d/写入SCL仓库文件scl-utils则提供scl命令本身。很多人卡在这一步是因为只装了前者然后运行scl命令时提示找不到。两个包是配套的缺一不可。如果你的系统是最小化安装可能还需要先装yum-utils这类基础工具但通常默认就有。2.2 验证仓库是否生效安装完可以先确认仓库状态yum repolist | grep -i scl正常会看到类似centos-sclo-rh/x86_64和centos-sclo-sclo/x86_64的仓库列表。rh仓库对应Red Hat官方维护的软件集合sclo仓库对应社区维护的集合。我这里用的是官方SCL源稳定优先。如果执行yum list available | grep rh-python能看到一堆rh-python*包就说明源已经正常。这一步看起来简单但我见过不少人在执行yum list available | grep rh-python时什么都没输出最后发现是CentOS-SCLo-scl.repo里的enabled0。仓库文件默认可能禁用安装完centos-release-scl务必检查一下repo文件里的配置。2.3 国内服务器替换镜像源的操作国内云服务器直接连官方SCL源速度通常不太理想尤其是一次性安装多个包的时候。建议换成国内镜像源。以阿里云为例编辑/etc/yum.repos.d/CentOS-SCLo-scl.repo和/etc/yum.repos.d/CentOS-SCLo-scl-rh.repo把baseurl指向对应的镜像地址[centos-sclo-rh] nameCentOS-7 - SCLo rh baseurlhttps://mirrors.aliyun.com/centos/7/sclo/x86_64/rh/ gpgcheck0 enabled1注意你本地的仓库文件名可能略有差异修改前先备份原文件。替换完成后执行yum clean all yum makecache这里有个小经验SCL仓库的gpgcheck可以设为0因为内网环境经常导致公钥导入失败如果公司安全要求严格则保留gpgcheck1并导入CentOS官方密钥。很多人把源换完就万事大吉结果yum报错找不到公钥其实就是gpgcheck没设置正确。2.4 完整命令汇总每次在新服务器上配置SCL我基本都跑这三条命令yum install -y centos-release-scl scl-utils yum repolist | grep -i scl yum list available | grep rh-python建议把这六行写成一个初始化脚本之后新机器直接执行。不是图省事而是确保每次环境一致。多次操作下来最大的感受是仓库配置这一步虽然简单但很多后续诡异问题都能追溯到“源没配好”。3. 用Yum安装rh-python38SCL包名的含义和安装位置3.1 版本选择rh-python36还是rh-python38SCL仓库中Python 3相关的包名是以rh-python开头后面跟大版本号。常见的有rh-python36、rh-python38、rh-python39。命名里有个容易混淆的点rh-python38不是代表“某个补丁版本38”而是对应Python 3.8系列。在实际项目里Python 3.6已经比较老官方维护也早过了生命周期Python 3.8在性能和第三方库兼容性上比较均衡所以我推荐选rh-python38。如果项目必须用3.9也可以选rh-python39安装和启用逻辑完全一样。我自己的经验是选版本时不要只追新要先看目标依赖库的兼容性比如某些老项目只支持到Python 3.6那么就算仓库里有3.8也别轻易换。3.2 安装命令和安装目录安装本身没什么悬念yum install -y rh-python38装完后整个环境都在/opt/rh/rh-python38/目录下。真正的可执行文件是/opt/rh/rh-python38/root/usr/bin/python3.8通常也提供python3这个链接。注意这里有一个让很多人不习惯的root子目录因为SCL模拟了一个独立根文件系统的结构所以每个集合的实际文件都挂在/opt/rh/集合名/root/下面。你用find /usr/bin -name python3.8大概率找不到就是这个原因。3.3 立即验证安装结果安装完成后可以直接用绝对路径验证/opt/rh/rh-python38/root/usr/bin/python3 --version能看到Python 3.8.x的输出。不过每次都用绝对路径太累更优雅的方式是进入SCL环境scl enable rh-python38 bash python3 --version这时python3指向的就是SCL的Python 3.8。另外我强烈建议顺手安装SCL对应的Python开发包后面编译扩展时一定能用上yum install -y rh-python38-python-devel很多人在这一步偷懒觉得“我又不写C扩展装什么devel”。等到pip安装lxml、pandas这类带二进制扩展的包时就开始四处找Python.h报错的解决方案了。4. scl enable的原理和几种常见用法4.1 环境变量切换的真相scl enable本质上是读取集合的enable脚本然后修改当前shell的环境变量。以rh-python38为例enable脚本位于/opt/rh/rh-python38/enable内容大致是往PATH前面添加/opt/rh/rh-python38/root/usr/bin往LD_LIBRARY_PATH添加对应的lib目录还会设置MANPATH等。所以你进入scl enable rh-python38 bash的新shell里运行which python3看到的路径已经变成了SCL的路径。这个环境只对当前shell有效退出后所有变量恢复。理解这个原理后面systemd、cron、脚本调度的问题就都能想通了。4.2 三种启用方式交互式、单命令、脚本内第一种交互式最常用scl enable rh-python38 bash适合在终端里临时用Python 3写点东西用完直接exit。第二种是单命令方式scl enable rh-python38 python3 -V适合在命令行一次性执行或者嵌入到自动化脚本里。第三种是在脚本内启用写法更直接#!/bin/bash source /opt/rh/rh-python38/enable python3 /data/app/main.py有人会问既然可以直接source enable为什么还要用scl因为scl还负责管理集合间的依赖比如某些集合需要先启用另一个集合scl能自动处理。日常使用中两者都行我更推荐在脚本里显式source因为别人看脚本的时候一眼就能明白Python3环境从哪来。4.3 让Python 3自动生效的配置如果你希望每次登录终端都自动进入Python 3环境不要直接去改系统/usr/bin/python而是可以把source /opt/rh/rh-python38/enable写进当前用户的~/.bashrc。如果希望所有登录用户都生效则在/etc/profile.d/下新建一个python38.shsource /opt/rh/rh-python38/enable注意这种方式只影响登录shell不影响systemd启动的服务进程。很多服务进程的运行环境不加载profile脚本所以如果你改了之后发现systemd服务里还是找不到python3那是正常现象需要走后面的绝对路径方案。5. 别动/usr/bin/python安全切换默认Python 3的方法5.1 血泪教训替换系统Python的后果网上有些教程会让你ln -sf python3 /usr/bin/python我劝你千万别在生产环境这么干。CentOS 7的系统工具和Python 2.7绑定得太深了/usr/bin/yum的shebang是#!/usr/bin/pythonfirewalld也依赖Python 2.7。一旦你把/usr/bin/python换成3.x轻则yum直接报语法错误重则系统服务异常。我自己在虚拟机里试过一次执行一条yum repolist就抛出一堆SyntaxError最后只能再切回来。安全切换默认Python 3的核心原则是/usr/bin/python和/usr/bin/python2永远保持2.7你想让用户敲python3时用Python 3那就让python3指向SCL环境。5.2 推荐方案source enable只影响当前登录用户对个人账号来说最安全的方式就是在~/.bashrc末尾加一行source /opt/rh/rh-python38/enable。这样每次打开终端python3自动是3.8而python仍然是2.7。系统服务、cron等不读你的bashrc所以受影响面只有你自己。如果你负责多台服务器想统一团队操作习惯可以在/etc/profile.d/下放一个脚本让所有登录用户默认启用SCL环境。这个方案的好处是彻底“旁路”对系统自带Python的环境零侵入卸载SCL时只要把profile脚本移除一切都回到原样。5.3 兜底方案软链接到/usr/local/bin如果不想每次打开shell都被SCL环境变量影响一个折中方案是把SCL的python3软链接到/usr/local/bin/python3pip3同理ln -s /opt/rh/rh-python38/root/usr/bin/python3 /usr/local/bin/python3 ln -s /opt/rh/rh-python38/root/usr/bin/pip3 /usr/local/bin/pip3因为/usr/local/bin通常已经在PATH里所以系统里任何用户执行python3都会用3.8。注意这里链接的是“python3”而不是“python”避免调用python时误入3.x。这样做的缺点是纯Python脚本没问题但如果某个扩展模块依赖SCL的LD_LIBRARY_PATH离开SCL环境可能import失败。5.4 两种方案对比方案影响范围对系统Python2.7影响适用场景~/.bashrc source当前用户无个人开发、运维日常操作/etc/profile.d source所有登录用户无多用户共享服务器/usr/local/bin软链接所有用户运行python3时无希望不加载SCL环境变量但又想用python3我的建议是优先用source方案因为它是SCL官方推荐的用法。软链接方案更像“走后门”日常跑简单脚本没毛病但遇到依赖动态库的复杂应用时问题会暴露得很突然。6. 配套pip、devel和虚拟环境从装包到跑通Django6.1 先升级pip、setuptools和wheel刚安装好的SCL Python 3.8自带pip但版本通常偏旧。我第一次装Django时遇到过pip install提示找不到合适版本最后发现是pip版本太老无法正确读取新版包索引。处理办法是进入SCL环境后先做一轮基础升级scl enable rh-python38 bash python3 -m pip install --upgrade pip setuptools wheel注意这里用的是python3 -m pip而不是直接敲pip。因为系统环境里可能同时存在Python 2.7的pip直接敲pip很容易装错环境。用python3 -m pip能确保pip和python3一定来自同一个环境。6.2 安装编译扩展前的devel准备SCL的Python要编译C扩展时主机环境必须能找到Python头文件和编译工具。只装rh-python38是不够的还需要开发包yum install -y gcc gcc-c make rh-python38-python-devel另外像lxml、pandas、cryptography这类库还依赖系统的openssl、libffi等库建议一并装上yum install -y openssl-devel libffi-devel zlib-devel bzip2-devel ncurses-devel sqlite-devel如果编译过程中报Python.h: No such file or directory绝大多数情况就是没装rh-python38-python-devel。这个报错很有迷惑性因为它看起来像是在说系统Python头文件缺失其实要装的正是SCL对应的devel包。6.3 使用venv隔离项目环境SCL环境本身是一个干净的Python 3环境但我不建议直接把项目的依赖全装进去因为多个项目容易互相冲突。更规范的做法是在SCL的Python 3环境中创建虚拟环境mkdir -p /data/app cd /data/app scl enable rh-python38 python3 -m venv venv source venv/bin/activate pip install --upgrade pipvenv创建时的基础解释器来自SCL之后所有依赖都在venv里不影响SCL基础环境。对于生产部署可以把venv路径和项目代码放一起打包迁移时也方便。这个习惯帮我避免过很多次依赖混乱尤其是同一台服务器上跑多个项目时虚拟环境几乎是刚需。6.4 实际跑一个Django服务用上面的venv装Django并验证pip install django django-admin startproject myproject cd myproject python3 manage.py migrate python3 manage.py runserver 0.0.0.0:8000如果服务器有防火墙记得执行firewall-cmd --add-port8000/tcp --permanent firewall-cmd --reload这一步和SCL没有直接关系但很多人跑通Django后发现外部访问不了十有八九就是忘了放行端口或云安全组规则。整个过程跑下来你应该能感受到SCL只是提供了一个可用的Python 3运行环境真正管理项目依赖的还是venv和pip。7. 我踩过的七个SCL相关的坑7.1 scl命令找不到最早我在一台最小化安装的CentOS 7上执行scl enable直接提示bash: scl: command not found。原因很简单只装了centos-release-scl或者只装了SCL集合本身但没装scl-utils。解决方式yum install -y scl-utils这个坑很基础但在排查时容易忽略。如果你确认装了还是找不到检查一下which scl看是不是PATH里没有/usr/bin不过这种情况比较少。7.2 scl enable后python3版本没变有同事反馈进入scl enable rh-python38 bash后python3 -V还是2.7。我让他执行echo $PATH发现/opt/rh/rh-python38/root/usr/bin并没有出现在PATH最前面。后来查明是他在~/.bashrc里自定义过PATH把/usr/local/bin放在前面覆盖了SCL的enable脚本。解决办法很简单进入SCL环境后临时强制指定export PATH/opt/rh/rh-python38/root/usr/bin:$PATH或者把自定义PATH的语句放到source /opt/rh/rh-python38/enable之后再执行。归根结底SCL启用靠的就是PATH顺序排查这类问题第一件事永远是看环境变量。7.3 systemd服务里找不到python3把Python 3写在systemd的ExecStart里启动时常会遇到“Executable not found”或者找不到模块。原因是systemd启动进程时只继承极简环境不读/etc/profile.d和用户bashrc。解决不要在ExecStart里写裸的python3而是写绝对路径ExecStart/opt/rh/rh-python38/root/usr/bin/python3 /data/app/run.py如果脚本依赖SCL动态库建议写一个wrapper启动脚本在脚本里先source /opt/rh/rh-python38/enable再运行Python。注意systemd的ExecStart默认路径里并不包含/opt/rh所以绝对路径是最省心的做法。7.4 cron里调度Python脚本失败cron任务的环境比systemd更简陋PATH通常只有/usr/bin:/bin而且不加载profile文件。你手工在终端能跑的脚本放到crontab里很可能直接报“python3: command not found”。解决方式有两种要么在crontab里写绝对路径调用0 2 * * * /opt/rh/rh-python38/root/usr/bin/python3 /data/scripts/daily.py /var/log/daily.log 21要么在脚本开头source enable#!/bin/bash source /opt/rh/rh-python38/enable python3 /data/scripts/daily.py我建议后者因为脚本里如果有多个Python调用都能正确继承环境。只在crontab里写绝对路径的话一旦脚本内部再去调用另一个python3命令还是可能找不到。7.5 pip安装的包和系统Python混淆在没有启用SCL环境的终端里pip可能指向Python 2.7的pippip3也可能来自其他软件。如果你直接执行pip install requests装完在SCL的Python 3里import requests可能依然找不到。这不是SCL的问题是pip指向乱了。最可靠的验证方式在SCL环境里执行python3 -m pip --version确认显示的路径是/opt/rh/rh-python38/root/usr/bin/python3。之后所有安装都使用python3 -m pip install不要用裸pip。这个习惯能避免大量“装完包却找不到”的问题。7.6 编译包时找不到Python.h我装lxml的时候遇到过下面的错误fatal error: Python.h: No such file or directory报错的是gcc但其实原因是缺少Python开发头文件。对SCL来说必须安装对应集合的devel包rh-python38-python-devel。装完后再编译就通过了。另外有些包还需要pkg-config和libffi-devel建议先补齐基础依赖再重试。Python.h这个坑特别容易让人误以为是系统Python头文件问题实际上SCL环境下的每个Python集合都有自己的devel包必须精确匹配。7.7 yum升级时提示SCL包冲突由于SCL集合是独立的一般不会和系统包冲突但个别情况会因为手动改了软链接导致yum认为文件冲突。比如你把SCL的python3链接到了/usr/bin/python3而某个系统包也提供了/usr/bin/python3文件yum就会警告“file conflicts”。遇到这种情况先把自定义软链接移走等yum操作完成后再恢复。处理原则是系统目录里不要轻易塞SCL的文件除非你非常清楚自己在做什么。我见过有人在/usr/bin/python3上做了软链接结果yum更新python包时直接报错排查半天才发现是软链接占用了系统包路径。8. 写在最后从SCL到更高版本Python的过渡思路SCL在CentOS 7上能装到的Python 3版本其实有上限主要集中在3.6到3.9这个区间。如果你项目非要使用3.10、3.11甚至3.12的新特性SCL仓库未必能及时跟进。此时替代方案一般有三条路源码编译安装到独立目录、用conda管理、或者干脆迁移到更新版本的操作系统。我个人在旧系统上还是优先SCL因为它能用yum维护出了问题也容易卸载这对生产环境太重要了。如果你决定源码编译新的Python版本请务必记住不要覆盖系统Python 2.7可以编译到/usr/local/python3.11这种独立目录然后单独软链接python3.11来使用。但这样你就需要自己处理pip、ssl证书、依赖库等一堆问题。相比之下SCL虽然版本保守一点但稳定性和可维护性确实高出不少。最后再分享一个我的小习惯无论用SCL还是源码编译我都会在项目目录下建一个说明文件或者在README里标注清楚这个项目到底需要哪个Python解释器同事接手时就不会因为版本不同而踩坑。对于CentOS 7我的建议是能用SCL就用SCL别为了追新版本把系统搞得乱七八糟。老系统能安安稳稳跑Python 3比什么都重要。
觉得有用,分享给同行:

为您的企业打造数字门面

稳重轻奢商务风格,端正雅致视觉,长效耐看不易过时。

立即咨询 →