为什么Ubuntu上的Python 3.8提示No module named '_ssl'
深入解析在Ubuntu系统上运行或编译Python 3.8时出现No module named '_ssl'的根本原因,涵盖缺少libssl-dev开发库与Ubuntu 22.04+下OpenSSL 3.0 API断代两大核心场景,并提供详实的排查命令与完整可落地的解决方案。
No module named '_ssl' 报错,核心原因是 Python 标准库底层的 C 扩展模块 _ssl.so 缺失或未成功构建。该扩展模块强依赖操作系统的 OpenSSL 开发头文件与动态库。绝大多数情况下,该问题源于两类具体场景:一是在通过源码编译或 pyenv 安装 Python 3.8 前,系统未预装 libssl-dev 开发依赖;二是在 Ubuntu 22.04 及以上较新的系统环境中,默认集成的 OpenSSL 3.0 与 Python 3.8 早期版本的源码构建逻辑发生 API 兼容性冲突,导致编译阶段静默放弃了 _ssl 扩展模块的生成。
一、故障表现与关键排查点
在 Ubuntu 上,该错误通常并不发生在基础语法的执行期间,而是在脚本或命令行尝试建立安全网络连接时突然暴露。最典型的高频触发点是在新安装的 Python 3.8 环境中使用 pip 安装第三方包,或者执行网络请求脚本:
python3.8 -m pip install requests
File "/usr/local/lib/python3.8/runpy.py", line 194, in _run_module_as_main
return _run_code(code, main_globals, None,
File "/usr/local/lib/python3.8/urllib/request.py", line 2589, in <module>
import ssl
File "/usr/local/lib/python3.8/ssl.py", line 98, in <module>
import _ssl #<-- 抛出核心异常
ModuleNotFoundError: No module named '_ssl'
这一报错具有迷惑性。很多使用者会误以为是 ssl 库需要通过 pip install ssl 进行单独安装,但尝试安装后会发现 pip 自身正是因为缺少 SSL 支持而完全无法连接 PyPI 服务器。
要理解这个错误,必须厘清 Python 标准库中 ssl 的实现架构:Python 的 ssl.py 只是一个上层的纯 Python 包装器,它内部所有加解密、证书校验与 TLS 握手操作,全部委托给底层的 C 语言扩展模块 _ssl。在 Linux 系统中,这个底层模块编译后的物理实体是名为 _ssl.cpython-38-x86_64-linux-gnu.so 的共享库文件,通常存放于 Python 安装路径的 lib-dynload/ 目录下。
# 检查当前 Python 3.8 的 lib-dynload 目录
ls -l /usr/local/lib/python3.8/lib-dynload/ | grep _ssl
如果执行该命令没有任何输出,说明 Python 在构建安装时根本没有生成 _ssl.so。此时 ssl.py 执行 import _ssl 时自然会触发 ModuleNotFoundError。
二、深层原因剖析:为什么缺少 _ssl.so
在 Ubuntu 上,官方软件源对每个发行版提供有默认的 Python 版本(例如 Ubuntu 20.04 默认为 Python 3.8.10)。通过官方源直接执行 sudo apt install python3 安装的 Python,其 _ssl 扩展模块是完备的。
出现 No module named '_ssl' 的机器,绝大多数属于以下情况之一:开发者自行下载源码包执行了 ./configure && make,或者通过 pyenv install 3.8.x、自定义 Docker 镜像构建脚本进行编译安装。导致 _ssl.so 缺失的具体原因有二:
原因一:CPython 源码编译机制的“容错陷阱”
当我们在 Linux 终端中运行 Python 源码的 ./configure 与 make 时,CPython 的构建体系(基于 Makefile 与 setup.py)将模块划分为“核心内置模块”与“可选扩展模块”。
_ssl 属于可选扩展模块。如果在编译开始之前,系统中没有安装 OpenSSL 的 C 语言头文件(openssl/ssl.h 等),Python 的构建脚本不会直接报错中断编译。相反,它会认为当前主机不需要或无法构建该特性,静默跳过 Modules/_ssl.c 的编译,仅在整个 make 流程的最末尾打印几行容易被滚屏忽略的提示信息:
The necessary bits to build these optional modules were not found:
_ssl _hashlib _tkinter
To find the necessary bits, look in setup.py in detect_modules() for the module's name.
随后执行 sudo make install 或 sudo make altinstall 时,安装程序依然会报告成功,并把没有 _ssl.so 的 Python 3.8 二进制文件完整写入系统路径。直到后续用户运行涉及 HTTPS 的代码,问题才彻底暴露。
原因二:Ubuntu 22.04+ 与 OpenSSL 3.0 的 API 兼容性断代
这是近年来在 Ubuntu 22.04 LTS (Jammy Jellyfish) 或 Ubuntu 24.04 LTS 上编译 Python 3.8 时最隐蔽、最普遍遇到的硬性阻塞。
Python 3.8 于 2019 年 10 月发布,其生命周期主要锚定在 OpenSSL 1.1.1 与 1.0.2 版本。而 Ubuntu 22.04 LTS 首次将系统全局默认的 OpenSSL 升级到了 OpenSSL 3.0 系列。OpenSSL 3.0 进行了大幅度重构,废弃了大量旧版宏定义、上下文操作函数以及不透明结构体直访机制。
因此,即便在 Ubuntu 22.04 上执行了 sudo apt install libssl-dev,安装的也是 OpenSSL 3.0 的开发库。当 Python 3.8(尤其是 3.8.0 至 3.8.12 等老版本)的 Modules/_ssl.c 尝试引入这些新头文件时,GCC 编译器会因函数原型不兼容、类型未定义等错误直接编译失败。构建系统检测到编译报错后,再次触发“容错机制”,将 _ssl 标记为构建失败并默默跳过。
三、精准修复方案与完整操作步骤
在修复前,先运行以下命令确认系统当前的 Ubuntu 发行版和默认 OpenSSL 版本号:
lsb_release -a
openssl version
根据输出结果,选用对应的解决方案。
方案 A:针对 Ubuntu 20.04 及更早版本(标准依赖补全)
如果系统返回的是 Ubuntu 20.04 或 18.04,说明系统内核提供的就是兼容的 OpenSSL 1.1.1。此时只需要安装全套编译构建依赖,然后重新配置编译即可:
# 1. 更新源并安装必需的编译依赖与 OpenSSL 头文件
sudo apt update
sudo apt install -y build-essential zlib1g-dev libncurses5-dev libgdbm-dev libnss3-dev libssl-dev libreadline-dev libffi-dev libsqlite3-dev wget libbz2-dev
# 2. 进入 Python 3.8 源码根目录
cd Python-3.8.18 # 以实际解压目录为准
# 3. 清理之前的错误构建缓存
make distclean
# 4. 重新执行配置,推荐开启优化选项
./configure --enable-optimizations
# 5. 编译并使用 altinstall 安装(切勿直接 make install 以免覆盖系统默认 python)
make -j$(nproc)
sudo make altinstall
方案 B:针对 Ubuntu 22.04 及更高版本(独立编译 OpenSSL 1.1.1)
如果系统是 Ubuntu 22.04 或 24.04,直接使用 apt install libssl-dev 得到的将是 OpenSSL 3.0,无法解决 Python 3.8 的编译问题。
绝对不要尝试降级或替换 Ubuntu 系统自带的 /usr/bin/openssl 和系统 libssl.so.3!Ubuntu 22.04+ 的包管理器 apt、系统核心服务 systemd 以及远程管理 ssh 均紧密绑定 OpenSSL 3.0。破坏全局 OpenSSL 会直接导致服务器无法联网、甚至重启后 SSH 服务崩溃断连。
正确的做法是:将 OpenSSL 1.1.1 源码下载并编译安装到独立的隔离路径(例如 /usr/local/openssl11),然后在编译 Python 3.8 时通过参数明确指定该独立路径。具体执行步骤如下:
# 第一步:准备基础编译工具链
sudo apt update
sudo apt install -y build-essential zlib1g-dev libffi-dev libsqlite3-dev wget
# 第二步:下载并编译 OpenSSL 1.1.1 到独立目录
cd /usr/local/src
sudo wget https://www.openssl.org/source/old/1.1.1/openssl-1.1.1w.tar.gz
sudo tar -zxvf openssl-1.1.1w.tar.gz
cd openssl-1.1.1w
# 配置安装前缀为 /usr/local/openssl11,启用动态链接库生成
sudo ./config --prefix=/usr/local/openssl11 --openssldir=/usr/local/openssl11 shared zlib
sudo make -j$(nproc)
sudo make install
# 第三步:确保动态链接器能够加载该独立库(配置软链接或 ld.so.conf)
echo "/usr/local/openssl11/lib" | sudo tee /etc/ld.so.conf.d/openssl11.conf
sudo ldconfig
# 第四步:进入 Python 3.8 源码目录,指定 OpenSSL 独立路径
cd /path/to/Python-3.8.18
make distclean
./configure --enable-optimizations --with-openssl=/usr/local/openssl11 --with-openssl-rpath=auto
# 第五步:多核编译与安装
make -j$(nproc)
sudo make altinstall
补充场景:通过 pyenv 管理 Python 3.8
如果你是使用 pyenv 安装 Python 3.8,在 Ubuntu 22.04+ 上直接执行 pyenv install 3.8.18 同样会遭遇该错误。解决机制完全一致,需要在执行安装命令前,通过环境变量把编译选项与上面编译好的 OpenSSL 1.1.1 目录链接起来:
# 为 pyenv 显式指定 OpenSSL 1.1.1 路径
CFLAGS="-I/usr/local/openssl11/include" LDFLAGS="-L/usr/local/openssl11/lib" pyenv install 3.8.18
四、验证修复成果与长效防坑清单
编译安装完成后,必须立即通过以下两项核心验证,确保 _ssl 扩展模块已被正确装载,且底层能够识别正确的 OpenSSL 库版本:
# 1. 验证 Python 3.8 是否能正常导入 ssl 并打印底层版本
python3.8 -c "import ssl; print(ssl.OPENSSL_VERSION)"
# 2. 验证 pip 的 HTTPS 通信能力
python3.8 -m pip --version
pip 23.3.1 from /usr/local/lib/python3.8/site-packages/pip (python 3.8)
当控制台能准确打印出 OpenSSL 1.1.1x 并且 pip 能够正常响应时,表明底层 C 扩展模块已完美对接,No module named '_ssl' 彻底解决。
总结与维护建议
在 Linux 系统的实际开发与运维中,Python 解释器与操作系统底层 C 库(如 OpenSSL、SQLite、FFI)存在强耦合关系。当遇到 No module named '_ssl' 时:
- 先查 Ubuntu 版本与 OpenSSL 基准:搞清楚是纯粹的缺少开发头文件,还是新旧版本 API 冲突;
- 区分系统软件源安装与源码自编:系统源安装的 Python 几乎不会遗漏该模块,自编或 pyenv 安装是重灾区;
- 严格遵守隔离原则:在 Ubuntu 22.04+ 等新系统上运行 Python 3.8 这类旧版本时,务必将 OpenSSL 1.1.1 约束在独立目录,使用
--with-openssl静态或 rpath 链接,既满足旧 Python 的编译要求,又确保整个操作系统的核心网络组件稳定安全。
免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。









