看到libcrypto.so.10(OPENSSL_1.0.2)(64bit) is needed by erlang-22.0.7-1.el7.x86_64这行报错,很多人的第一反应是系统里没装 OpenSSL,然后去装了一遍openssl,发现版本明明比 1.0.2 还高,结果依然过不去。这个坑我踩过不止一次,折腾到最后才搞明白:这行报错真正在问的不是"你装了 OpenSSL 没",而是"你系统里有没有一个带 OPENSSL_1.0.2 符号版本的 libcrypto.so.10,以及 RPM 的依赖解析能不能找到它"。
这篇文章我把这个报错从原理到排查再到几种实际可行的解法完整拆一遍。不管你是还在 CentOS 7 上维护老 RabbitMQ 集群,还是不小心把 el7 的 Erlang 包装到了 CentOS Stream 9 上,看完基本都能定位到问题,然后选一条自己能走通的路。
1. 先看懂报错的三层含义:SONAME、符号版本和 RPM 依赖解析
1.1 libcrypto.so.10 里的 ".10",本质是 ELF 的 SONAME
libcrypto.so.10并不是一个真实的物理文件名。在 CentOS 7 上,OpenSSL 1.0.2 系列实际安装的文件通常叫/usr/lib64/libcrypto.so.1.0.2k。而.so.10这个名称,是编译 OpenSSL 时写入 ELF 文件头里的DT_SONAME字段。
你可以用下面命令验证:
readelf -d /usr/lib64/libcrypto.so.10 | grep SONAME输出会是:
0x000000000000000e (SONAME) Library soname: [libcrypto.so.10]用生活类比理解:libcrypto.so.1.0.2k是人的身份证全名,libcrypto.so.10是大家叫惯的绰号。程序在编译时记录的不是全名,而是这个绰号;运行时动态加载器拿着绰号去/etc/ld.so.cache里找对应文件。这就是为什么你/usr/lib64/下明明有一个带 OpenSSL 1.1.1 的 libcrypto,却不能满足老程序的加载需求——因为它不叫libcrypto.so.10。
RPM 在做依赖检查时,也会检查系统里有没有提供libcrypto.so.10这个 SONAME 的文件,或者有没有某个 RPM 包在自己的Provides字段里声明了这个能力。如果系统里只有libcrypto.so.3(OpenSSL 3.0 的 SONAME),那这一项就直接判定为不满足。
1.2 OPENSSL_1.0.2 是符号版本,不是文件名
比 SONAME 更绕的一层是括号里的OPENSSL_1.0.2。这是 ELF 的符号版本机制(Symbol Versioning),它不是指"文件名里带 1.0.2",而是指这个.so文件在导出函数符号时,给符号打上的版本标签。
OpenSSL 1.0.2 编译出来的libcrypto.so.10,内部会导出一组带版本标签的符号:
objdump -T /usr/lib64/libcrypto.so.10 | grep OPENSSL_1.0.2 | head你会看到类似:
000000000035a240 g DF .text 00000000000000d0 OPENSSL_1.0.2 BIO_new 000000000035a280 g DF .text 0000000000000030 OPENSSL_1.0.2 BIO_free 000000000035a540 g DF .text 0000000000000020 OPENSSL_1.0.2 OPENSSL_cleanseOPENSSL_1.0.2就是这套符号版本标签中的一个集合名。程序链接 OpenSSL 并使用某个符号后,动态链接器不仅要求运行时能找到libcrypto.so.10,还要求该库必须导出对应符号的OPENSSL_1.0.2版本,否则程序即使启动了,也随时可能因为找不到符号版本而崩溃。
这就能解释一个很常见的怪象:有人从某个老系统里手工拷了一个libcrypto.so.10放到/usr/lib64/,文件存在了,但 RPM 依然报缺少libcrypto.so.10(OPENSSL_1.0.2)(64bit)。原因很简单——拷过来的库符号版本标签不完整,或者根本不带OPENSSL_1.0.2这个版本集合。
1.3 RPM 依赖是"包提供能力"的匹配过程
RPM 的依赖解析不是简单做文件比对,而是基于包的Requires和Provides元数据。你在安装 erlang-22.0.7 时看到的报错,完整形态是这样的:
错误:软件包:erlang-22.0.7-1.el7.x86_64(/erlang-22.0.7-1.el7.x86_64) 需要:libcrypto.so.10(OPENSSL_1.0.2)(64bit)libcrypto.so.10(OPENSSL_1.0.2)(64bit)是 erlang 这个 RPM 的Requires声明。yum 在解决依赖时,会去所有已启用仓库中寻找哪个包的Provides能覆盖这一项。在 CentOS 7 里,提供这项能力的包是openssl-libs。
rpm -q --provides openssl-libs | grep libcrypto.so.10正常输出:
libcrypto.so.10()(64bit) libcrypto.so.10(OPENSSL_1.0.2)(64bit) libcrypto.so.10(OPENSSL_1.0.2_EC)(64bit)注意:openssl-libs的版本必须是 1.0.2k 这一代,如果系统里装的是从 CentOS Stream 9 那边移植过来的 OpenSSL 3.x,它Provides的是libcrypto.so.3,跟libcrypto.so.10没有任何关系,报错就必然出现了。
所以这个报错本质上是在告诉你:RP M 解析器在当前的系统环境和仓库组合里,找不到一个能提供libcrypto.so.10(OPENSSL_1.0.2)(64bit)的包。至于为什么找不到,往下看。
2. 三种最容易踩出这个报错的场景自查
2.1 CentOS 7 已 EOL,yum 源失效导致依赖链断裂
这是 2024 年之后最容易踩的场景。CentOS 7 在 2024 年 6 月 30 日正式停止维护,官方源整体从mirror.centos.org挪到了vault.centos.org。如果你机器的/etc/yum.repos.d/CentOS-Base.repo里还写着老地址,执行yum install时极大概率伴随这一串东西:
cannot find a valid baseurl for repo: base/7/x86_64 cannot find a valid baseurl for repo: centos-sclo-rh/x86_64base/7/x86_64就是 CentOS 7 的基础源,centos-sclo-rh/x86_64是老的 Software Collections 库,同样只面向 CentOS 7。这些源一旦失效,yum 连 repo 元数据都拉不下来,依赖解析自然不可能成功。你看到的"缺少 libcrypto.so.10"很多时候只是暴露出来的表面错误,真正的病根是仓库列表里没有任何一个可用 base 源。
自查命令:
yum repolist如果提示 "Cannot retrieve metalink for repository" 或者屏幕上直接出现 cannot find a valid baseurl,那基本可以确定是源的问题。
2.2 系统 OpenSSL 被替换/升级,libcrypto.so.10 整个消失
这种情况多发生在有人手动编译安装过高版本 OpenSSL,或者装过某些第三方源里自带的 openssl 包。系统里openssl version输出是高版本,但底层的openssl-libs已经被动过手脚。
自查命令:
rpm -qa | grep openssl ldconfig -p | grep libcrypto如果看到:
libcrypto.so.3 (libc6,x86-64) => /usr/lib64/libcrypto.so.3而完全没有libcrypto.so.10,说明系统的 OpenSSL 主流已经切换到 3.x 时代。此时即便你恢复了 yum 源,依然要去仓库里把匹配 CentOS 7 的openssl-libs拉回来,或者用兼容包把缺口补上。
还有一种隐蔽情况:机器上确实存在/usr/lib64/libcrypto.so.10,但它来自某次手工tar解包或非标准安装,RPM 数据库里没有任何包登记Provides: libcrypto.so.10(OPENSSL_1.0.2)(64bit)。这种情况 yum 一样会报错,因为它认的是 RPM 元数据,而不是裸文件。
2.3 把 el7 的 rpm 装到了 CentOS Stream 8/9 上
这个场景最近特别常见。很多人搜到 erlang RPM 下载页,看到链接里带el7就下了,也不看自己系统是什么。CentOS Stream 9 自带的是 OpenSSL 3.0,/usr/lib64/里只有libcrypto.so.3,用yum install ./erlang-22.0.7-1.el7.x86_64.rpm安装时第一关就死在这个依赖上。
而且这类问题的报错会非常有迷惑性——它明确告诉你缺的是OPENSSL_1.0.2,容易让新手以为装个 OpenSSL 1.0.2 兼容库就行。但 CentOS Stream 9 的软件包生态整体基于 RHEL 9,强行往里面塞 el7 的依赖,轻则依赖冲突,重则把系统的 openssl 相关组件搅成一锅粥。
自查命令:
cat /etc/os-release看一眼VERSION_ID,再回头看你下载的 Erlang 包名字里是el7、el8还是el9。如果系统是 Stream 9,包名却是el7,那这就是当前报错的直接原因。
3. 老集群救法:CentOS 7 上恢复可用环境并装对 Erlang
如果你确实还在 CentOS 7 上维护老业务(尤其是跑着 RabbitMQ 3.8 这类需要 Erlang 22 的环境),目标不是升级 OpenSSL,而是把系统环境恢复到一个"el7 包能正常装"的状态。
3.1 第一步:把源切到 vault.centos.org
CentOS 7 的官方归档地址是https://vault.centos.org/7.9.2009/。修改/etc/yum.repos.d/CentOS-Base.repo时,核心是把mirrorlist相关配置全部注释掉,改成baseurl指向 vault,下面是一个经过验证的最小可用配置:
[base] name=CentOS-7 - Base baseurl=https://vault.centos.org/7.9.2009/os/x86_64/ enabled=1 gpgcheck=1 gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7 [updates] name=CentOS-7 - Updates baseurl=https://vault.centos.org/7.9.2009/updates/x86_64/ enabled=1 gpgcheck=1 gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7 [extras] name=CentOS-7 - Extras baseurl=https://vault.centos.org/7.9.2009/extras/x86_64/ enabled=1 gpgcheck=1 gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7改完后执行:
yum clean all yum makecachemakecache能正常拉取元数据,说明源已经可用。如果机器在国内,访问 vault 速度不理想,也可以用阿里云的归档镜像:
baseurl=https://mirrors.aliyun.com/centos-vault/7.9.2009/os/x86_64/注意:不要再把源指到清华 tuna 或阿里云上那些centos-stream/9-stream的目录,那是 CentOS Stream 9 的镜像。CentOS 7 机器用了 Stream 9 的仓库,依赖解析会直接乱套,libcrypto.so.10这种东西在 Stream 9 仓库里不可能存在。
3.2 第二步:确认 openssl-libs 能否满足依赖
源恢复后,先确认当前openssl-libs的情况:
rpm -q openssl-libs如果返回package openssl-libs is not installed,直接装:
yum install openssl-libs装好后再次验证:
rpm -q --provides openssl-libs | grep 'libcrypto.so.10(OPENSSL_1.0.2)'能看到输出,说明依赖缺口已经补上。如果yum install openssl-libs报错,先排查是不是还有别的失效仓库,比如centos-sclo-rh:
yum repolist --verbose | grep -B2 -A2 'centos-sclo'老架构的 SCL 仓库已经没人维护,建议直接把/etc/yum.repos.d/下对应的 repo 文件禁用或删除,不要让它干扰依赖解析。对绝大多数装 Erlang 的场景,SCL 里的软件用不到。
3.3 第三步:用 yum install ./rpm 或 Erlang Solutions 源安装
这一步是最容易翻车的环节。很多人下载 Erlang RPM 后直接rpm -ivh erlang-xxx.rpm,结果报出一个依赖错误,又手动去下一个依赖,陷入依赖地狱。正确操作是让 yum 替你处理依赖:
yum install ./erlang-22.0.7-1.el7.x86_64.rpmyum install ./xxx.rpm和rpm -ivh xxx.rpm的区别在于:yum 会把这个本地包纳入依赖解析流程,如果它还需要别的包而仓库里有,yum 会一并安装;rpm 则不会去查任何仓库。
如果你更希望走官方源,可以用 Erlang Solutions 为 CentOS 7 提供的 rpm 源:
wget https://binaries2.erlang-solutions.com/rpm/centos/erlang-solutions-2.0-1.noarch.rpm rpm -ivh erlang-solutions-2.0-1.noarch.rpm yum install erlang在源和依赖都正常的情况下,这条路最省事,装出来的 Erlang 直接和系统 OpenSSL 1.0.2 对接好。
提示:如果你的原始需求是给 RabbitMQ 用,更推荐从 rabbitmq/erlang-rpm Releases 下载和 RabbitMQ 版本匹配的 Erlang 包,选
el7后缀那个,然后同样用yum install ./方式装。RabbitMQ 官方维护的 Erlang 包在兼容性上更稳。
3.4 备选:源码编译 OTP 22
如果必须锁死在 erlang 22.0.7 这个版本,又不想被第三方包装包方式绑定,源码编译是保底方案。在 CentOS 7 上编译 OTP 22 需要先准备好编译器工具链和依赖:
yum groupinstall "Development Tools" yum install ncurses-devel openssl-devel unixODBC-devel下载OTP-22.0.7源码后:
tar -xf otp_src_22.0.7.tar.gz cd otp_src_22.0.7 ./configure --prefix=/usr/local/erlang --with-ssl=/usr make -j$(nproc) make install--with-ssl=/usr是为了让它明确找到 CentOS 7 上 OpenSSL 1.0.2 的头文件和库文件。编译完成后把/usr/local/erlang/bin加入 PATH:
echo 'export PATH=/usr/local/erlang/bin:$PATH' >> /etc/profile.d/erlang.sh source /etc/profile.d/erlang.sh erl -version源码编译最大的好处是绕开了 RPM 依赖层的所有检查,只要你机器上编译时能链接到 OpenSSL 1.0.2,产出的 Erlang 运行起来就没有libcrypto.so.10的问题。缺点是后续维护更新要靠自己,不像 RPM 包装的那么好升级。
4. 新系统该怎么办:配对的发行版包比硬刚依赖更重要
4.1 先看 /etc/os-release,再挑包的后缀
如果你机器是 CentOS Stream 9 或者 RHEL 9,那刚才所有围绕"恢复 CentOS 7 环境"的操作都不适用。你应该做的是停止尝试安装 el7 的 Erlang RPM,而是去下载对应 el9 版本的包。
判断系统版本:
cat /etc/os-release只要看到VERSION_ID="9",后面下载包时就要认准el9后缀。el8 的包原则上也比 el7 的可接受度高一些,但最稳妥的还是和系统大版本保持一致。
用 RabbitMQ 官方 erlang-rpm 仓库举例,下载地址里的文件名长这样:
erlang-26.2.5-1.el9.x86_64.rpm安装命令不变:
yum install ./erlang-26.2.5-1.el9.x86_64.rpmyum 会从当前系统仓库中找到匹配的 OpenSSL 3.x 依赖,装完就完事。
4.2 用 RabbitMQ 官方 erlang-rpm 里的 el9 包
之前有朋友问:为什么我直接用yum install erlang在 CentOS Stream 9 上也能装,但装出来的版本很老?因为 CentOS Stream 9 的 AppStream 仓库里 Erlang 版本往往偏向较新 OTP,如果你需要的是和某个业务框架严格匹配的 Erlang 版本,用发行版自带包反而不合适。
RabbitMQ 官方 erlang-rpm 仓库里对不同系统版本维护了多套构建产物,它至少覆盖 el7、el8、el9。需要什么 Erlang 版本就去 Releases 页找对应的 tag。比如 Erlang 26.2.5 的 release 页面里,el9 和 el7 的包是分开放的,下载时看清楚。
这种集中维护的仓库还有一个好处:它的包在构建时已经验证过和同一发行版上的 OpenSSL 能正常配合,不会出现"装完 erlang 后发现 crypto 应用起不来"的尴尬。相比之下,个人在不知名源里打包的 Erlang 就不好说了。
4.3 由 Erlang 版本反查业务:你现在到底为啥要 22.0.7
这里想多说一句和 OpenSSL 无关,但实际工作中经常卡住人的事:很多时候安装 Erlang 22 只是因为它和 RabbitMQ 3.8 是搭好的组合。如果项目允许,在新系统上更合理的做法是同步升级 RabbitMQ 到 3.12/3.13 或 4.x,然后直接使用 Erlang 26/27。
原因是 Erlang 22 是 2019 年的版本,OpenSSL 1.0.2 也已经 EOL 很久。你为了一个老依赖去整体维持一套 EOL 环境,后面遇到的安全漏洞和兼容问题会源源不断。给一个粗略的对照参考:
| 服务版本 | 常用 Erlang/OTP 版本 | 说明 |
|---|---|---|
| RabbitMQ 3.8.x | 22.3.x | 老集群常见,需要 el7 时代 OpenSSL 1.0.2/1.1.x |
| RabbitMQ 3.11.x | 24.x / 25.x | 已经支持 OpenSSL 3.0 |
| RabbitMQ 3.12.x | 25.x / 26.x | 新系统上比较稳的搭配 |
| RabbitMQ 4.x | 26.x / 27.x | 新项目推荐,直接走 el9 包 |
如果最后还是决定用 Erlang 22,那就接受它只能在 CentOS 7 类环境下好用的现实;如果用新系统,趁早选新 Erlang 版本,而不是想着让新系统去兼容 2019 年的动态库命名。
5. --nodeps 等"歪门邪道"的实际后果与仅有的救急场景
遇到这类依赖报错,总有同学会想到跳过依赖检查。我可以明确说:rpm -ivh --nodeps和yum install --nodeps在这些场景下大概率会留下一个"看起来装了,实际跑不动"的 Erlang。
5.1 用 --nodeps 装上去,第一关就挂在 crypto/ssl 应用
Erlang 的crypto和ssl两个标准库是通过 NIF 直接加载 OpenSSL 动态库的。系统里没有libcrypto.so.10时,即使你强制装上 Erlang RPM,启动erl后执行:
application:start(crypto).大概率得到:
{error,{not_started,crypto}}或者更直接的:
Failed to load NIF: libcrypto.so.10: cannot open shared object fileerl本身能进 shell,但那完全是表象。RabbitMQ 启动时如果依赖 crypto/ssl 应用,会在初始化阶段直接崩掉,日志里又是一长串 NIF 加载错误。
还有一个隐藏问题:--nodeps装的包在 RPM 数据库里处于"依赖不完整"状态,之后你执行任何yum update、yum remove,都可能因为它引发连锁检查失败,反而把系统包管理状态搞脏。
5.2 手工把 libcrypto.so.10 拷进 /usr/lib64 的连锁反应
更不建议的做法是从别的机器上把libcrypto.so.10直接scp到/usr/lib64/。前面讲过,RPM 依赖检查认的是包元数据而不是裸文件,所以拷文件并不能消除报错。更麻烦的是,这个手工拷贝的库可能和系统里已有的 OpenSSL 1.0.2 头文件、工具链版本不匹配,覆盖后可能导致openssl命令行、yum自身乃至 sshd 的加密模块工作异常。
如果你非要手工验证,正确姿势不是往/usr/lib64/里丢文件,而是把库放到独立目录,比如/opt/openssl-1.0.2/lib/,然后通过/etc/ld.so.conf.d/下新增配置:
echo "/opt/openssl-1.0.2/lib" > /etc/ld.so.conf.d/openssl-1.0.2.conf ldconfig再确认:
ldconfig -p | grep libcrypto.so.10这样至少不会污染系统自带库目录。但依然要注意:RPM 依赖检查仍然不认,因为没有任何包声明Provides: libcrypto.so.10。
5.3 什么情况下的确可以绕过:符号版本匹配的库已在系统里
--nodeps也并非完全没有使用场景。有一种情况:系统里本身已经由源码编译方式安装了一整套 OpenSSL 1.0.2 到/usr/local/ssl,其libcrypto.so.10符号版本完整,并且已经通过 ld.so.conf 或LD_LIBRARY_PATH让动态加载器能找到。此时你完全清楚自己在做什么,只是想让 Erlang 的 RPM 装进系统,然后依赖这个自定义 OpenSSL 运行。
这种情况下用:
rpm -ivh --nodeps erlang-22.0.7-1.el7.x86_64.rpm是可行的,但需要同时保证运行 Erlang 的进程环境里LD_LIBRARY_PATH包含了/usr/local/ssl/lib,否则erl一启动就找不到libcrypto.so.10。
如果是在容器里做测试,也可以把编译好的 Erlang 直接放进镜像,配合指定 OpenSSL 1.0.2 的基础镜像,绕开 RPM 依赖体系。生产环境我不建议这么干,因为维护成本太高,你以后每次重构环境都要重新梳理这些手工约定。
注意:无论你采用什么绕过方案,安装完都务必验证一行命令:
erl -noshell -eval 'io:format("~p~n", [crypto:supports()]), halt().'如果crypto模块不能正常输出支持的算法列表,说明 OpenSSL 库加载还是有问题,趁早回头修依赖才是正路。
在我自己处理过的类似问题里,最省心的永远是"让系统的包管理和 Erlang 包的标注匹配起来":CentOS 7 就老老实实修好 vault 源用 el7 包,Stream 9 就用 el9 包。跳过依赖检查、手工拷贝动态库,那些都是把问题推迟到运行时的做法,代价只会更高。最后再提醒一句:不管哪条路,改完源或装完包后,先把yum repolist和rpm -q --provides openssl-libs两个输出看清楚,这两个命令比任何网上搜来的报错帖子都更能说明你机器现在的真实状态。