
CentOS 7.9 上做 OpenSSL 升级和 Python3 编译安装绝对是每个运维都会经历的一次“渡劫”。今天聊聊这两个话题。业务要跑新协议Python 项目要求 3.8系统却死死抱着 OpenSSL 1.0.2k 和 Python 2.7不升级没法交差。但升级 OpenSSL 又不像装个 RPM 那么简单一个不小心yum 崩了、curl 废了、SSH 连不上生产环境直接给你脸色看。这篇文章把我实际踩过的坑、验证过的步骤、排错的思路完整写出来给同样在 CentOS 7.9 上挣扎的朋友一个可落地的参考。先说结论升级 OpenSSL 最忌讳直接覆盖系统自带的库正确做法是编译到独立目录让 Python3 这样的新应用去链接新版本系统原有组件继续用老版本。Python3 的安装也同样有讲究不是 ./configure、make、make install 三步就完事的依赖没装全、OpenSSL 路径没指对装出来的 Python 自带 ssl 模块直接不可用到时候 pip 装什么都报证书错误那才是真的折磨。1. 为什么 CentOS 7.9 的 OpenSSL 和 Python 版本让人头疼1.1 系统自带版本的“时代烙印”CentOS 7.9 是 2020 年发布的最后一个 7.x 版本内核 3.10自带的 OpenSSL 是 1.0.2k-fipsPython 是 2.7.5。说句公道话这套组合在当年是稳如老狗但放到现在确实有点跟不上了。很多现代软件要求 OpenSSL 1.1.1 甚至 3.0比如 Python 3.9 以后的 ssl 模块就明确要求 OpenSSL 1.1.1 或更高Python 3.10 开始更是推荐 OpenSSL 1.1.1g。如果你用 yum 装过一些带 Python3 的软件包大概率会遇到 “_ssl module missing” 这种提示根源就是编译 Python 时找不到新版本的 OpenSSL 库。而系统自带的 Python 2.7 更是尴尬Django 早就不支持了各种新库也只给 Python3 版本。所以在这个系统上装 Python3 算是刚需。但很多人会直接去 python.org 下载源码包在服务器上 ./configure make make install结果装完了发现 pip install 一个包就报 SSL 证书错误或者 import ssl 直接 ModuleNotFoundError。这不是操作流程错了而是缺了关键依赖和参数配置尤其是 OpenSSL 的路径没有指对。1.2 升级原则尽量不碰系统组件这里必须强调一条保命原则千万不要想着“升级 OpenSSL 就是把新版覆盖到系统原来的位置”。我见过有人直接在编译完 OpenSSL 1.1.1 后执行 make install把 /usr/bin/openssl 和 /usr/lib64/libssl.so 全替换成了新版结果 yum 马上报错curl 无法访问 httpsSSH 服务直接起不来因为系统里很多二进制文件是依赖 OpenSSL 1.0.2 的符号版本和 ABI 的新库不保证完全向后兼容。所以正确策略是“共存”新版本安装到 /usr/local/openssl系统老版本不动需要新版 OpenSSL 的应用比如 Python3在编译时通过参数明确指定新库路径命令行的 openssl 命令可以通过软链接或 PATH 环境变量切换但不影响系统底层库。这种做法的好处是风险隔离出了问题回滚也简单。2. OpenSSL 升级实操选版本、编译、避免炸系统2.1 版本选择与前期准备OpenSSL 的版本分支很多1.0.2、1.1.1、3.0、3.1、3.2 等。在 CentOS 7.9 上我推荐选择 1.1.1 系列的最后版本1.1.1w因为 3.x 系列在 CentOS 7.9 上虽然也能编译但是对系统库依赖更复杂而且 SSL 模块的兼容性有时候会出幺蛾子。如果一定要用 3.x建议 3.0.13 这种比较成熟的版本但不管选哪个都要去 OpenSSL 官网下载源码包别用不知名的镜像和二手打包。编译需要的基础环境先检查一遍yum install -y gcc make perl tar wget其中 perl 是 OpenSSL 编译脚本依赖的没有它会直接报perl not found。另外如果有 zlib 的库就安装 zlib-devel否则输出会提示无法压缩但这不是致命错误后面可以不用管。下载源码包的时候建议把包放到 /usr/local/src 目录下这个目录专门用来放自编译的软件源码便于集中管理。cd /usr/local/src wget https://www.openssl.org/source/openssl-1.1.1w.tar.gz tar -xzf openssl-1.1.1w.tar.gz cd openssl-1.1.1w2.2 编译安装到独立目录OpenSSL 的编译并不复杂但参数要注意。我这里用的命令是./config --prefix/usr/local/openssl --openssldir/usr/local/openssl shared zlib make -j4 make install解释一下几个关键点--prefix/usr/local/openssl指定安装目录这是避免污染系统库的核心。--openssldir/usr/local/openssl指定配置目录证书路径等会放在这里。shared表示生成动态库如果不加这个参数以后链接会非常麻烦。zlib启用压缩支持需要系统有 zlib-devel。make -j4用 4 核并行编译根据机器 CPU 核数调整比如-j8。并行编译太快不一定好内存小的机器反而会 OOM建议核数不超过内存 GB 数的一半。编译结束之后验证一下安装结果/usr/local/openssl/bin/openssl version应该显示OpenSSL 1.1.1w XX 2023之类的字符串。此时系统的/usr/bin/openssl还是老版本新的命令在独立目录里两者互不干扰。2.3 最容易被忽视的链接库坑编译完成只是第一步更大的坑在后面。OpenSSL 的动态库安装到了/usr/local/openssl/lib如果其他程序要链接这个新库必须让系统能找到它。两个办法第一写入/etc/ld.so.conf.d/openssl111.confecho /usr/local/openssl/lib /etc/ld.so.conf.d/openssl111.conf ldconfig第二如果你不想全局生效或者怕影响其他程序可以在编译具体应用时通过-L/usr/local/openssl/lib -I/usr/local/openssl/include单独指定。更推荐后者因为全局 ldconfig 可能会让系统的 curl 突然开始链接新版 OpenSSL 库虽然大部分情况没问题但生产环境不必要冒险。这里我遇到过一个奇怪的问题ldconfig 之后执行/usr/local/openssl/bin/openssl会报openssl: error while loading shared libraries: libssl.so.1.1: cannot open shared object file。原因是该命令默认去系统库路径找库系统没有 1.1 版本而/usr/local/openssl/lib还没被加载。解决办法就是先执行ldconfig或者临时设置LD_LIBRARY_PATH/usr/local/openssl/lib。但我不建议长期设置这个环境变量它会影响所有程序的链接行为很容易弄巧成拙。3. 升级报错定位我在 CentOS 7.9 上踩过的错误3.1 常见编译错误速查OpenSSL 编译报错多半集中在几个点上。我整理一下自己遇到的和网上高频出现的错误现象根因解决办法perl not found in PATH缺少 Perl 环境yum install -y perlzlib.h: No such file or directory缺少 zlib 开发包yum install -y zlib-develmake[1]: *** [crypto/include/internal/...] Error 1磁盘空间不足或源码目录权限问题检查/usr/local/src空间和目录属主fatal error: openssl/opensslv.h: No such file or directory编译其他程序时找不到 OpenSSL 头文件确认-I是否指向/usr/local/openssl/includeundefined reference to SSL_...链接时没找到新 OpenSSL 库或库顺序错误用-L/usr/local/openssl/lib -lssl -lcrypto注意顺序还有一个隐蔽问题如果系统之前装过其他版本的 OpenSSL 源码包或通过 RHEL 源安装过 openssl-devel可能会在/usr/local/include里残留头文件。编译时若头文件路径优先级不对会引用到旧头文件导致编译出来的程序行为诡异。所以我建议在编译前把/usr/local/include和/usr/local/lib里的旧 OpenSSL 痕迹清一清或者用./config的-I参数强制指定。3.2 实战排错案例编译 Python 时找不到新 OpenSSL我帮一个同事排查过这样的问题他编译 Python 3.9 时./configure检测时提示checking for openssl/ssl.h... no然后编译出来的 Python 没有 ssl 模块。后来发现openssl源码包编译时用的--prefix/usr/local/openssl但 Python 的./configure并没有指定这个目录导致 Python 的 setup.py 去系统默认路径找 OpenSSL 头文件找到的是 1.0.2k 老版本头文件版本不匹配干脆就跳过了 ssl 模块。正确的做法是给 Python 的./configure传参让它明确知道新 OpenSSL 的路径。具体见下文 Python 安装部分。这个案例想说明的是OpenSSL 装好了不等于就能被其他程序用必须显式告诉对方位置。3.3 如何让新老 OpenSSL 共存而不打架OpenSSL 共存的核心是管理好“库路径”和“头文件路径”。我习惯的做法老版本系统库不动新版本独立目录安装。命令路径通过软链接切换但只给特定用户或会话使用mkdir -p /usr/local/bin/pub ln -sf /usr/local/openssl/bin/openssl /usr/local/bin/pub/openssl export PATH/usr/local/bin/pub:$PATH这样在当前 shell 里敲 openssl 就是新版但系统服务的 PATH 不受影响。编译新程序时用CFLAGS和LDFLAGS明确指定新库目录避免依赖随机环境变量。在 CentOS 7.9 上只要不覆盖/usr/lib64/libssl.so.10这个老库文件yum 和 rpm 都能正常工作。如果某个 RPM 包需要 OpenSSL 1.1.1你可以用yum install openssl11部分第三方源里提供但那个版本比较特殊一般不推荐。4. Python3 版本安装与 OpenSSL 的无缝对接4.1 编译 Python3 前的一堆依赖很多人编译 Python 失败是因为依赖没装全。CentOS 7.9 的最小化安装缺很多东西。我在编译前会一次性把依赖装齐yum install -y gcc make zlib-devel libffi-devel readline-devel sqlite-devel bzip2-devel tk-devel这些依赖分别对应 Python 的不同模块zlib-devel是压缩模块libffi-devel是ctypes模块readline-devel是交互式命令行历史记录sqlite-devel是sqlite3模块bzip2-devel和tk-devel分别是压缩和 tkinter 界面。如果少了其中某一个Python 会在编译结束后悄悄缺少某些标准库等用到的时候才报ModuleNotFoundError那时候再补依赖重新编译就非常痛苦。另外libffi特别容易漏。Python 3.8 开始_ctypes是默认启用的很多包依赖它比如cryptography。如果你的环境没有libffi-devel编译时会出现ModuleNotFoundError: No module named _ctypes这在新手里出现频率极高。4.2 关键在 configure 参数Python 源码包下载和编译的流程基本固定cd /usr/local/src wget https://www.python.org/ftp/python/3.9.18/Python-3.9.18.tgz tar -xzf Python-3.9.18.tgz cd Python-3.9.18 ./configure --prefix/usr/local/python3 \ --with-openssl/usr/local/openssl \ --with-openssl-rpathauto \ --enable-shared make -j4 make install参数解释--with-openssl/usr/local/openssl告诉 Python 编译 ssl 模块时去哪里找 OpenSSL 的头文件和库。这是解决 ssl 模块缺失的关键。--with-openssl-rpathauto把 OpenSSL 库路径写进 Python 可执行文件的 rpath这样运行时不依赖LD_LIBRARY_PATH也能找到库。这个参数在 Python 3.10 及以后版本更好用3.9 也不影响。--enable-shared生成 libpython 共享库有些第三方模块需要同时也方便后续把 Python 嵌入其他应用。如果不加Python 会以静态方式编译问题也不大但为了兼容性建议加上。有人会问为什么不直接用yum install python3CentOS 7.9 的官方软件源里其实是有 Python 3.6.8 的但版本太老很多现代框架要求 3.8而且它自带的 OpenSSL 链接的还是系统老库。所以源码编译是通过“新版本”的唯一干净途径。编译完成后检查 Python 的 ssl 模块是否正常/usr/local/python3/bin/python3 -c import ssl; print(ssl.OPENSSL_VERSION)如果输出OpenSSL 1.1.1w XX 2023说明 Python 已经正确链接到新 OpenSSL。如果报ModuleNotFoundError: No module named _ssl那就是 configure 时没有检测到 OpenSSL需要回头检查路径。4.3 安装后的软链接与环境变量make install之后Python 被安装到/usr/local/python3不会覆盖系统的 python2。接下来要做的是把它接入 PATH但要注意别把系统默认python抢了。CentOS 7.9 的 yum 和系统管理工具依赖/usr/bin/python如果你把/usr/bin/python改成 python3yum 会瞬间崩溃。正确做法是只建立python3和pip3的链接ln -sf /usr/local/python3/bin/python3 /usr/bin/python3 ln -sf /usr/local/python3/bin/pip3 /usr/bin/pip3不要动/usr/bin/python。如果某些脚本里写的是#!/usr/bin/env python它们会继续使用 python2这样可以保命。同时把 Python3 的安装目录加入 PATH比如创建一个/etc/profile.d/python3.shexport PATH/usr/local/python3/bin:$PATH这样新登录的会话就能直接用python3和pip3。注意这里把路径放在前面确保python3命令优先找到新版本而python依旧是系统老版本互不干扰。5. 常见问题与排查技巧实录5.1 pip 与 SSL 证书问题的组合拳Python 装好后第一件事通常是升级 pip。但很多人一执行pip3 install --upgrade pip就报错错误大概是Could not fetch URL https://pypi.org/... There was a problem confirming the ssl certificate: HTTPSConnectionPool ... SSL: CERTIFICATE_VERIFY_FAILED。这个问题八成是因为 Python 的 ssl 模块没链接对 OpenSSL 的证书路径或者系统 CA 证书过旧。CentOS 7.9 自带 ca-certificates但如果不是最新版可能不认新版 pypi 的证书。解决方法yum update -y ca-certificates /usr/local/python3/bin/python3 -c import ssl; print(ssl.get_default_verify_paths())如果路径指向/etc/pki/tls/certs/ca-bundle.crt那就检查这个文件是否存在。有时候编译 OpenSSL 时设置的--openssldir/usr/local/openssl会导致 Python 去/usr/local/openssl/certs找 CA 证书那里是空的于是CERTIFICATE_VERIFY_FAILED。解决办法是做个软链接ln -sf /etc/pki/tls/certs/ca-bundle.crt /usr/local/openssl/certs/ca-bundle.crt或者把/etc/pki/tls/cert.pem链接过去。这一步很关键我之前在 CentOS 7.9 上装完 Python 后pip 一直报证书错误折腾了半天才发现是 CA 路径丢失。5.2 版本回滚方案万一升级过程中把系统搞坏了怎么回滚如果是按照上面的思路把新版装到独立目录回滚很简单把/etc/ld.so.conf.d/openssl111.conf删除ldconfig恢复把/usr/local/python3整个目录改名或删除把/usr/bin/python3软链接删掉。系统基本不会受影响。但如果你真的覆盖了系统库那回滚就麻烦了。我有一次在测试机上直接编译安装 OpenSSL 到默认路径yum 崩了连 curl 都不工作。当时手里没有备份只能从同版本 CentOS 机器上拷贝libssl.so.10和libcrypto.so.10回去然后用rpm -Vf验证。那真是一身冷汗。所以再次强调独立路径 软链接是运维保命的关键。5.3 我的个人使用心得最后分享几个我后来一直遵守的习惯。第一编译源码前先看README和INSTALLOpenSSL 和 Python 的文档里都写明了编译参数比网上任何二手教程都准确。我不否认搜索引擎很好用但很多教程默认环境是 Ubuntu/DebianCentOS 7.9 是另类很多参数对不上照着抄常常挂在第一步。第二每一次 make 都加上make -jN的 N 按机器负载来定而不是越大越好。我曾经在 2 核 4G 的机器上用-j8编译 Python结果编译器直接 OOMkilled 进程最后配置全乱。后来规规矩矩用-j2一次过。第三写一个简单的部署脚本把 OpenSSL 和 Python 的编译、安装、链接、验证步骤记下来。因为这种环境不是装一次就完事了后续新机器都要走一遍脚本能省掉大量记忆负担。脚本里特别注意不要设置全局LD_LIBRARY_PATH否则整个系统的库查找顺序会被打乱出问题会莫名其妙。我在实际运维中越来越觉得CentOS 7.9 虽然是老系统但用“源码安装 独立目录 软链接管理”的方式完全可以让它跑现代应用。关键是每一步都要理解“你为什么要这么做”而不是照着命令敲一遍。OpenSSL 和 Python 的坑绝大多数都是因为路径不对、依赖缺失、盲目覆盖造成的。只要把这两个基础问题解决掉后续安装任何 Python 包都会顺畅很多。希望这篇记录能帮你少走几个来回。