#!/bin/bash# =============================================================================# 脚本名称: nginx_triage.sh# 功能描述: 对 Nginx 意外中断进行系统化、哲学驱动的快速检查与取证# 遵循 本体论->奥卡姆剃刀->辩证法->整体论 的认知框架# 使用方式: 以 root 或具有适当权限的用户运行# 重要原则: 只取证,不恢复。恢复动作应在充分判断后由人工执行。# =============================================================================set-uopipefail# ---------------------------- 配置区 ----------------------------------------NGINX_MASTER_PID_FILE="/var/run/nginx.pid"NGINX_ERROR_LOG="/var/log/nginx/error.log"NGINX_ACCESS_LOG="/var/log/nginx/access.log"# 可能用于判断流量中断时间点JOURNAL_SINCE="5 minutes ago"# ---------------------------------------------------------------------------# 颜色定义RED='\033[0;31m'GREEN='\033[0;32m'YELLOW='\033[1;33m'BLUE='\033[0;34m'NC='\033[0m'# No Colorecho-e"${BLUE}============================================${NC}"echo-e"${BLUE}Nginx 中断现场快速检查 (Triage)${NC}"echo-e"${BLUE}理念: 本体归类 -> 奥卡姆剃刀 -> 辩证分期${NC}"echo-e"${BLUE}============================================${NC}"# ---------------------------------------------------------------------------# 第一部分:本体论归类 —— 快速识别“中断”属于哪类实体# 哲学依据: 区分症状实体(进程消失/无响应)与根因实体(OOM/磁盘满等)# 通过几条轻量命令,迅速将当前现象映射到已知故障模式# ---------------------------------------------------------------------------echo-e"\n${YELLOW}[阶段一] 本体论:识别中断现象实体${NC}"# 1.1 进程存在性echo-e"\n${GREEN}[检查1] Nginx 进程状态${NC}"if[-f"$NGINX_MASTER_PID_FILE"];thenmaster_pid=$(cat"$NGINX_MASTER_PID_FILE"2>/dev/null)if[-n"$master_pid"]&&kill-0"$master_pid"2>/dev/null;thenecho" Master 进程存在,PID:$master_pid"# 检查 worker 进程worker_count=$(pgrep-P"$master_pid"-c2>/dev/null||echo0)echo" Worker 进程数:$worker_count"if["$worker_count"-eq0];thenecho-e"${RED}>>> 现象归类:Master 在但无 Worker,可能配置重载失败或 worker 频繁退出${NC}"fielseecho-e"${RED}>>> 现象归类:PID 文件存在但进程不存在,Nginx 进程已死亡${NC}"fielseifpgrep-xnginx>/dev/null2>&1;thenecho" 未找到标准 PID 文件,但发现 nginx 相关进程运行中(可能为其他实例)"pgrep-anginxelseecho-e"${RED}>>> 现象归类:Nginx 进程完全不存在${NC}"fifi# 1.2 端口监听状态(整体论视角:也间接检查后端连接堆积)echo-e"\n${GREEN}[检查2] 端口监听与连接状态${NC}"# 检查常见端口,可根据需要调整forportin80443;dolisten_info=$(ss-tlnp"sport = :$port"2>/dev/null)if[-n"$listen_info"];thenecho" 端口$port正在监听:"echo"$listen_info"# 额外检查 Recv-Q/Send-Q 是否有堆积(无为思想:让内核数据说话)recvq=$(ss-tan"sport = :$port"2>/dev/null|awk'NR>1 {sum+=$2} END{print sum}')sendq=$(ss-tan"sport = :$port"2>/dev/null|awk'NR>1 {sum+=$3} END{print sum}')echo" 当前端口$port总 Recv-Q:$recvq, Send-Q:$sendq(非零可能表示请求堆积)"elseecho-e"${RED}端口$port未被监听${NC}"fidone# 手工检查步骤注释:如果无端口监听,请手动执行 'ss -tlnp' 检查是否有其他进程占用了 Nginx 需要的端口# 若有端口监听但无法访问,请检查防火墙规则 (iptables -L -n) 及本地策略# ---------------------------------------------------------------------------# 第二部分:奥卡姆剃刀 —— 用最少的命令获取最大信息量# 优先检查高概率、高信息密度的日志,而非全面铺开# ---------------------------------------------------------------------------echo-e"\n${YELLOW}[阶段二] 奥卡姆剃刀:高密度信息优先${NC}"# 2.1 Nginx 错误日志(最近30行,重点看 emerg/alert/crit)echo-e"\n${GREEN}[检查3] Nginx 错误日志 (最近30行)${NC}"if[-f"$NGINX_ERROR_LOG"];thentail-30"$NGINX_ERROR_LOG"|whileIFS=read-rline;doifecho"$line"|grep-qE'\[emerg\]|\[alert\]|\[crit\]|\[error\]';thenecho-e"${RED}$line${NC}"elseecho"$line"fidone# 快速检查是否有磁盘空间不足的典型错误iftail-50"$NGINX_ERROR_LOG"|grep-q"No space left on device";thenecho-e"${RED}>>> 明确发现:磁盘空间不足${NC}"fiiftail-50"$NGINX_ERROR_LOG"|grep-q"Too many open files";thenecho-e"${RED}>>> 明确发现:文件描述符耗尽${NC}"fielseecho-e"${RED}未找到错误日志文件:$NGINX_ERROR_LOG${NC}"fi# 手工检查:如果日志文件过大,请执行 'tail -100 /var/log/nginx/error.log | grep -E "emerg|alert|crit"' 并查看时间戳是否与中断吻合# 2.2 系统日志中的关键事件(OOM、段错误、systemd 杀死进程)echo-e"\n${GREEN}[检查4] 系统日志 (OOM/segfault/systemd)${NC}"# 优先使用 journalctl,若无则回退到 /var/log/messagesifcommand-vjournalctl>/dev/null2>&1;thenecho" --- journalctl (最近$JOURNAL_SINCE) ---"journalctl--since"$JOURNAL_SINCE"--no-pager2>/dev/null|grep-iE"nginx|oom|killed process|segfault|memory cgroup"||echo" (无匹配关键信息)"elseif[-f/var/log/messages];thenecho" --- /var/log/messages ---"grep-iE"nginx|oom|killed process|segfault"/var/log/messages|tail-20||echo" (无匹配关键信息)"fifi# 内核环形缓冲区echo" --- dmesg (最后15行) ---"dmesg|tail-15|grep-iE"oom|nginx|segfault|killed"||echo" (无匹配关键信息)"# 手工检查:如果怀疑 OOM,执行 'dmesg | grep -i "out of memory"' 查看完整 kill 列表# 手工检查:如果怀疑 systemd 资源限制,执行 'systemctl status nginx' 查看 Main PID 退出状态# ---------------------------------------------------------------------------# 第三部分:关键资源快速检查(奥卡姆剃刀继续:只查最可能且代价最低的)# ---------------------------------------------------------------------------echo-e"\n${YELLOW}[阶段三] 关键资源剃刀检查 (磁盘/文件描述符/内存)${NC}"# 3.1 磁盘空间与 inodeecho-e"\n${GREEN}[检查5] 磁盘使用情况${NC}"df-h|grep-vE'^tmpfs|^devtmpfs|^overlay'|awk'{if (NR==1 || $5+0 > 80) print}'# 只显示使用率>80%的分区echo""df-i|grep-vE'^tmpfs|^devtmpfs|^overlay'|awk'{if (NR==1 || $5+0 > 80) print}'echo-e"${YELLOW}提示: 重点关注 Nginx 日志目录所在分区、/tmp 和 /var${NC}"# 手工检查:若发现分区已满,执行 'du -sh /var/log/nginx/* | sort -rh | head' 找出大文件# 手工检查:若 inode 耗尽,执行 'find /tmp -type f | wc -l' 检查是否有海量临时文件# 3.2 文件描述符限制与当前使用量echo-e"\n${GREEN}[检查6] 文件描述符限制${NC}"if[-f/proc/sys/fs/file-max];thenecho" 系统全局限制:$(cat/proc/sys/fs/file-max)"fi# 如果 Master PID 存在,查看其 limitsif[-f"$NGINX_MASTER_PID_FILE"];thenmaster_pid=$(cat"$NGINX_MASTER_PID_FILE"2>/dev/null)if[-n"$master_pid"]&&[-d"/proc/$master_pid"];thenecho" Master 进程 limits:"grep"Max open files"/proc/$master_pid/limits2>/dev/null# 当前打开的文件数(快速估算,不遍历全进程以减轻负载)open_files=$(ls/proc/$master_pid/fd/2>/dev/null|wc-l)echo" Master 进程当前打开文件描述符数:$open_files"fifi# 手工检查:执行 'lsof -p <master_pid> | wc -l' 获得精确数量(可能稍慢)# 手工检查:若怀疑 ulimit 问题,检查 '/etc/security/limits.conf' 中的 nofile 设置# 3.3 内存与 Swap 快照echo-e"\n${GREEN}[检查7] 内存与 Swap 状态${NC}"free-hecho""if[-f/proc/swaps];thenecho" Swap 使用情况:"swapon--show2>/dev/null||cat/proc/swapsfi# 手工检查:如果内存不足,运行 'ps aux --sort=-%mem | head -10' 找出内存大户# 手工检查:如果怀疑 Nginx 内存泄漏,后续可使用 'pmap -x <worker_pid>' 分析# ---------------------------------------------------------------------------# 第四部分:整体论与配置检查 —— 跳出单点看交互# ---------------------------------------------------------------------------echo-e"\n${YELLOW}[阶段四] 整体论:跨组件关联与配置状态${NC}"# 4.1 检查与后端的连接状态echo-e"\n${GREEN}[检查8] 到上游后端服务的连接状态${NC}"# 查看与常见后端端口 (如 8080, 9000) 的连接ss-tanstate established|grep-E':80[^0-9]|:443[^0-9]|:8080|:9000'|head-15||echo" 无相关连接"echo-e"${YELLOW}提示: 如果连接数极少或状态异常,根因可能在后端${NC}"# 手工检查:执行 'ss -tanp | grep nginx' 查看所有 Nginx 相关连接# 手工检查:结合 APM/分布式追踪查看上游服务是否过载或不可达# 4.2 快速检查 Nginx 配置语法(有配置时)echo-e"\n${GREEN}[检查9] Nginx 配置状态${NC}"ifcommand-vnginx>/dev/null2>&1;then# 仅做语法检查,不输出正常信息,有错误才显示config_test=$(nginx-t2>&1)ifecho"$config_test"|grep-q"test is successful";thenecho" 配置文件语法: OK"elseecho-e"${RED}配置文件语法错误:${NC}"echo"$config_test"echo-e"${RED}>>> 这可能是 Nginx 无法启动或 reload 失败的直接原因${NC}"fielseecho" nginx 命令不可用,跳过配置检查"fi# 手工检查:如果配置错误导致 reload 失败,不要重启!先通过 'nginx -t' 定位并修正配置,再执行 reload# 手工检查:查看最后一次配置变更记录,使用版本控制系统 diff# 4.3 检查证书有效性(如果使用 HTTPS)echo-e"\n${GREEN}[检查10] SSL 证书状态 (概览)${NC}"# 遍历配置中常见的证书路径cert_paths=$(grep-r"ssl_certificate "/etc/nginx/2>/dev/null|grep-v"#"|awk'{print $NF}'|tr-d';'|sort-u)forcertin$cert_paths;doif[-f"$cert"];thenend_date=$(openssl x509-enddate-noout-in"$cert"2>/dev/null|cut-d=-f2)if[-n"$end_date"];thenexpiry_epoch=$(date-d"$end_date"+%s2>/dev/null)now_epoch=$(date+%s)if["$now_epoch"-gt"$expiry_epoch"];thenecho-e"${RED}证书已过期:$cert(过期于$end_date)${NC}"elseecho" 证书有效:$cert(到期:$end_date)"fifielseecho-e"${YELLOW}证书文件未找到:$cert${NC}"fidone# ---------------------------------------------------------------------------# 第五部分:辩证法总结 —— 给出排查方向,提示恢复决策# ---------------------------------------------------------------------------echo-e"\n${BLUE}============================================${NC}"echo-e"${BLUE}现场取证完成 - 诊断摘要${NC}"echo-e"${BLUE}============================================${NC}"# 利用已收集的信息给出快速判断(简单启发式)issues=()# 判断进程与端口if!pgrep-xnginx>/dev/null2>&1;thenissues+=("Nginx 进程已不存在")fiif[-f"$NGINX_ERROR_LOG"];thenifgrep-q"No space left on device""$NGINX_ERROR_LOG";thenissues+=("磁盘空间已满 (由错误日志确认)")fiifgrep-q"Too many open files""$NGINX_ERROR_LOG";thenissues+=("文件描述符不足 (由错误日志确认)")fiifgrep-q"cannot bind""$NGINX_ERROR_LOG";thenissues+=("端口绑定失败 (由错误日志确认)")fifiifdmesg|grep-qi"out of memory.*nginx"||journalctl--since"$JOURNAL_SINCE"2>/dev/null|grep-qi"out of memory.*nginx";thenissues+=("系统曾触发 OOM Killer,目标包含 Nginx")fiifcommand-vnginx>/dev/null2>&1;thenif!nginx-t>/dev/null2>&1;thenissues+=("Nginx 配置文件存在语法错误")fifiif[${#issues[@]}-eq0];thenecho-e"${GREEN}未发现明确的高概率根因。建议进行深度检查:${NC}"echo" 1. 检查 worker 是否因段错误频繁退出 (dmesg/coredump)"echo" 2. 分析后端服务响应时间与错误率 (全链路追踪)"echo" 3. 审查近期变更记录 (配置、代码、依赖库)"echo" 4. 如果 Nginx 进程存在但无响应,使用 strace -p <worker_pid> 观察"elseecho-e"${RED}发现以下关键问题:${NC}"forissuein"${issues[@]}";doecho-e"${RED}*$issue${NC}"donefiecho-e"\n${YELLOW}【辩证法决策提示】${NC}"echo-e" - 如果服务当前仍中断,优先根据上述线索执行${RED}可逆的恢复动作${NC}(如清理磁盘、回滚配置)"echo-e" - 恢复前请确保已保存上述输出或屏幕截图作为事后分析依据"echo-e" - 服务恢复后,再基于保留的证据进行${GREEN}彻底的根因分析${NC}"# ---------------------------------------------------------------------------# 手工补充检验的参考命令集合(不自动执行,仅供参考)# ---------------------------------------------------------------------------echo-e"\n${BLUE}--------------------------------------------${NC}"echo-e"${BLUE}手工深度检验命令参考 (奥卡姆剃刀之下的后续步骤)${NC}"echo-e"${BLUE}--------------------------------------------${NC}"cat<<'EOF' # 1. 若怀疑 OOM 但日志不明确,查看完整 OOM 计分板 dmesg | grep -i "oom" -A 10 # 2. 若 Master 进程在但无 Worker,开启 debug 日志重载 nginx -s stop # 仅在确认无法 reload 时使用 nginx -c /etc/nginx/nginx.conf # 观察控制台输出 # 3. 检查打开文件详情 lsof -p <master_pid> | awk '{print $5,$9}' | sort | uniq -c | sort -rn | head # 4. 分析 worker 段错误 coredumpctl list nginx coredumpctl gdb <pid> # 在 gdb 中执行 bt 命令查看调用栈 # 5. 检查 systemd 资源限制是否生效 systemctl show nginx | grep -iE "mem|limit|nofile" # 6. 全链路视角:使用 curl 测试本地回环和上游后端 curl -v http://localhost/status # 本地探针 curl -v http://<upstream_ip>:<port>/health # 后端健康检查 # 7. 检查 DNS 解析问题 tcpdump -i any port 53 -c 20 # 查看 Nginx 配置中的 resolver 设置,确认 DNS 可达 # 8. SELinux/AppArmor 审计 ausearch -m avc -ts recent # 查看最近的权限拒绝事件 aa-status # 查看 AppArmor 状态 # 9. 定时任务与人为脚本 crontab -l -u root ls -la /etc/cron.d/ # 排查是否有定期 stop/restart nginx 的脚本 # 10. 性能分析(仅当服务恢复后可安全执行) perf top -p <worker_pid> strace -p <worker_pid> -c -f EOFecho-e"\n${GREEN}脚本执行完成。请根据上述输出和手工检查提示完成故障定位。${NC}"