Nginx偶发超时排查实战:用tcpdump与eBPF定位无日志幽灵问题

📅 2026/8/19 7:41:16
Nginx偶发超时排查实战:用tcpdump与eBPF定位无日志幽灵问题
线上接口偶发超时但 Nginx 日志里干干净净这种问题最让人头疼。它不像 5xx 错误那样有明确指向往往需要你从网络、系统、应用多个层面去“破案”。如果你负责的 Web 服务遇到过这种“幽灵超时”这篇文章就是为你准备的。我们不谈空洞的理论直接拆解一套从现象到根因的实战排查流程核心工具就是tcpdump和eBPF前者帮你看清网络层到底发生了什么后者让你深入内核态揪出那些日志里看不到的阻塞点。这类问题的排查切忌一上来就盲目调整 Nginx 参数或重启应用。正确的思路是先定位超时发生的具体层面是客户端到 Nginx还是 Nginx 到后端应用或是后端应用自身再逐层深入。下面我们就按照这个思路把整个排查过程走一遍。1. 第一步确认问题边界与准备排查环境在开始抓包或分析内核之前必须先明确问题的现象和范围并准备好趁手的工具。盲目操作只会浪费时间。1.1 明确问题现象与收集信息当接到“接口偶发超时”的反馈时不要急于登录服务器。先问清楚这几个关键信息超时时间是多少是 30 秒、60 秒还是 5 秒这有助于判断是 TCP 层连接超时、应用层读写超时还是其他机制如负载均衡器、健康检查触发的。超时发生的频率和规律是每天固定时间还是随机发生请求量大的时候更容易出现吗这有助于判断是否与定时任务、资源争抢或流量峰值相关。客户端收到的具体错误是什么是Connection timed out、Read timed out还是Gateway Timeout (504)不同的错误指向不同的环节。影响范围是所有接口还是特定接口如果只是某个耗时较长的list接口或文件上传接口超时那问题可能出在后端应用处理逻辑或资源如数据库锁上。同时立刻登录服务器检查 Nginx 的错误日志 (error.log) 和访问日志 (access.log)。虽然标题说“没报错”但我们仍需确认error.log在超时时间点附近是否有upstream timed out相关的警告即使没有也要记录下这个“没有”的事实。access.log中对应请求的$upstream_response_time和$request_time字段值是多少这两个字段是黄金指标$upstream_response_time: Nginx 向后端应用建立连接、发送请求、接收完响应头部所花费的总时间。如果这个值接近或超过你配置的proxy_read_timeout问题很可能在后端。$request_time: 从接收客户端请求第一个字节到发送完响应最后一个字节的总时间。如果它很大而$upstream_response_time正常问题可能出在 Nginx 向客户端发送响应数据的过程网络或客户端。日志配置示例确保你的 Nginxlog_format包含了这些关键字段log_format main $remote_addr - $remote_user [$time_local] $request $status $body_bytes_sent $http_referer $http_user_agent $http_x_forwarded_for $request_time $upstream_response_time;1.2 部署关键排查工具tcpdump 与 eBPF工欲善其事必先利其器。我们需要在出问题的服务器上准备好抓包和深度追踪工具。tcpdump网络层抓包几乎所有的 Linux 发行版都可通过包管理器安装。它是查看 TCP 握手、数据传输、挥手以及是否有丢包、重传、零窗口等问题的标准工具。CentOS/RHEL:sudo yum install -y tcpdumpUbuntu/Debian:sudo apt-get install -y tcpdumpeBPF内核态追踪eBPF 允许我们在不修改内核代码的情况下动态注入程序来追踪系统调用、内核函数、网络事件等。对于“无日志”的偶发问题它是终极武器。常用的前端工具是bpftrace或BCC工具集它们对用户更友好。安装 bpftrace (推荐语法简洁):Ubuntu:sudo apt-get install -y bpftraceCentOS: 需要先启用 EPEL 仓库然后sudo yum install -y bpftrace安装 BCC 工具集 (功能更全):官方提供了各发行版的安装指南通常比 bpftrace 稍复杂一些但工具如execsnoop,opensnoop,tcplife等开箱即用。一个重要原则尽量在非生产环境或业务低峰期测试和熟悉这些命令。特别是 eBPF 脚本不当使用可能影响系统性能。2. 第二步使用 tcpdump 进行网络层问题隔离当应用日志没有线索时tcpdump 是我们的第一道“CT 扫描”。目标是确定超时发生在网络链路的哪一段。2.1 抓包策略与关键命令不要在服务器上无差别地抓所有包那样数据量太大。应该分层、分流量抓取。场景一怀疑是客户端到 Nginx 的网络问题在 Nginx 服务器上抓取到达指定端口如 80 或 443的流量并过滤出疑似超时的客户端 IP。# 假设 Nginx 监听 80 端口超时客户端IP是 10.0.0.100 sudo tcpdump -i any -s 0 -w client_to_nginx.pcap host 10.0.0.100 and port 80 # -i any: 监听所有网卡 # -s 0: 抓取完整数据包 # -w: 保存到文件便于用 Wireshark 图形化分析运行此命令后让客户端复现一次超时请求然后CtrlC停止抓包。用tcpdump -r client_to_nginx.pcap简单查看或下载到本地用 Wireshark 分析。场景二怀疑是 Nginx 到后端应用的问题在 Nginx 服务器上抓取它向后端服务器如10.0.0.200:8080发起的流量。sudo tcpdump -i any -s 0 -w nginx_to_upstream.pcap host 10.0.0.200 and port 8080场景三不确定流量方向需要同时抓取进出 Nginx 的流量这能帮你完整看到一个请求的生命周期。# 假设 Nginx 服务器IP是 10.0.0.99监听80后端是 10.0.0.200:8080 sudo tcpdump -i any -s 0 -w full_trace.pcap (port 80) or (host 10.0.0.200 and port 8080)2.2 分析抓包文件寻找超时线索用 Wireshark 打开.pcap文件分析更直观。重点关注以下几点TCP 握手是否成功看有没有完整的SYN-SYN-ACK-ACK。如果只有SYN重传说明网络不通或对端端口未监听。请求发出后是否有响应找到 HTTPGET/POST请求包看后面是否有ACK确认以及是否在合理时间内收到了 HTTP 响应200 OK或其他状态码。如果请求发出后只有零星的ACK长时间没有响应数据问题可能在后端应用处理慢。是否存在 TCP 重传Wireshark 会用黑色背景标记重传包。大量重传意味着网络丢包严重会导致应用层超时。检查Expert Information查看重传统计。是否存在“零窗口” (Zero Window) 如果接收方可能是 Nginx 或后端通告窗口大小为 0表示其缓冲区已满无法接收更多数据发送方就会暂停。这通常意味着接收方应用处理不过来数据堆积在了 TCP 缓冲区。连接是否被正常关闭超时后连接是被RST(重置) 断开的还是最终完成了FIN挥手RST通常表示应用层发生了错误。一个典型超时场景的 tcpdump 分析你可能会看到这样的序列客户端发送[SYN]。Nginx 回复[SYN, ACK]。客户端发送[ACK]。三次握手完成。客户端发送HTTP POST /api/list(携带数据)。Nginx 对此POST包回复[ACK]。此后长达 30 秒你的超时时间内没有任何数据包。30 秒后客户端发送[FIN, ACK]主动关闭连接。这个序列清晰地表明Nginx 已经收到了客户端的完整请求并回复了 ACK但在向后端应用转发或等待后端响应时卡住了。问题范围从“客户端到 Nginx”缩小到了“Nginx 到后端”或“后端应用内部”。3. 第三步使用 eBPF 深入内核与应用层如果 tcpdump 显示 TCP 连接建立成功请求也送达了但响应迟迟不来那么问题很可能在内核态的系统调用阻塞或应用层代码逻辑上。这时eBPF 就该上场了。3.1 追踪系统调用耗时execsnoop与opensnoop偶发超时有时是因为某个平时很快的系统调用如执行外部命令、打开文件在特定情况下变慢了。execsnoop: 追踪所有exec()系列系统调用即新进程的创建。如果超时请求触发了一个你没预料到的外部命令如curl,convert并且这个命令挂起了就会导致超时。sudo execsnoop # 在复现超时期间运行观察是否有异常进程被创建及其耗时。opensnoop: 追踪所有open()系统调用显示进程打开的文件路径、返回的文件描述符(FD)和耗时。如果应用在请求处理中需要打开一个网络锁文件 (/var/lock/xxx)、一个缓慢的 NFS 共享文件或一个已经满的日志文件open调用可能会阻塞。sudo opensnoop # 重点关注耗时TIME_MS 列异常长的记录。3.2 追踪 TCP 连接生命周期tcplifetcplife可以展示 TCP 会话的完整生命周期包括本地和远端地址、端口、传输字节数以及连接持续时间。这对于发现异常的长连接非常有用。sudo tcplife # 输出示例 # PID COMM LADDR LPORT RADDR RPORT TX_KB RX_KB MS # 12345 nginx 10.0.0.99 80 10.0.0.200 8080 5 120 30001上面示例中最后一列MS是连接持续时间毫秒。如果发现某个到后端10.0.0.200:8080的连接持续了 30001 毫秒30秒正好匹配你的超时时间那么这个连接就是嫌疑犯。结合 PID你可以进一步用strace或perf分析这个特定的 Nginx 工作进程。3.3 编写定制化 bpftrace 脚本追踪应用层逻辑BCC 工具是通用的有时我们需要更精确的追踪。bpftrace允许我们编写灵活的脚本。例如我们可以追踪某个特定后端应用比如一个 Java 进程的read()和write()系统调用在某个文件描述符上的耗时。假设我们通过lsof或ss命令发现 Nginx 的后端连接对应的文件描述符是fd25进程 PID 是8888。我们可以编写一个bpftrace脚本 (trace_slow_io.bt)#!/usr/bin/bpftrace tracepoint:syscalls:sys_enter_read, tracepoint:syscalls:sys_enter_write /pid 8888 args-fd 25/ { start[tid] nsecs; } tracepoint:syscalls:sys_exit_read, tracepoint:syscalls:sys_exit_write /pid 8888 args-fd 25 start[tid]/ { $duration_ms (nsecs - start[tid]) / 1000000; if ($duration_ms 100) { // 只打印耗时超过100毫秒的IO printf(%s fd%d, ret%d, duration%d ms\n, probe, args-fd, args-ret, $duration_ms); } delete(start[tid]); }运行脚本sudo bpftrace trace_slow_io.bt。当超时发生时如果是因为这个文件描述符即连接到后端的 socket上的read等待后端响应阻塞了数十秒这个脚本就能捕捉到。注意eBPF 脚本功能强大但需要一定的内核和编程知识。在生产环境使用前务必在测试环境充分验证。4. 第四步综合分析与常见根因排查清单结合 tcpdump 和 eBPF 的发现我们可以将问题归因并对照以下清单进行排查。4.1 根因分类与排查方向排查工具线索可能根因下一步行动tcpdump 显示大量 TCP 重传客户端与 Nginx或 Nginx 与后端之间的网络丢包。1. 检查网络设备交换机、防火墙状态、错误计数。2. 使用ping -f(洪水ping) 或mtr测试链路稳定性。3. 检查双方网卡状态 (ethtool 网卡名)关注drops,errors。tcpdump 显示 TCP 零窗口接收方Nginx 或后端应用处理不过来TCP 接收缓冲区满。1. 检查接收方服务器的 CPU、内存、IO 使用率 (top,vmstat 1)。2. 检查接收方应用是否有阻塞操作如同步锁、慢查询、Full GC。3. 考虑调整 TCP 内核参数net.ipv4.tcp_rmem(但需谨慎)。tcpdump 显示请求已送达后端但无响应后端应用处理超时。Nginx 在等待proxy_read_timeout。1.重点排查后端检查应用日志、线程堆栈 (jstack,pstack)、数据库慢查询。2. 检查后端是否在等待外部依赖如另一个微服务、Redis、数据库锁。3. 使用 eBPF 的offcputime或profile工具分析后端进程为何不占用 CPU可能在等待锁或IO。eBPF 发现open()调用慢应用在访问慢速存储如 NFS、已满的磁盘、损坏的 inode或等待文件锁。1. 检查磁盘 IO 使用率 (iostat -x 1)。2. 检查opensnoop输出的慢路径对应的文件系统。3. 检查是否有其他进程持有文件锁 (lsof,fuser)。eBPF 发现connect()调用慢后端应用在连接其下游服务如数据库时DNS 解析慢或下游服务端口响应慢。1. 检查/etc/resolv.conf和 DNS 服务器响应时间 (dig,nslookup)。2. 在后端应用服务器上使用telnet或nc测试连接下游服务的端口是否通畅、延迟如何。3. 考虑使用 IP 直连或优化 DNS 缓存。tcplife显示连接存活时间极长连接泄漏或长事务。连接建立后未被正常关闭占用资源。1. 检查后端应用和数据库的连接池配置最大连接数、空闲超时。2. 检查代码中是否有未正确关闭的数据库连接或 HTTP 客户端。3. 使用ss -t或netstat查看 TIME_WAIT 或 CLOSE_WAIT 状态的连接数是否异常。4.2 Nginx 配置相关检查点虽然问题可能不在 Nginx但其配置会影响超时行为务必检查proxy_connect_timeout、proxy_send_timeout、proxy_read_timeout这三个是核心超时设置分别控制连接后端、发送请求、读取响应的超时。确保它们设置合理如60s并且大于后端应用的实际最大处理时间。keepalive配置upstream块中的keepalive指令用于维持到后端的长连接。如果设置过小或后端不支持频繁建连会导致超时。同时检查后端的keepalive_timeout。负载均衡与健康检查如果upstream中某个后端节点响应变慢但未标记为“down”Nginx 仍可能将请求分发过去导致偶发超时。检查max_fails和fail_timeout配置。缓冲区 (proxy_buffering): 如果proxy_buffering为on默认Nginx 会先缓冲后端响应再发给客户端。如果响应很大而缓冲区 (proxy_buffer_size,proxy_buffers) 设置过小可能引起额外 IO 操作。对于流式响应或大文件可能需要关闭缓冲。4.3 系统资源与内核参数文件描述符限制使用ulimit -n检查 Nginx 进程和后端应用进程的可用文件描述符数量。如果耗尽新的连接将无法建立。本地端口范围当 Nginx 作为客户端连接后端时会使用本地临时端口。如果并发连接数很高可能耗尽端口。检查net.ipv4.ip_local_port_range。TCP 半连接队列与全连接队列如果瞬间并发连接数巨大可能导致 SYN 包被丢弃。检查net.core.somaxconn和 Nginxlisten指令的backlog参数。系统内存与 Swap内存不足导致频繁 Swap会使整个系统响应变慢。监控free -h和sar -B。5. 第五步构建可持续的监控与告警解决一次偶发超时后更重要的是建立机制防止类似问题再次发生或能快速定位。完善 Nginx 日志如前所述确保访问日志包含$request_time和$upstream_response_time。可以设置一个日志分析规则当这两个时间超过阈值如 5 秒时触发告警。应用链路追踪引入 APM (Application Performance Monitoring) 工具如 SkyWalking, Pinpoint, Jaeger。它们能自动记录跨进程调用的耗时直观展示是哪个环节如某个数据库查询或远程调用导致了延迟。关键指标监控网络服务器网卡的丢包率、错包率。TCPss -ti显示的每个连接的retrans重传计数。系统CPU 软中断 (si) 占比、磁盘 IO 等待时间 (await)、TCP 连接状态数量。应用关键接口的 P99/P999 响应时间、错误率。定期压测与混沌工程在测试环境定期进行压力测试并模拟网络延迟、丢包、下游服务慢响应等故障验证系统的弹性和监控告警的有效性。排查“无报错偶发超时”这类问题本质上是一个分层假设与验证的过程。从最外层的网络包开始用 tcpdump 验证传输层再深入到系统调用和内核事件用 eBPF 验证资源与调度最后结合应用日志和代码逻辑找到最终根因。掌握这个流程和工具链下次再遇到类似的“幽灵问题”你就能有条不紊地把它揪出来。记住日志只是观察系统的一个窗口当这个窗口看不到问题时你需要自己打开更多的窗口。