48 | 案例篇:服务器总是时不时丢包,我该怎么办?(下)
下载APP
关闭
渠道合作
推荐作者
48 | 案例篇:服务器总是时不时丢包,我该怎么办?(下)
2019-03-15 倪朋飞 来自北京
《Linux性能优化实战》
课程介绍
讲述:冯永吉
时长07:49大小7.14M
你好,我是倪朋飞。
上一节,我们一起学习了如何分析网络丢包的问题,特别是从链路层、网络层以及传输层等主要的协议栈中进行分析。
不过,通过前面这几层的分析,我们还是没有找出最终的性能瓶颈。看来,还是要继续深挖才可以。今天,我们就来继续分析这个未果的案例。
在开始下面的内容前,你可以先回忆一下上节课的内容,并且自己动脑想一想,除了我们提到的链路层、网络层以及传输层之外,还有哪些潜在问题可能会导致丢包呢?
iptables
首先我们要知道,除了网络层和传输层的各种协议,iptables 和内核的连接跟踪机制也可能会导致丢包。所以,这也是发生丢包问题时,我们必须要排查的一个因素。
不过,由于连接跟踪在 Linux 内核中是全局的(不属于网络命名空间),我们需要退出容器终端,回到主机中来查看。
你可以在容器终端中,执行 exit ;然后执行下面的命令,查看连接跟踪数:
从这儿你可以看到,连接跟踪数只有 182,而最大连接跟踪数则是 262144。显然,这里的丢包,不可能是连接跟踪导致的。
接着,再来看 iptables。回顾一下 iptables 的原理,它基于 Netfilter 框架,通过一系列的规则,对网络数据包进行过滤(如防火墙)和修改(如 NAT)。
这些 iptables 规则,统一管理在一系列的表中,包括 filter(用于过滤)、nat(用于 NAT)、mangle(用于修改分组数据) 和 raw(用于原始数据包)等。而每张表又可以包括一系列的链,用于对 iptables 规则进行分组管理。
对于丢包问题来说,最大的可能就是被 filter 表中的规则给丢弃了。要弄清楚这一点,就需要我们确认,那些目标为 DROP 和 REJECT 等会弃包的规则,有没有被执行到。
你可以把所有的 iptables 规则列出来,根据收发包的特点,跟 iptables 规则进行匹配。不过显然,如果 iptables 规则比较多,这样做的效率就会很低。
当然,更简单的方法,就是直接查询 DROP 和 REJECT 等规则的统计信息,看看是否为 0。如果统计值不是 0 ,再把相关的规则拎出来进行分析。
我们可以通过 iptables -nvL 命令,查看各条规则的统计信息。比如,你可以执行下面的 docker exec 命令,进入容器终端;然后再执行下面的 iptables 命令,就可以看到 filter 表的统计数据了:
从 iptables 的输出中,你可以看到,两条 DROP 规则的统计数值不是 0,它们分别在 INPUT 和 OUTPUT 链中。这两条规则实际上是一样的,指的是使用 statistic 模块,进行随机 30% 的丢包。
再观察一下它们的匹配规则。0.0.0.0/0 表示匹配所有的源 IP 和目的 IP,也就是会对所有包都进行随机 30% 的丢包。看起来,这应该就是导致部分丢包的“罪魁祸首”了。
既然找出了原因,接下来的优化就比较简单了。比如,把这两条规则直接删除就可以了。我们可以在容器终端中,执行下面的两条 iptables 命令,删除这两条 DROP 规则:
删除后,问题是否就被解决了呢?我们可以切换到终端二中,重新执行刚才的 hping3 命令,看看现在是否正常:
这次输出你可以看到,现在已经没有丢包了,并且延迟的波动变化也很小。看来,丢包问题应该已经解决了。
不过,到目前为止,我们一直使用的 hping3 工具,只能验证案例 Nginx 的 80 端口处于正常监听状态,却还没有访问 Nginx 的 HTTP 服务。所以,不要匆忙下结论结束这次优化,我们还需要进一步确认,Nginx 能不能正常响应 HTTP 请求。
我们继续在终端二中,执行如下的 curl 命令,检查 Nginx 对 HTTP 请求的响应:
从 curl 的输出中,你可以发现,这次连接超时了。可是,刚才我们明明用 hping3 验证了端口正常,现在却发现 HTTP 连接超时,是不是因为 Nginx 突然异常退出了呢?
不妨再次运行 hping3 来确认一下:
奇怪,hping3 的结果显示,Nginx 的 80 端口确确实实还是正常状态。这该如何是好呢?别忘了,我们还有个大杀器——抓包操作。看来有必要抓包看看了。
tcpdump
接下来,我们切换回终端一,在容器终端中,执行下面的 tcpdump 命令,抓取 80 端口的包:
然后,切换到终端二中,再次执行前面的 curl 命令:
等到 curl 命令结束后,再次切换回终端一,查看 tcpdump 的输出:
经过这么一系列的操作,从 tcpdump 的输出中,我们就可以看到:
前三个包是正常的 TCP 三次握手,这没问题;
但第四个包却是在 3 秒以后了,并且还是客户端(VM2)发送过来的 FIN 包,也就说明,客户端的连接关闭了。
我想,根据 curl 设置的 3 秒超时选项,你应该能猜到,这是因为 curl 命令超时后退出了。
我把这一过程,用 TCP 交互的流程图(实际上来自 Wireshark 的 Flow Graph)来表示,你可以更清楚地看到上面这个问题:
这里比较奇怪的是,我们并没有抓取到 curl 发来的 HTTP GET 请求。那么,究竟是网卡丢包了,还是客户端压根儿就没发过来呢?
我们可以重新执行 netstat -i 命令,确认一下网卡有没有丢包问题:
从 netstat 的输出中,你可以看到,接收丢包数(RX-DRP)是 344,果然是在网卡接收时丢包了。不过问题也来了,为什么刚才用 hping3 时不丢包,现在换成 GET 就收不到了呢?
还是那句话,遇到搞不懂的现象,不妨先去查查工具和方法的原理。我们可以对比一下这两个工具:
hping3 实际上只发送了 SYN 包;
而 curl 在发送 SYN 包后,还会发送 HTTP GET 请求。
HTTP GET ,本质上也是一个 TCP 包,但跟 SYN 包相比,它还携带了 HTTP GET 的数据。
那么,通过这个对比,你应该想到了,这可能是 MTU 配置错误导致的。为什么呢?
其实,仔细观察上面 netstat 的输出界面,第二列正是每个网卡的 MTU 值。eth0 的 MTU 只有 100,而以太网的 MTU 默认值是 1500,这个 100 就显得太小了。
当然,MTU 问题是很好解决的,把它改成 1500 就可以了。我们继续在容器终端中,执行下面的命令,把容器 eth0 的 MTU 改成 1500:
修改完成后,再切换到终端二中,再次执行 curl 命令,确认问题是否真的解决了:
非常不容易呀,这次终于看到了熟悉的 Nginx 响应,说明丢包的问题终于彻底解决了。
当然,案例结束前,不要忘记停止今天的 Nginx 应用。你可以切换回终端一,在容器终端中执行 exit 命令,退出容器终端:
最后,再执行下面的 docker 命令,停止并删除 Nginx 容器:
小结
今天,我继续带你分析了网络丢包的问题。特别是在时不时丢包的情况下,定位和优化都需要我们花心思重点投入。
网络丢包问题的严重性不言而喻。碰到丢包问题时,我们还是要从 Linux 网络收发的流程入手,结合 TCP/IP 协议栈的原理来逐层分析。
思考
最后,我想邀请你一起来聊聊,你碰到过的网络丢包问题。你是怎么分析它们的根源?又是怎么解决的?你可以结合我的讲解,总结自己的思路。
欢迎在留言区和我讨论,也欢迎把这篇文章分享给你的同事、朋友。我们一起在实战中演练,在交流中进步。
分享给需要的人,Ta购买本课程,你将得20元
生成海报并分享
赞 16
提建议
© 版权归极客邦科技所有,未经许可不得传播售卖。 页面已增加防盗追踪,如有侵权极客邦将依法追究其法律责任。
上一篇
47 | 案例篇:服务器总是时不时丢包,我该怎么办?(上)
下一篇
49 | 案例篇:内核线程 CPU 利用率太高,我该怎么办?
精选留言(23)
- 怀特2019-03-15有一个问题不明白:IP包不是可以根据网络自动组合和拆分的吗?为什么会直接丢弃呢?
作者回复: 也可以设置不允许拆包
19 - 佳2019-03-15我在用k8s中过netfilter的招。通过service cluster访问pod,发现包到了pod。被丢弃了,tcp超时。抓包发现目的地址没有修改为pod ip.安装脚本没有配置centos开机加载netfilter驱动
作者回复: 👍谢谢分享
共 3 条评论15 - 大坏狐狸2019-04-12学到这,突然有种这个订阅是我职业生涯中做的一件很正确的事情了的感觉。
作者回复: 😊
12 - kissingers2019-03-15还有传输设备引入的丢包,比如接口模式不匹配,物理接口或线缆,广播风暴大流量等。另外案例这里如果get 包允许分片那就不会丢包吧?只是传输效率低。那么既然允许分片可以规避中间链路mtu 过小引起的问题,为什么很多应用默认就是不允许分片呢?谢谢
作者回复: 分片带来的成本还是蛮高的
共 2 条评论12 - 麦小旭2019-08-29老师关于mtu100的问题,我想问下在三次握手的时候不是会协商mtu窗口的大小吗,容器的mtu明明是100为什么容器返回给客户端的ack包的mss值是256?共 2 条评论9
- ninuxer2019-03-15打卡day51 知识没有融会贯通,我能想到iptables的问题,也能想到抓包分析,但是后面定位到mtu的问题,我估计只能凭灵感了,思维不能马上跟这个产生关联9
- Spoon2021-01-02用tcpdump抓包发现 1.只有length(tcpdump输出,表示TCP playload值为0)为0的tcp包才能被走到tcp协议层,IP头20字节,TCP头40字节加起来就60字节了 2.在client端抓包发现tcp playload为76字节,加上IP和TCP头肯定是超过100字节的MTU 3.可以做个试验将MTU设置为137字节3
- 王聪 Claire2019-06-221. 不应该是0.2向0.30发送请求吗?为什么wireshark的图SYN是0.30发给0.2的?2. MTU过小,是因为要进行大量的数据分片分包,所以导致服务器端接收不到curl http get请求吗?谢谢。
作者回复: 1. 谢谢指出,wireshark的图片标错了 2. 不是的,是因为容器的eth0实际上只是veth pair,不会分包
2 - 挺直腰板2019-05-24老师,跟踪到内核函数,但不清楚任何函数是做什么的,怎么查?
作者回复: 查内核源码
3 - Huayra2019-03-16针对这问题,tcpdump效率太低了,倒不如使用系统动态追踪或者pcap技术来实现这么一个专门的工具
作者回复: 嗯嗯,好主意
3 - AceslupK2021-10-15老师,47篇也执行过netstat -i,为啥那个时候没注意MTU值呢?1
- 无名老卒2019-05-04iptables我后面是想到了,但是mtu没有想到。有一个疑问,为什么说mtu值等于100是太小了,我测试过这个案例,当mtu值小于127值就会出现异常,等于以及大于127这个值就是正常的,倪老师,这个可以解释一下吗?共 1 条评论1
- 如果2019-04-15DAY48,打卡1
- 西红柿牛腩2019-03-15好玩,抽空要把Netfilter好好玩一遍1
- moooofly2022-02-21什么情况/条件/设置下,系统收发包的时候会按照 mtu 值进行分片呢?
- 小袁2020-03-15定位问题的思路太清晰了,没有一点碰运气的成分。
- 记事本2019-12-05老师好,有个问题想问下,服务端发送了两组数据包,我用txpdump抓包然后wireshark看到的那两组包在一个tcp包里,这个是协议栈组在一起的吗?另外这个组过包我调用recv接口接收的时候只有第一包的数据 是怎么回事?感谢老师,期待您的回复。
- 美美2019-10-10为什么最后一次握手的时候ack=1而不是seq+1呢?
- 深海极光2019-06-12老师最近在查线上问题时,发现服务在tcp层有丢包,通过netstat - s可以看到,具体如下: 32474 passive connections rejected because of time stamp 71707 packets rejects in established connections because of timestamp 查看机器配置,也确实是开启了tcp timestamp和recycle,都为1,但是这个是通过nat访问才回有这个问题吧,我们是nginx直接打给我的网关服务的,理论上不回有放弃链接啥,还请老师解答,谢谢了展开
- fran7122019-03-20曾经被跟踪表坑过,索性就 # cat /etc/modprobe.d/conntrack.conf install nf_conntrack /bin/false
作者回复: 嗯,这是直接禁止掉了