深夜三点,手机闹钟炸响。生产环境的监控大屏上,几台核心业务的 AlmaLinux 节点红灯狂闪。用户反馈页面加载失败,API 响应超时。你连上跳板机,发现 SSH 登录异常缓慢,终端光标像是在泥沼里挣扎。这不仅是技术问题,更是一场与时间的赛跑。今天,我们就来拆解这场典型的“系统卡死”危机,从内核恐慌到网络风暴,一步步还原排查逻辑。
第一现场:SSH 连不上,系统“死”了吗?
别急着重启。这是新手最容易犯的错误——在没搞清楚状态前盲目重启,不仅数据可能丢失,还会破坏现场日志,让后续复盘无从下手。
首先,我们需要判断系统的真实状态。当你 SSH 上去看到 last login: ... 但输入任何命令都毫无反应时,屏幕卡在 Starting... 或者 Reached target... 字样,这通常是 systemd 挂起或某个服务依赖死锁。
第一步:用魔法键“起死回生”
如果键盘还能响应,尝试按下 SysRq 组合键。在 Linux 中,通过发送魔术信号给内核,可以强制完成一些底层操作。比如,按下 Alt + SysRq + R 可以将键盘从 X 模式切换回 raw 模式,有时能恢复终端响应。接着尝试 Alt + SysRq + E(发送 SIGTERM 给所有进程)或 Alt + SysRq + I(发送 SIGKILL)。这比直接拉电源安全得多。
第二步:物理或带外访问
如果 SSH 完全无响应,检查控制台输出。如果有 IPMI/iDRAC/ILO 带外管理权限,立刻通过 KVM 连接。在 KVM 界面中,你通常能看到内核打印的最后几行日志,或者 systemd 卡住的具体位置。
深入内核:Panic 与 Oops 的解读
如果系统在重启后再次崩溃,或者在运行中突然内核报错,你看到的可能是 Kernel Panic。
场景模拟: 屏幕上出现彩虹色的代码块,最后一行写着 Kernel panic - not syncing: Fatal exception 或 stack trace:。
这时候,截图!或者通过控制台导出日志。内核panic 通常由驱动程序 bug、硬件故障(如内存条位翻转)或文件系统损坏引起。
关键工具:kdump 服务
在 AlmaLinux 中,kdump 是捕捉内核崩溃现场的关键。它会在内核崩溃时,将内存转储到磁盘或远程服务器。
# 检查 kdump 服务是否启用
systemctl status kdump
# 如果没启用,立即启用并配置保存路径
sudo systemctl enable kdump
sudo systemctl start kdump
当系统意外重启后,查看 /var/crash/ 目录。如果有新的时间戳目录,里面就是珍贵的 vmcore 文件。配合 crash 工具分析:
# 安装 crash 工具
sudo dnf install crash
# 分析 vmcore(需要对应的 debuginfo 包)
crash /usr/lib/debug/lib/modules/$(uname -r)/vmlinux /var/crash/127.0.0.1-2023-10-27-10:00:00/vmcore
在 crash 交互界面中,使用 bt 查看当前进程的调用栈,使用 ps 查看崩溃时的进程状态。这能告诉你到底是哪个驱动或哪个函数让内核“躺平”了。
内存泄漏与 OOM Killer 的“暴行”
另一种常见的“卡死”假象:系统没崩,但变慢了。这时候,top 或 htop 可能进不去,或者显示全部进程占用极高。
排查 OOM(内存不足):
内核会在内存耗尽时触发 OOM Killer,强行杀掉占用内存最大的进程。但这往往不是我们业务的关键进程,而是数据库或中间件。
# 查看内核日志中是否有 OOM 记录
sudo dmesg | grep -i "out of memory"
sudo dmesg | grep -i "killed process"
# 查看具体被杀的进程
sudo grep -r "oom-kill" /var/log/messages /var/log/secure /var/log/syslog 2>/dev/null
如果你看到 Out of memory: Killed process 1234 (postgres),那问题就清晰了。
隐藏的深度内存占用:
有时候,top 显示的 RSS(物理内存)正常,但内存依然不够。这时候要看 ps aux --sort=-%mem,并重点关注 VIRT(虚拟内存)和 RES(物理内存)。
# 查看内存详细使用
free -h
cat /proc/meminfo | grep -E "MemFree|MemAvailable|Buffers|Cached|Slab|CommitLimit|Committed_AS"
注意 Slab 和 Cached。如果 Slab 异常高,可能是文件系统缓存或 inode 泄漏;如果 Cached 很高但 MemAvailable 很低,说明内核没有足够内存给新进程分配空间。
实战技巧:找到“吃内存”的罪魁祸首
# 列出每个进程的详细内存映射
sudo pmap -x $(pgrep -f your_suspect_service)
# 或者使用 smem 工具查看 PSS( proportional set size )
sudo dnf install smem
sudo smem -t -p -r
网络异常的隐蔽陷阱
系统看起来活着,但服务无法访问,或者对外请求失败。这时候,网络栈可能是“半死不活”的状态。
场景:TCP 连接堆积
检查连接状态:
ss -tan state established '( dport = :80 or dport = :443 )' | wc -l
ss -tan state syn-recv | head -20
如果 syn-recv 状态连接暴增,你可能正在遭受 SYN Flood 攻击,或者防火墙规则导致连接无法完成握手。
场景:DNS 解析超时
服务起不来,可能是 DNS 解析卡死。测试一下:
time nslookup google.com
time dig google.com
如果 DNS 解析超过 5 秒,检查 /etc/resolv.conf 和网络连通性。有时是 NTP 时间不同步导致证书校验失败,进而影响服务启动。
场景:端口被占用
sudo ss -tlnp | grep :8080
sudo lsof -i :8080
确保没有残留的僵尸进程占着端口。
磁盘 I/O 等待:看不见的瓶颈
当 CPU 闲置,但系统依然卡顿,大概率是磁盘 I/O 等待过高(iowait)。
# 实时监控 I/O 状态
iostat -x 1 10
# 查看哪个进程在疯狂读盘
sudo iotop -oP
如果 await 值很高(比如超过 100ms),说明磁盘响应极慢。这可能是 RAID 卡电池故障、磁盘坏道,或者是日志文件被疯狂写入。
检查磁盘健康:
sudo smartctl -a /dev/sda
sudo dmesg | grep -i error | grep -i sd
服务启动失败的常见雷区
在 AlmaLinux 中,服务起不来通常有几个“老熟人”:
1. 磁盘空间已满
df -h
du -sh /var/log
如果根分区满了,systemd 可能无法创建临时文件,导致所有服务启动失败。清理 /var/log 下的旧日志文件,或者扩展逻辑卷。
2. SELinux 拦截
AlmaLinux 默认启用 SELinux。如果上下文配置错误,服务会被静默拒绝。
# 查看 SELinux 拒绝记录
sudo ausearch -m avc -ts recent
sudo audit2allow -m mypolicy < /tmp/audit.log
# 临时设置为 permissive 模式测试(仅用于排查,生产环境慎用)
sudo setenforce 0
3. 依赖服务未就绪
使用 systemctl status 查看服务的依赖树,确认前置服务(如数据库、网络)已正常启动。
systemctl list-dependencies nginx.service
journalctl -u nginx.service -n 20 --no-pager
实战案例:一次典型的排查全过程
背景:一台运行 MySQL 和 Nginx 的 AlmaLinux 9 服务器,某天下午开始响应缓慢,最终 MySQL 无法启动。
步骤 1:登录与初步判断
通过 IPMI 登录控制台,发现系统启动卡在 Started MySQL Community Server 的等待状态。SSH 偶尔能连上,但负载极高。
步骤 2:检查资源
uptime
# 输出: 14:32:15 up 2:15, 1 user, load average: 28.50, 15.20, 8.10
free -h
# Mem: 32Gi total, 200Mi free, 31Gi used
内存几乎耗尽。
步骤 3:定位进程
ps aux | head -20
发现一个名为 mysqld 的进程占用 95% 的内存。
步骤 4:分析原因 查看 MySQL 错误日志:
sudo tail -50 /var/log/mysqld.log
日志显示 InnoDB: Fatal error: cannot allocate memory for the buffer pool。
步骤 5:紧急处理 由于无法立即重启,尝试调整 MySQL 配置限制内存使用。但 systemd 服务文件锁定了配置,且修改配置需要重启。
在极度紧急的情况下,使用 kubectl 风格思维,先止血:
# 杀掉占用内存最高的非关键进程(假设发现某个 cron 任务泄漏)
sudo kill -9 <pid>
但这并没有解决问题,因为 MySQL 自己就占满了内存。
步骤 6:最终解决
由于内存完全耗尽,系统无法响应任何命令。最终通过 IPMI 强制重启。重启后,进入救援模式,修改 /etc/my.cnf.d/server.cnf,降低 innodb_buffer_pool_size 从 24G 到 16G,以适应 32G 的物理内存上限(保留足够内存给系统和缓存)。
步骤 7:复盘与预防 重启后,监控显示内存使用平稳。但为什么内存会突然飙升?检查 MySQL 慢查询日志,发现有一个全表扫描的复杂 JOIN 操作,导致临时表溢出到内存。优化 SQL 后,问题不再复现。
给小朋友也能听懂的比喻
想象你的电脑是一个巨大的图书馆:
- CPU 是图书管理员,负责整理和借书。
- 内存 是办公桌,上面能同时摊开几本书。
- 硬盘 是书架,存放所有书的地方。
- 网络 是快递,负责把书送到读者手里。
系统卡死就像是:
- 办公桌堆满了(内存泄漏):管理员想不出手,因为手都被书占住了。
- 书架塌了(磁盘故障):书找不到,管理员在那儿傻等。
- 快递被堵死了(网络异常):书在图书馆里,但读者拿不到,以为图书馆关门了。
- 管理员晕倒了(内核 Panic):整个图书馆瘫痪,没人能工作了。
排查的时候,你要当侦探,看看是哪里堵住了,是书太多了,还是路不通了。
总结:建立你的“急救包”
在 AlmaLinux 上,拥有一套标准的应急工具集至关重要:
- always-on 监控:部署 Prometheus + Grafana 或 Zabbix,设置内存、CPU、磁盘、网络的阈值告警。
- 日志集中化:使用 ELK Stack 或 Loki 收集日志,避免在故障机器上查找日志。
- 定期演练:不要等到生产环境崩了才去熟悉
kdump、crash、ss、iostat等工具。 - 文档化:记录每次故障的排查路径,形成知识库。
系统故障是运维的常态,而不是例外。保持冷静,遵循逻辑,利用工具,你就能在混乱中找到秩序。记住,每一次故障排查,都是你成为 Linux 专家的一块垫脚石。
