linux-incident-triage
name: linux-incident-triage
description: Linux 线上故障应急排查。当服务器负载高、内存吃紧、磁盘满、服务无响应、端口连不上、进程被杀、网络丢包时使用,用于快速定位并止血。触发词:服务器卡了、负载高、CPU 100%、内存爆了、OOM、磁盘满了、no space left、服务挂了、连不上、502、504、丢包、僵尸进程、应急、排查。不负责服务器初始化安全加固和长期容量规划。
Linux 线上应急排查
应急有两条铁律,比任何命令都重要:
- 先保留现场,再动手。 重启能解决问题,但也会销毁全部证据,下次照样挂。
- 止血优先于治本。 回滚比修复快,先让用户能用,再慢慢查。
零、先留证据(60 秒)
在做任何变更之前,把现场存下来。
TS=$(date +%F-%H%M%S); D=/var/tmp/incident-; mkdir -p
{ uptime; echo; free -m; echo; df -h; echo; df -i; } > /basic.txt 2>&1
top -b -n1 | head -50 > /top.txt 2>&1
ps auxf > /ps.txt 2>&1
ss -antp > /socket.txt 2>&1
dmesg -T | tail -200 > /dmesg.txt 2>&1
journalctl -n 2000 --no-pager > /journal.txt 2>&1
vmstat 1 5 > /vmstat.txt 2>&1
iostat -x 1 5 > /iostat.txt 2>&1
echo "现场已保存到 "
df -i 别漏。inode 耗尽和磁盘满的表现一模一样,但 df -h 显示还有空间,能让人查半小时。
一、五分钟通用体检
按这个顺序过一遍,通常前三条就能定位。
uptime # 1/5/15 分钟负载。和 CPU 核数比,nproc 看核数
free -m # 看 available 那一列,不是 free
df -h; df -i # 空间和 inode
top -o %CPU # 谁在吃 CPU
top -o %MEM # 谁在吃内存
ss -s # 连接数概览
dmesg -T | tail -50 # OOM、磁盘错误、网卡异常都在这
判读负载:负载数字要除以核数看。8 核机器负载 8 是满载,负载 30 是严重排队。但负载高不等于 CPU 高,vmstat 1 的 b 列(不可中断阻塞)大,说明是 IO 在拖。
二、按症状分诊
CPU 打满
top -H -p <pid> # 定位到线程
perf top -p <pid> # 看热点函数
cat /proc/<pid>/status | grep -i threads
Java 用 jstack,Go 用 curl localhost:<port>/debug/pprof/profile,Python 用 py-spy dump --pid <pid>。
常见原因:死循环、正则回溯、GC 频繁、突然放大的流量、加密解密没走硬件指令。
内存吃紧 / OOM
dmesg -T | grep -i -E "out of memory|killed process"
cat /proc/<pid>/status | grep -E "VmRSS|VmSwap"
smem -rs uss | head # 按实际独占内存排序,比 RSS 准
free -m 里 buff/cache 高是正常的,那是可回收的页缓存。真正要看 available。
被 OOM Killer 干掉的进程,dmesg 里会写明分数和内存占用。容器场景要看 cgroup 限制:
cat /sys/fs/cgroup/memory.max # cgroup v2
cat /sys/fs/cgroup/memory/memory.limit_in_bytes # v1
容器里 OOM 通常是 --memory 设小了,或者 JVM/Node 的堆上限没跟着容器限制走。
磁盘满
df -h; df -i
du -x -h --max-depth=1 / 2>/dev/null | sort -h | tail -20
lsof +L1 # 已删除但句柄未释放的文件,占空间但 du 看不见
lsof +L1 是磁盘满排查里最容易被忽略的一步。日志被 rm 了但进程还开着句柄,空间不会释放,必须重启进程或者 truncate -s 0 /proc/<pid>/fd/<n>。
止血:先清 /var/log 下的旧日志和 journalctl --vacuum-size=200M,再补 logrotate 配置。所有应用日志都必须配轮转,没配的迟早会以磁盘满的形式提醒你。
服务无响应 / 端口连不上
systemctl status <svc>; journalctl -u <svc> -n 200 --no-pager
ss -lntp | grep <port> # 端口在不在监听
curl -sv --max-time 5 http://127.0.0.1:<port>/healthz
分三层确认:进程活着吗 → 端口监听吗 → 本机能连通吗。三层都通还连不上,问题在防火墙、安全组或反向代理。
iptables -S; nft list ruleset # 本机防火墙
ss -ant state syn-recv | wc -l # 半连接堆积,可能是 SYN flood 或 backlog 太小
cat /proc/net/sockstat # 连接数与内存
TIME_WAIT 堆积到几万很常见且通常无害,别急着调内核参数。真需要调之前,先确认你理解那个参数的含义。
网络慢 / 丢包
ping -c 20 <目标>
mtr -rwzc 50 <目标> # 逐跳丢包,比 traceroute 有用得多
ss -ti # 看 rtt、重传
ethtool -S <网卡> | grep -i -E "drop|err"
mtr 中间跳丢包但最后一跳不丢,通常是中间路由的 ICMP 限速,不是真丢包。要看最后一跳的丢包率。
三、止血手段(按优先级)
- 回滚。 如果故障和最近一次发布时间吻合,先回滚,别查。恢复旧产物通常一两分钟。
- 摘流量。 从负载均衡摘掉异常节点,保留现场慢慢查。
- 限流降级。 关掉非核心功能,保住主链路。
- 扩容。 加机器是最贵但最快的解法,适合确认是容量问题时。
- 重启。 排最后。重启前务必已经完成第零步的现场保留。
四、常见误区
- 一上来就
reboot。 证据全没了,且不一定能好。 - 看到
TIME_WAIT多就改内核参数。 尤其是打开tcp_tw_recycle,在 NAT 环境下会造成随机连接失败,且该参数在新内核已被移除。 kill -9数据库或有状态服务。 先kill -15等它自己收尾。- 磁盘满了直接
rm大日志。 句柄不释放,空间不回来。用truncate -s 0或先停服务。 - 在没有备份确认的情况下动数据。 任何
UPDATE/DELETE都要先确认最近备份可恢复,且必须带WHERE。 - 只修表象不记录。 故障处理完没有复盘,同一个坑会踩三次。
五、收尾
故障恢复后必须做三件事,缺一件这次故障就白挂了:
- 写复盘:影响范围、时间线、直接原因、根本原因、改进项。改进项必须带负责人和截止日期。
- 补监控:这次是靠用户报障发现的,还是监控发现的?靠用户报障说明监控有洞,补上。重点是 CPU、内存、磁盘空间与 inode 都要有 80% 阈值告警。
- 验证备份:顺手确认备份能恢复。没演练过的备份等于没有备份。