性能测试分析案例-定位服务器丢包

环境准备

预先安装 docker、curl、hping3 等工具,如 apt install docker.io curl hping3。

操作和分析

案例是一个 Nginx 应用,如下图所示,hping3 和 curl 是 Nginx 的客户端。

在终端一中执行下面的命令,启动 Nginx 应用,并在 80 端口监听。如果一切正常,你应该可以看到如下的输出:

c 复制代码
docker run --name nginx --hostname nginx --privileged -p 80:80 -itd feisky/nginx:drop

执行 docker ps 命令,查询容器的状态,你会发现容器已经处于运行状态(Up)了:

c 复制代码
docker ps

终端二中,执行下面的 hping3 命令,进一步验证 Nginx 是不是真的可以正常访问了。

c 复制代码
hping3 -c 10 -S -p 80 xxx.xxx.xxx.xxx

发送了 10 个请求包,却只收到了 5 个回复,50% 的包都丢了。再观察每个请求的 RTT 可以发现,RTT 也有非常大的波动变化,小的时候只有 52ms,而大的时候则有1s。

发生丢包的位置,实际上贯穿了整个网络协议栈。换句话说,全程都有丢包的可能。比如我们从下往上看:

在两台 VM 连接之间,可能会发生传输失败的错误,比如网络拥塞、线路错误等;

在网卡收包后,环形缓冲区可能会因为溢出而丢包;

在链路层,可能会因为网络帧校验失败、QoS 等而丢包;

在 IP 层,可能会因为路由失败、组包大小超过 MTU 等而丢包;

在传输层,可能会因为端口未监听、资源占用超过内核限制等而丢包;

在套接字层,可能会因为套接字缓冲区溢出而丢包;

在应用层,可能会因为应用程序异常而丢包;

此外,如果配置了 iptables 规则,这些网络包也可能因为 iptables 过滤规则而丢包。

终端一,执行下面的命令,进入容器的终端中:

c 复制代码
$ docker exec -it nginx bash

链路层

当缓冲区溢出等原因导致网卡丢包时,Linux 会在网卡收发数据的统计信息中,记录下收发错误的次数。

你可以通过 ethtool 或者 netstat ,来查看网卡的丢包记录。比如,可以在容器中执行下面的命令,查看丢包情况:

c 复制代码
netstat -i

RX-OK、RX-ERR、RX-DRP、RX-OVR ,分别表示接收时的总包数、总错误数、进入 Ring Buffer 后因其他原因(如内存不足)导致的丢包数以及 Ring Buffer 溢出导致的丢包数。没有任何错误,说明容器的虚拟网卡没有丢包。

还要查看一下 eth0 上是否配置了 tc 规则,并查看有没有丢包。我们继续容器终端中,执行下面的 tc 命令,不过这次注意添加 -s 选项,以输出统计信息:

c 复制代码
tc -s qdisc show dev eth0

eth0 上面配置了一个网络模拟排队规则(qdisc netem),并且配置了丢包率为 30%(loss 30%)。再看后面的统计信息,发送了 29 个包,但是丢了 13 个。Nginx 回复的响应包,被 netem 模块给丢了。删掉 netem 模块就可以了。我们可以继续在容器终端中,执行下面的命令,删除 tc 中的 netem 模块:

c 复制代码
root@nginx:/# tc qdisc del dev eth0 root netem loss 30%

切换到终端二中,重新执行刚才的 hping3 命令,看看现在还有没有问题:

c 复制代码
hping3 -c 10 -S -p 80 xxx.xxx.xxx.xxx

还是 50% 的丢包;RTT 的波动也仍旧很大,从 3ms 到 1s。

丢包还在继续发生。不过,既然链路层已经排查完了,我们就继续向上层分析,看看网络层和传输层有没有问题。

网络层和传输层

执行下面的 netstat -s 命令,就可以看到协议的收发汇总,以及错误信息了:

c 复制代码
netstat -s
Ip:
    Forwarding: 1
    61 total packets received
    0 forwarded
    0 incoming packets discarded
    43 incoming packets delivered
    48 requests sent out
Icmp:
    0 ICMP messages received
    0 input ICMP message failed
    ICMP input histogram:
    0 ICMP messages sent
    0 ICMP messages failed
    ICMP output histogram:
Tcp:
    0 active connection openings
    2 passive connection openings
    10 failed connection attempts
    0 connection resets received
    0 connections established
    43 segments received
    64 segments sent out
    17 segments retransmitted
    0 bad segments received
    0 resets sent
Udp:
    0 packets received
    0 packets to unknown port received
    0 packet receive errors
    0 packets sent
    0 receive buffer errors
    0 send buffer errors
UdpLite:
TcpExt:
    10 resets received for embryonic SYN_RECV sockets
    1 TCP sockets finished time wait in fast timer
    4 SYNs to LISTEN sockets dropped
    0 packet headers predicted
    7 acknowledgments not containing data payload received
    3 predicted acknowledgments
    TCPSackRecovery: 1
    3 congestion windows recovered without slow start after partial ack
    TCPSackFailures: 1
    1 timeouts in loss state
    2 fast retransmits
    TCPTimeouts: 12
    TCPLossProbes: 2
    TCPDSACKRecv: 1
    TCPSpuriousRTOs: 2
    TCPSackShifted: 1
    TCPSackShiftFallback: 3
    TCPRetransFail: 3
    TCPSynRetrans: 5
    TCPOrigDataSent: 20
IpExt:
    InOctets: 2853
    OutOctets: 3642
    InNoECTPkts: 61

TCP 协议发生了丢包和重传,分别是:

10次连接失败重试(failed connection attempts)

17次重传(segments retransmitted)

10 次半连接重置(resets received for embryonic SYN_RECV sockets)

5次 SYN 重传(TCPSynRetrans)

12 次超时(TCPTimeouts)

TCP 协议有多次超时和失败重试,并且主要错误是半连接重置。换句话说,主要的失败,都是三次握手失败。

iptables

iptables 和内核的连接跟踪机制也可能会导致丢包。

c 复制代码
# 容器终端中执行exit
root@nginx:/# exit
exit

# 主机终端中查询内核配置
$ sysctl net.netfilter.nf_conntrack_max
net.netfilter.nf_conntrack_max = 65536
$ sysctl net.netfilter.nf_conntrack_count
net.netfilter.nf_conntrack_count = 14

连接跟踪数只有 14,而最大连接跟踪数则是 65536。显然,这里的丢包,不可能是连接跟踪导致的。

iptables 的原理,它基于 Netfilter 框架,通过一系列的规则,对网络数据包进行过滤(如防火墙)和修改(如 NAT)。

iptables -nvL 命令,查看各条规则的统计信息。比如,你可以执行下面的 docker exec 命令,进入容器终端;然后再执行下面的 iptables 命令,就可以看到 filter 表的统计数据了:

c 复制代码
# 在主机中执行
docker exec -it nginx bash

# 在容器中执行
root@nginx:/# iptables -t filter -nvL
Chain INPUT (policy ACCEPT 25 packets, 1000 bytes)
 pkts bytes target     prot opt in     out     source               destination
    6   240 DROP       all  --  *      *       0.0.0.0/0            0.0.0.0/0            statistic mode random probability 0.29999999981

Chain FORWARD (policy ACCEPT 0 packets, 0 bytes)
 pkts bytes target     prot opt in     out     source               destination

Chain OUTPUT (policy ACCEPT 15 packets, 660 bytes)
 pkts bytes target     prot opt in     out     source               destination
    6   264 DROP       all  --  *      *       0.0.0.0/0            0.0.0.0/0            statistic mode random probability 0.29999999981

两条 DROP 规则的统计数值不是 0,它们分别在 INPUT 和 OUTPUT 链中。这两条规则实际上是一样的,指的是使用 statistic 模块,进行随机 30% 的丢包。

接下来的优化就比较简单了。比如,把这两条规则直接删除就可以了。我们可以在容器终端中,执行下面的两条 iptables 命令,删除这两条 DROP 规则:

c 复制代码
root@nginx:/# iptables -t filter -D INPUT -m statistic --mode random --probability 0.30 -j DROP
root@nginx:/# iptables -t filter -D OUTPUT -m statistic --mode random --probability 0.30 -j DROP

执行刚才的 hping3 命令,看看现在是否正常:

c 复制代码
hping3 -c 10 -S -p 80 xxx.xxx.xxx.xxx

没有丢包了

终端一,在容器终端中执行 exit 命令,退出容器终端:

c 复制代码
root@nginx:/# exit
相关推荐
可涵不会debug17 分钟前
【Linux】信号知识三把斧——信号的产生、保存和处理
linux·运维·信号
jyan_敬言19 分钟前
【Linux】Linux命令与操作详解(一)文件管理(文件命令)、用户与用户组管理(创建、删除用户/组)
linux·运维·服务器·c语言·开发语言·汇编·c++
青云交1 小时前
大数据新视界 --大数据大厂之 Kafka 性能优化的进阶之道:应对海量数据的高效传输
大数据·数据库·人工智能·性能优化·kafka·数据压缩·分区策略·磁盘 i/o
unix2linux1 小时前
Parade Series - SHA256
linux·python·mysql·shell
学思之道1 小时前
给Linux操作系统命令取个别名
linux·运维·经验分享·学习方法
GOTXX2 小时前
应用层协议HTTP
linux·网络·网络协议·计算机网络·http·fiddler
dong_beijing2 小时前
GO语言工程构建示例-mac和linux适用
linux·运维·服务器
EterNity_TiMe_2 小时前
【机器学习】智驭未来:探索机器学习在食品生产中的革新之路
人工智能·python·机器学习·性能优化·学习方法
小小工匠2 小时前
系统安全 - Linux /Docker 安全模型及实践
linux·安全·系统安全
2201_761199043 小时前
nginx 负载均衡1
linux·运维·服务器·nginx·负载均衡