这是一位同事写的笔记,里面用到一些抓包命令,感觉不错,分享给大家
业务反应偶尔出现访问比较慢,出现landing状态,检查业务发现有一定概率浏览器返回504 gateway timeout (504错误代表网关超时 (Gateway timeout),是指服务器作为网关或代理,但是没有及时从上游服务器收到请求)。
整体的服务架构是lvs->nginx->golang,其中nginx和golang的服务不在同一台机器。登录nginx的服务器,在日志中发现不少504的请求,golang服务超过60秒没有应答:
504的请求,golang服务超过60秒没有应答
对日志进行统计发现504比例比较常高,并且分散到各种接口上都有,对mysql,redis等服务检查发现各项指标正常,因此初步排除了是某项业务导致的接口超时。怀疑是nginx服务器和golang服务器之间网络原因造成的。在其他各个机房进行访问测试,发现都有一定概率发生超时。
通过ss查看到超时时的链接一直处于syn-send的状态,没有收到ack应答报文:
通过ss查看到超时时的链接一直处于syn-send的状态,没有收到ack应答报文
通过tcpdump也同样观察到只发送syn报文,但是一直没收到ack报文。并且分别在超时的2s,4s,8s后进行超时重试,直至最终超时都没有收到对应的ack应答。这里有个疑问,大部分情况是不会出现这种情况,证明两台服务器的链路应该是有至少一条通路。为什么在对syn报文进行重发的时候一直不成功。tcp协议中的两个报文是完全可能从不同的路由发送到目的ip的,这是协议允许的。
通过ping命令和mtr命令检查到目的端口的网络丢包情况,都没有发现丢包。由于ping操作走的是icmp协议,工作在网络层,而tcp协议是传输层协议。通过ping发现没有丢包,只能证明两个网络连通是正常的,并不代表tcp的的报文也会正常,但能证明链路没有问题。
ping
traceroute
在docker服务器中抓包发现确实没有收到client的syn包,因此也就没有了应答。怀疑是宿主机与容器之间出现问题。
联系容器服务的同事,让他们协助抓取宿主机和容器服务的报文。在client机器上,我们可以看到一个seq为1174736498, ack为3190591930的请求后,重新发起了一个seq为1068498692的syn报文,之后一直没有收到ack报文,进行多次重试后仍未收到应答。
seq=1068498692的syn报文
宿主机上的抓包显示最后一个报文的seq为1174736498
wireshark
容器服务器上抓包显示最后一个报文的seq也为1174736498
wireshark
宿主机和容器都对没有收到seq为1068498692的syn报文,基本排除了宿主机的问题。对宿主机中运行的其他docker服务进行测试,发现也存在相同的问题。因此怀疑报文是在路由中丢失,一直没有到达宿主机。
通过traceroute 10.198.134.183 发现每次的路由结果都不一样。而其他没有这种现象的机器路由表则是稳定的。内网环境下网络环境比较简单,路由器状态相对稳定,路由表应该相对固定才对,这种经常变得情况下应该是有问题。联系ops同事协助定位问题,最终确认是路由上的问题。由于有一台交换机故障,导致有一定概率形成环路,导致报文无法送达。
traceroute
tcp报文重发也一直收不到的疑惑也随着ops的解答而得到解决。交换机配置的hash模式是根据五元组进行hash,由于重发还是同一个端口,导致重发的链路是固定不变的,一直使用之前的链路。重新发起请求时,使用了新的端口,就有一定概率使用另一条链路。而ping命令使用的是icmp协议,没有端口的概念,因此一直是一条固定不变的链路。因此ping能证明两台服务器是连通的,但是不能证明tcp报文一定可以到达。