7/13/2026DevOps与工程化30 Views

Python 3.11 编译后缺少 _ssl 模块:从 OpenSSL 共享库到重新编译的完整修复记录

在一台使用 yum 管理软件包的 Linux 服务器上源码安装 Python 3.11.9,安装过程没有明显报错,但执行 SSL 测试时失败:

bash

报错内容:

text

这个问题比较容易误判。Python 可执行文件已经安装成功,python3.11 --version 也能正常输出,但这并不代表所有标准扩展都编译成功。_ssl 属于可选的 C 扩展,编译失败时,Python 主程序仍可能继续安装。

本文记录一次完整的排查和修复过程。

一、问题现象

源码安装 Python 的主要操作如下:

bash

第一次编译时,系统没有提前安装完整的开发依赖。后面补装了依赖包并重新编译:

bash

但重新安装 Python 后,import ssl 仍然失败。

为了绕过系统自带 OpenSSL 版本较旧的问题,又单独编译了 OpenSSL 1.1.1w:

bash

这里埋下了第一个问题。

二、根因分析

这次故障不是单一原因,而是几个问题叠加。

1. OpenSSL 被编译成静态库

no-shared 表示不生成共享库,只生成:

text

Python 的 _ssl 模块最终需要生成一个动态扩展文件:

text

在这种构建方式下,_ssl 很可能链接失败。即使 make altinstall 执行完成,安装出来的 Python 仍然可能没有 SSL 功能。

2. 动态链接器找不到自定义 OpenSSL

将 OpenSSL 改成共享库重新编译后,直接运行:

bash

又出现了新的错误:

text

这说明 libssl.so.1.1 已经存在,但系统动态链接器不知道它位于:

text

自定义软件安装到 /usr/local 的子目录后,不能只确认文件存在,还要处理运行时库路径。

3. Python 保留了旧的构建缓存

只执行 make clean 不一定能清除之前的检测结果。Python 源码目录中的 config.cachebuild 目录和旧 Makefile 都可能继续影响后续编译。

因此,重新编译前需要彻底清理。

三、修复方案选择

这里有两种处理方式。

方案一:使用系统 OpenSSL 开发包

适合系统自带 OpenSSL 版本满足 Python 3.11 要求的环境。

bash

然后直接重新配置 Python,不指定自定义 OpenSSL 路径。

优点是维护简单,系统负责动态库路径和安全更新。

缺点是老系统的 OpenSSL 版本可能不满足 Python 3.11 的编译要求。

方案二:独立安装 OpenSSL

适合 CentOS、RHEL 兼容旧版本系统,或者不能替换系统 OpenSSL 的服务器。

安装目录单独使用:

text

Python 通过编译参数和 RPATH 指向这个目录,不覆盖系统 OpenSSL。

本次采用第二种方案。

四、重新编译 OpenSSL 共享库

进入 OpenSSL 源码目录:

bash

清理旧的编译结果:

bash

重新配置。这里必须使用 shared,不能再使用 no-shared

bash

开始编译和安装:

bash

检查共享库:

bash

正常情况下可以看到:

text

五、注册 OpenSSL 动态库路径

先做一次临时测试:

bash

如果能够正常显示版本,说明 OpenSSL 编译没有问题,故障只在动态库搜索路径。

写入系统动态库配置:

bash

刷新缓存:

bash

确认系统已经识别:

bash

再次测试:

bash

此时命令应当可以正常执行。

六、彻底清理 Python 编译缓存

进入 Python 源码目录:

bash

清理旧构建结果:

bash

make distcleanmake clean 更彻底。遇到依赖库变化时,建议直接执行这一组命令,不要在旧构建结果上反复覆盖编译。

七、重新配置 Python

设置编译头文件、链接库和运行时库路径:

bash

重新执行配置:

bash

首次修复时不建议马上加 --enable-optimizations。先把 _ssl 编译问题解决,再考虑启用优化编译,这样排障更直接。

八、编译并检查 _ssl

开始编译,同时保存日志:

bash

检查 _ssl 编译记录:

bash

本次修复后,日志中出现了:

text

以及最终的链接命令:

text

同时可以看到 _ssl 链接到了自定义 OpenSSL:

text

这说明 _ssl 已经成功编译。

如果日志中出现下面的内容,则不能继续安装:

text

或者:

text

需要先检查 OpenSSL 头文件、共享库和链接参数。

九、安装 Python 3.11

确认编译没有问题后执行:

bash

使用 altinstall 可以避免覆盖系统默认的 python 命令。

安装完成后,明确使用完整路径验证:

bash

本次最终验证成功,ssl_ssl 均可正常导入。

十、检查实际链接的 OpenSSL

找到 _ssl 扩展:

bash

检查动态库依赖:

bash

预期结果应指向自定义目录:

text

这一步很重要。import ssl 成功只能说明当前能运行,ldd 才能确认它实际使用的是哪一套 OpenSSL。

十一、完整修复命令

下面这组命令适合在相同目录结构下直接参考。

重新编译 OpenSSL

bash

重新编译 Python

bash

最终验证

bash

十二、实际部署中的几个坑

不要覆盖系统 OpenSSL

不要把自定义的 libssl.so.1.1 直接复制到 /usr/lib64,也不要替换系统的 openssl 命令。

系统中的 yumcurlssh 和其他组件可能依赖原有 OpenSSL。直接覆盖系统库,修好 Python 的同时可能破坏系统工具。

自定义 OpenSSL 应安装到独立目录,再通过以下方式引用:

text

make altinstall 成功不代表扩展全部成功

Python 构建时,一些可选扩展失败不会让整个编译过程退出。

安装前应主动检查:

bash

除了 _ssl,还可以关注:

text

这些模块都依赖对应的开发包。

不要只执行 make clean

更换 OpenSSL 路径、版本或编译参数后,建议使用:

bash

这样可以避免旧检测结果继续生效。

验证时使用完整路径

服务器上可能存在多个 Python 3.11:

bash

测试时最好明确执行:

bash

否则可能一直在验证旧版本,造成已经修好但看起来仍然报错的假象。

十三、故障链路总结

本次问题的完整链路如下:

text

这类问题不难修,难点在于编译过程看起来是成功的。源码安装 Python 后,至少应检查一次 sslsqlite3bz2lzmareadline,避免服务上线后才发现标准模块缺失。

讨论 0

0 / 100
Supports **Bold**, `Code`
No comments yet.