故障排查实战
生产环境故障排查需要系统化的方法和丰富的实战经验。本章介绍常见故障的排查思路和解决方案。
故障排查流程
1. 故障现象确认
├─ 收集错误信息
├─ 复现问题
└─ 确定影响范围
2. 初步诊断
├─ 检查系统日志
├─ 查看监控指标
└─ 排除常见原因
3. 深入分析
├─ 使用诊断工具
├─ 分析根本原因
└─ 验证假设
4. 解决问题
├─ 实施解决方案
├─ 验证修复效果
└─ 文档记录
5. 预防措施
├─ 完善监控
├─ 更新文档
└─ 制定预案
系统无法启动
案例1:Grub 损坏
现象:重启后显示 "grub rescue>"
解决步骤:
# 1. 查找 Linux 分区
grub rescue> ls
(hd0) (hd0,msdos1) (hd0,msdos2)
# 2. 逐个检查分区
grub rescue> ls (hd0,msdos1)/
error: unknown filesystem
grub rescue> ls (hd0,msdos2)/
boot/ etc/ home/ ...
# 3. 设置正确的分区
grub rescue> set root=(hd0,msdos2)
grub rescue> set prefix=(hd0,msdos2)/boot/grub
# 4. 加载模块
grub rescue> insmod normal
grub rescue> normal
# 5. 进入系统后重新安装 grub
sudo grub-install /dev/sda
sudo update-grub
案例2:文件系统损坏
现象:系统启动失败,提示文件系统错误
解决步骤:
# 1. 进入救援模式
# 在 GRUB 菜单添加参数:systemd.unit=rescue.target
# 2. 以只读方式挂载根分区
mount -o remount,ro /
# 3. 检查并修复文件系统
fsck -y /dev/sda1
# 4. 如果是 XFS 文件系统
xfs_repair /dev/sda1
# 5. 重新挂载为读写
mount -o remount,rw /
# 6. 重启
reboot
案例3:忘记 root 密码
解决步骤:
# 1. GRUB 菜单按 'e' 编辑
# 2. 找到 linux 行,在末尾添加:init=/bin/bash
# 3. Ctrl+X 启动
# 4. 重新挂载根分区为读写
mount -o remount,rw /
# 5. 修改密码
passwd root
# 6. 更新 SELinux 上下文(如果启用)
touch /.autorelabel
# 7. 正常启动
exec /sbin/init
CPU 问题排查
案例:CPU 使用率持续 100%
排查脚本:
#!/bin/bash
# diagnose-high-cpu.sh
echo "=== CPU 使用率分析 ==="
echo "时间: $(date)"
echo ""
# 1. 查看整体 CPU 使用
echo "--- 系统负载 ---"
uptime
echo ""
# 2. 找出 CPU 占用最高的进程
echo "--- Top 10 CPU 进程 ---"
ps aux --sort=-%cpu | head -11
echo ""
# 3. 获取最高 CPU 进程的详细信息
HIGH_CPU_PID=$(ps aux --sort=-%cpu | awk 'NR==2 {print $2}')
echo "--- 进程 $HIGH_CPU_PID 详细信息 ---"
ps -p $HIGH_CPU_PID -o pid,ppid,cmd,pcpu,pmem,vsz,rss,stat,start,time
echo ""
# 4. 查看进程的线程
echo "--- 进程 $HIGH_CPU_PID 的线程 CPU 使用 ---"
ps -L -p $HIGH_CPU_PID -o pid,tid,pcpu,pmem,comm
echo ""
# 5. 查看进程打开的文件
echo "--- 进程 $HIGH_CPU_PID 打开的文件 ---"
lsof -p $HIGH_CPU_PID | head -20
echo ""
# 6. 系统调用统计(采样 5 秒)
echo "--- 系统调用统计 ---"
timeout 5 strace -c -p $HIGH_CPU_PID 2>&1 | grep -v "Process"
echo ""
# 7. CPU 核心使用分布
echo "--- CPU 核心使用情况 ---"
mpstat -P ALL 1 3
常见原因和解决方法:
# 1. 死循环代码
# 解决:使用 gdb 或 perf 分析热点函数
# 2. 正则表达式性能问题
grep -P 'complex.*regex' huge_file.txt
# 优化:使用更简单的正则或其他工具
# 3. 进程未正常退出
# 检查僵尸进程
ps aux | grep Z
# 清理僵尸进程(杀死父进程)
kill -9 $(ps -o ppid= -p <zombie_pid>)
内存问题排查
案例:系统内存不足
诊断脚本:
#!/bin/bash
# diagnose-memory.sh
echo "=== 内存使用分析 ==="
# 1. 内存概览
echo "--- 内存使用情况 ---"
free -h
echo ""
# 2. 详细内存信息
echo "--- 详细内存统计 ---"
cat /proc/meminfo | grep -E 'MemTotal|MemFree|MemAvailable|Buffers|Cached|SwapTotal|SwapFree|Dirty|Writeback|Slab'
echo ""
# 3. 内存占用 Top 10
echo "--- Top 10 内存占用进程 ---"
ps aux --sort=-%mem | head -11
echo ""
# 4. 每个进程的内存详情
echo "--- 进程内存详细信息 ---"
for pid in $(ps aux --sort=-%mem | awk 'NR>1 && NR<=11 {print $2}'); do
echo "PID: $pid"
cat /proc/$pid/status 2>/dev/null | grep -E 'Name|VmSize|VmRSS|VmSwap'
echo ""
done
# 5. Slab 内存使用
echo "--- Slab 内存占用 Top 10 ---"
sudo slabtop -o | head -20
echo ""
# 6. OOM Killer 历史
echo "--- OOM Killer 事件 ---"
dmesg | grep -i 'out of memory'
内存泄漏检测:
#!/bin/bash
# detect-memory-leak.sh
PID=$1
INTERVAL=5
COUNT=12
if [ -z "$PID" ]; then
echo "用法: $0 <PID>"
exit 1
fi
echo "监控进程 $PID 的内存使用(每 $INTERVAL 秒)"
echo "时间,VSZ(KB),RSS(KB),内存%"
for i in $(seq 1 $COUNT); do
ps -p $PID -o pid,vsz,rss,%mem --no-headers | \
awk -v time="$(date +%H:%M:%S)" '{printf "%s,%s,%s,%s\n", time,$2,$3,$4}'
sleep $INTERVAL
done
OOM Killer 分析
# 查看 OOM 日志
journalctl -k | grep -i 'killed process'
# 配置 OOM 分数调整
echo -1000 > /proc/<PID>/oom_score_adj # -1000 禁用 OOM killer
echo 1000 > /proc/<PID>/oom_score_adj # 1000 优先被杀
# 查看进程的 OOM 分数
cat /proc/<PID>/oom_score
磁盘问题排查
案例:磁盘空间不足
快速定位大文件:
#!/bin/bash
# find-large-files.sh
echo "=== 磁盘使用情况 ==="
df -h
echo ""
echo "=== 各目录占用空间 Top 10 ==="
du -h --max-depth=1 / 2>/dev/null | sort -hr | head -10
echo ""
echo "=== 查找大于 100MB 的文件 ==="
find / -type f -size +100M -exec ls -lh {} \; 2>/dev/null | \
awk '{ print $9 ": " $5 }' | sort -k2 -hr | head -20
echo ""
echo "=== 查找大量小文件的目录 ==="
find / -type d -exec sh -c 'echo "$(find "{}" -maxdepth 1 -type f | wc -l) {}"' \; 2>/dev/null | \
sort -rn | head -10
清理空间:
# 1. 清理日志文件
journalctl --vacuum-time=7d # 保留最近 7 天
journalctl --vacuum-size=1G # 限制最大 1GB
# 2. 清理包管理器缓存
# Debian/Ubuntu
apt clean
apt autoclean
# RHEL/CentOS
yum clean all
# 3. 清理旧内核
# Ubuntu
apt autoremove --purge
# 4. 查找并删除已删除但未释放的文件
lsof | grep deleted
kill -9 <PID> # 重启持有文件的进程
案例:磁盘 I/O 性能下降
诊断脚本:
#!/bin/bash
# diagnose-disk-io.sh
echo "=== 磁盘 I/O 分析 ==="
# 1. I/O 统计
echo "--- 磁盘 I/O 使用率 ---"
iostat -x 1 5
echo ""
# 2. I/O 密集型进程
echo "--- I/O 密集型进程 ---"
iotop -b -n 1 -o
echo ""
# 3. 磁盘健康状态
echo "--- 磁盘健康检查 ---"
for disk in /dev/sd[a-z]; do
if [ -b "$disk" ]; then
echo "检查 $disk"
smartctl -H $disk 2>/dev/null
fi
done
echo ""
# 4. 挂载点 I/O 统计
echo "--- 文件系统 I/O ---"
cat /proc/diskstats
网络问题排查
案例:网络连接故障
网络诊断脚本:
#!/bin/bash
# diagnose-network.sh
TARGET_HOST="8.8.8.8"
TARGET_PORT=443
echo "=== 网络诊断 ==="
# 1. 网络接口状态
echo "--- 网络接口 ---"
ip addr show
echo ""
# 2. 路由表
echo "--- 路由表 ---"
ip route show
echo ""
# 3. DNS 解析
echo "--- DNS 解析测试 ---"
nslookup google.com
echo ""
# 4. 连通性测试
echo "--- Ping 测试 ---"
ping -c 4 $TARGET_HOST
echo ""
# 5. 路由追踪
echo "--- 路由追踪 ---"
traceroute -n $TARGET_HOST
echo ""
# 6. 端口连接测试
echo "--- 端口连接测试 ---"
nc -zv $TARGET_HOST $TARGET_PORT
echo ""
# 7. 防火墙规则
echo "--- 防火墙规则 ---"
iptables -L -n -v
echo ""
# 8. 网络统计
echo "--- 网络连接统计 ---"
ss -s
案例:网络丢包
# 查看网络错误统计
netstat -i
ip -s link show
# 检查丢包
ping -c 100 -i 0.2 $TARGET | grep loss
# 查看网络缓冲区
sysctl net.core.rmem_max
sysctl net.core.wmem_max
# 调整缓冲区大小
sysctl -w net.core.rmem_max=134217728
sysctl -w net.core.wmem_max=134217728
服务不可用排查
Web 服务故障
Nginx 故障排查:
#!/bin/bash
# diagnose-nginx.sh
echo "=== Nginx 诊断 ==="
# 1. 服务状态
echo "--- Nginx 服务状态 ---"
systemctl status nginx
echo ""
# 2. 配置测试
echo "--- 配置文件语法检查 ---"
nginx -t
echo ""
# 3. 端口监听
echo "--- 端口监听状态 ---"
ss -tuln | grep :80
ss -tuln | grep :443
echo ""
# 4. 进程信息
echo "--- Nginx 进程 ---"
ps aux | grep nginx
echo ""
# 5. 错误日志
echo "--- 最近错误日志 ---"
tail -50 /var/log/nginx/error.log
echo ""
# 6. 访问日志
echo "--- 最近访问日志 ---"
tail -20 /var/log/nginx/access.log
echo ""
# 7. 连接数统计
echo "--- 当前连接数 ---"
ss -ant | grep :80 | wc -l
数据库故障
MySQL 故障排查:
#!/bin/bash
# diagnose-mysql.sh
echo "=== MySQL 诊断 ==="
# 1. 服务状态
systemctl status mysql
# 2. 连接测试
mysql -u root -p -e "SELECT 1;"
# 3. 查看进程列表
mysql -u root -p -e "SHOW PROCESSLIST;"
# 4. 查看锁等待
mysql -u root -p -e "SHOW ENGINE INNODB STATUS\G" | grep -A 10 "LATEST DETECTED DEADLOCK"
# 5. 慢查询日志
tail -100 /var/log/mysql/slow-query.log
# 6. 错误日志
tail -100 /var/log/mysql/error.log
# 7. 连接数
mysql -u root -p -e "SHOW STATUS LIKE 'Threads_connected';"
mysql -u root -p -e "SHOW STATUS LIKE 'Max_used_connections';"
故障预防
监控脚本
#!/bin/bash
# health-check.sh
ALERT_EMAIL="admin@example.com"
ALERT_THRESHOLD_CPU=80
ALERT_THRESHOLD_MEM=90
ALERT_THRESHOLD_DISK=85
# CPU 检查
CPU_USAGE=$(top -bn1 | grep "Cpu(s)" | awk '{print $2}' | cut -d'%' -f1)
if (( $(echo "$CPU_USAGE > $ALERT_THRESHOLD_CPU" | bc -l) )); then
echo "警告: CPU 使用率 ${CPU_USAGE}% 超过阈值 ${ALERT_THRESHOLD_CPU}%" | \
mail -s "CPU Alert" $ALERT_EMAIL
fi
# 内存检查
MEM_USAGE=$(free | grep Mem | awk '{printf("%.0f", $3/$2 * 100)}')
if [ $MEM_USAGE -gt $ALERT_THRESHOLD_MEM ]; then
echo "警告: 内存使用率 ${MEM_USAGE}% 超过阈值 ${ALERT_THRESHOLD_MEM}%" | \
mail -s "Memory Alert" $ALERT_EMAIL
fi
# 磁盘检查
df -h | awk 'NR>1 {print $5 " " $6}' | while read output; do
usage=$(echo $output | awk '{print $1}' | sed 's/%//g')
mount=$(echo $output | awk '{print $2}')
if [ $usage -gt $ALERT_THRESHOLD_DISK ]; then
echo "警告: 磁盘 $mount 使用率 ${usage}% 超过阈值 ${ALERT_THRESHOLD_DISK}%" | \
mail -s "Disk Alert" $ALERT_EMAIL
fi
done
总结:故障排查是一个系统工程,需要扎实的基础知识、丰富的实战经验和科学的排查方法。