Skip to content

09a|软件安装故障与进阶

上一篇学会了三种安装方式。但实际装软件时会遇到各种问题:yum 报锁装不上、依赖冲突、源码编译失败、装完跑不起来。本篇讲这些故障的排查,以及一些进阶话题(动态库依赖、版本回滚、Python 多版本)。

一、yum 报锁:Another app is holding yum lock

装软件时报这个错,说明有另一个 yum 进程在跑(可能是系统的自动更新工具):

bash
yum install nginx
# Existing lock /var/run/yum.pid: another copy is running as pid 12345.

排查

bash
# 第 1 步:看是谁持有锁
ps -ef | grep yum
# 可能是 yum-cron、PackageKit、dnf-automatic 这类自动更新工具

# 第 2 步:如果是自动更新工具,等它跑完
# 如果是异常残留的进程,清理锁
rm -f /var/run/yum.pid
kill <残留进程PID>

生产环境推荐关闭自动更新,避免它和手工安装互相阻塞:

bash
# RHEL 系关闭 yum-cron
systemctl stop yum-cron
systemctl disable yum-cron

# 或 dnf-automatic
systemctl stop dnf-automatic
systemctl disable dnf-automatic

二、依赖冲突

装 A 依赖 B 的旧版,装 C 依赖 B 的新版,两个需求打架,就是依赖冲突。表现通常是:

bash
yum install packageA
# Error: Package: packageA requires libfoo.so.2, but libfoo-1.x is installed

排查

先看两个包分别来自哪个仓库:

bash
yum list available libfoo
yum list installed libfoo

再看冲突方提供的是哪个版本:

bash
yum provides libfoo.so.2

处理

方式 1:卸载冲突方(如果它不是关键组件)。

方式 2:禁用提供冲突包的仓库后再安装:

bash
yum install packageA --disablerepo=epel

严重冲突时可能需要升级或降级某个核心库,但核心库(glibc、openssl、libstdc++)的版本变动会影响全系统,操作前要评估影响。

三、误删系统包导致系统损坏

误删了 glibc、coreutils 这类基础包,会导致命令找不到、甚至无法登录。

防护

大多数包管理器会拒绝卸载受保护包。可以装保护插件:

bash
yum install yum-plugin-protect-packages
# 编辑 /etc/yum/protected.d/system.conf,列出受保护的包
glibc
coreutils
bash
openssl

应急

极端情况下系统已经损坏(命令找不到、无法正常启动),从其他同版本机器拷贝包文件,手动重装:

bash
# 用 rescue 模式或 live CD 启动
# 挂载原系统分区
rpm -ivh --root=/mnt/sysimage --force *.rpm

或者用安装盘进 rescue 模式重装核心包。

四、源码编译失败

源码编译最常见的失败是缺开发库,./configuremake 报错。

现象 1:configure 报缺库

text
./configure: error: the HTTP rewrite module requires the PCRE library.

根因:缺 pcre-devel(或 Debian 的 libpcre3-dev)。上一篇讲过,运行时库和开发库是分开的两个包,编译要装带 -devel/-dev 后缀的开发库:

bash
yum install pcre-devel -y       # RHEL 系
apt install libpcre3-dev -y      # Debian 系

现象 2:make 报找不到头文件

text
fatal error: openssl/ssl.h: No such file or directory

根因:缺 openssl-devel(或 libssl-dev)。ssl.h 是头文件,只装运行时库 openssl 不带,要装开发库。

现象 3:链接时报 undefined symbol

text
undefined reference to `function_name'

根因:编译时没链接到所需的库。./configure 要加 -l 参数指定库名,或装对应库的开发包。

排查编译失败的关键是看错误信息里缺的是头文件.h)还是库文件.so)——头文件缺装 -devel/-dev,库文件缺要检查 ./configure 的链接参数。

五、动态库依赖:ldd 与 ldconfig

源码编译或二进制部署的软件,运行时报"找不到动态库":

text
./app: error while loading shared libraries: libfoo.so.2: cannot open shared object file: No such file or directory

这是动态库依赖问题。

用 ldd 看依赖

ldd 列出一个可执行文件依赖哪些动态库:

bash
ldd /opt/app/bin/app
# linux-vdso.so.1
# libfoo.so.2 => not found          ← 这行 not found 就是问题
# libc.so.6 => /lib64/libc.so.6

not found 的库就是缺失的。接下来找这个库装在哪了:

bash
find / -name "libfoo.so*" 2>/dev/null
# /opt/libfoo/lib/libfoo.so.2

让系统找到库:ldconfig

库文件在非标准路径下(如 /opt/libfoo/lib),系统默认找不到。两种方式让它被找到:

方式 1:把库路径加到 ld 配置:

bash
echo "/opt/libfoo/lib" > /etc/ld.so.conf.d/libfoo.conf
ldconfig                # 刷新动态库缓存

方式 2:用环境变量(临时):

bash
export LD_LIBRARY_PATH=/opt/libfoo/lib
./app

ldconfig 刷新的是 /etc/ld.so.cache,系统启动时和装新库后都要跑一次。LD_LIBRARY_PATH 只对当前 shell 生效,适合临时测试,不适合写进生产配置。

六、版本升级导致 ABI 不兼容

升级某个库后,依赖它的程序跑不起来了——这是 ABI(应用二进制接口)不兼容。常见场景:升级 libstdc++ 后,旧程序报 GLIBCXX_3.4.29 not found

排查

看程序需要哪些符号(symbol):

bash
# 看应用需要哪些符号版本
strings /opt/app/bin/app | grep GLIBC
# GLIBC_2.28
# GLIBCXX_3.4.29

看当前系统提供哪些:

bash
strings /lib64/libstdc++.so.6 | grep GLIBCXX

如果系统提供的符号版本低于程序需要的,就是 ABI 不兼容。

处理

  • 系统缺少高版本符号:升级对应的库(但要注意影响全系统)
  • 之前对某库做了升级或降级导致:回滚那个操作
bash
# 看最近的安装/升级记录
yum history list
# RHEL 系可以撤销某次操作
yum history undo <ID>

生产环境对核心库(glibc、libstdc++、openssl)的版本变动要非常谨慎,最好先在测试机验证。

七、GPG 校验失败

安装时报 GPG key 校验失败:

text
warning: Package nginx.rpm is not signed
Error: GPG check FAILED

根因:仓库的 GPG key 没导入,或包确实被篡改。

处理

从仓库提供的 URL 导入 key(确认仓库可信的前提下):

bash
rpm --import https://mirrors.aliyun.com/epel/RPM-GPG-KEY-EPEL-9

或者临时跳过校验(只在确认仓库可信时用,不推荐作为常规做法):

bash
yum install --nogpgcheck nginx

跳过 GPG 校验意味着不验证包的来源和完整性,有安全风险。生产环境应该导入正确的 key 走正常校验,而不是 --nogpgcheck 绕过。

八、同一台机器多个 Python 解释器

很多服务器同时存在 python2python3/usr/local/bin/python(自己编译的)、/opt/conda/bin/python(Anaconda)。脚本里写 python xxx.py 实际跑的是哪个,完全看 PATH 顺序。

排查

bash
which -a python python2 python3       # 列出 PATH 中所有同名命令
ls -l /usr/bin/python                  # 看是否软链接到某个具体版本

which -a 会列出 PATH 里所有叫 python 的命令,按优先级排列。/usr/bin/python 可能是个软链接,指向 python3python2,不同发行版默认不同。

规范

生产脚本写 shebang 时,明确指定版本:

bash
#!/usr/bin/python3.9    # 比 #!/usr/bin/env python 更可靠

#!/usr/bin/env python 会按 PATH 找 python,不同机器 PATH 不一样就可能跑错版本。明确写 /usr/bin/python3.9 能避免这个问题。

九、卸载的注意事项

apt purge 删配置

apt purge nginx 不仅卸载程序,还会删 /etc/nginx/ 下的全部配置文件。如果机器上有手工调优过的配置,操作前必须先备份:

bash
# 卸载前快照
tar czf /backup/nginx-conf-$(date +%Y%m%d).tar.gz /etc/nginx /etc/ssl

# 卸载
apt purge nginx -y

生产环境的服务配置文件应当统一纳入版本管理系统(Git),机器上的配置只是工作副本。这样即使误执行 purge,从仓库重新拉取即可恢复。

卸载后依赖残留

yum remove nginx 卸载 nginx,但当初装 nginx 时一并装上的依赖包(如 nginx-all-modules)不会自动卸载。要清理这些孤儿包:

bash
# RHEL 系
yum autoremove

# Debian 系
apt autoremove

autoremove 会清理"当初作为依赖装上、现在没有其他包依赖它"的包。操作前看清楚列表,避免误删。

故障排查的共同思路

装软件出问题,排查顺序基本固定:

  1. 看报错信息——是锁、依赖冲突、缺库、还是 GPG。报错信息通常直接指向问题类型。
  2. 查文件归属和包来源——rpm -qf/dpkg -S 看文件属于哪个包,yum provides 看命令属于哪个包。
  3. 看仓库状态——yum repolist/apt list 确认仓库是否正常、版本是否符合预期。
  4. 动态库问题用 ldd——ldd 看缺什么库,ldconfig 刷新缓存。
  5. 核心库操作前评估影响——glibc、openssl 这类核心库的版本变动影响全系统,生产环境务必先测试。

下一篇转向计划任务——让机器按时间自动跑命令。