我们这次看一个丢包问题的分析方法

丢包是指网络数据的收发过程中,由于各种原因,数据包还没到应用程序,就被丢弃了,被丢包的数量除以总的传输包数,就是丢包率

我们今天,就拿着Nginx进行举例

hping3和curl是Nginx的客户端

图片

我们进行启动一个Docket镜像

在容器启动之后,尝试ping对应的Nginx服务器

记性相关的hping3命令,在其中,我们使用的是TCP命令进行的测试,而不是ICMP的ping命令

获取输出如下

# -c表示发送10个请求,-S表示使用TCP SYN,-p指定端口为80

$ hping3 -c 10 -S -p 80 192.168.0.30

HPING 192.168.0.30 (eth0 192.168.0.30): S set, 40 headers + 0 data bytes

len=44 ip=192.168.0.30 ttl=63 DF id=0 sport=80 flags=SA seq=3 win=5120 rtt=7.5 ms

len=44 ip=192.168.0.30 ttl=63 DF id=0 sport=80 flags=SA seq=4 win=5120 rtt=7.4 ms

len=44 ip=192.168.0.30 ttl=63 DF id=0 sport=80 flags=SA seq=5 win=5120 rtt=3.3 ms

len=44 ip=192.168.0.30 ttl=63 DF id=0 sport=80 flags=SA seq=7 win=5120 rtt=3.0 ms

len=44 ip=192.168.0.30 ttl=63 DF id=0 sport=80 flags=SA seq=6 win=5120 rtt=3027.2 ms

— 192.168.0.30 hping statistic —

10 packets transmitted, 5 packets received, 50% packet loss

round-trip min/avg/max = 3.0/609.7/3027.2 ms

hping的输出中,可以发现有10个请求包,只有5个回复,50%的包都丢失了

那么,我们就需要分析,哪里出现了丢包的问题,对于网络协议栈,从下到上的不同层次都有着丢包问题的可能性

接下来,我们就根据网络协议栈中丢包的可能性,列出如下的一张图

图片

从最下层的,黑盒的网络传输来看起

网卡收包之后,环形缓冲区可能会溢出

链路层有QOS导致丢弃

IP层可能有路由失败 MTU太大的问题

传输层有着,端口未监听资源占用太大的问题

套接字层,有着套接字缓冲区溢出的问题

应用层有着应用程序异常导致的丢包问题

我们假设VM1和VM2本机没有问题,而是直接看容器相关的问题

我们直接进入容器内部,根据协议栈,逐步排查问题

首先是链路层

首先是最底下的链路层,可能存在缓冲区溢出的问题,但是如果出现了溢出的问题,Linux会在网卡收发的过程中,记录下错误次数

可以通过ethtool 或者 netstat来查看网卡丢包情况,比如如下的查看

root@nginx:/# netstat -i

Kernel Interface table

Iface      MTU    RX-OK RX-ERR RX-DRP RX-OVR    TX-OK TX-ERR TX-DRP TX-OVR Flg

eth0       100       31      0      0 0             8      0      0      0 BMRU

lo       65536        0      0      0 0             0      0      0      0 LRU

输出中的RX-OK RX-ERR RX-DRP RX-OVR

分别表示接受总包数 总错误包数,进入Ring Buffer后因为其他原因导致的丢包数,RingBuffer溢出导致的丢包数

在输出中,没有任何的错误,说明容器的虚拟网卡没有丢包

不过要注意,如果要tc等工具配置QoS,那么导致的丢包,就不会统计到这里面

我们就需要查看是否配置了tc规则,是否有丢包

root@nginx:/# tc -s qdisc show dev eth0

qdisc netem 800d: root refcnt 2 limit 1000 loss 30%

Sent 432 bytes 8 pkt (dropped 4, overlimits 0 requeues 0)

backlog 0b 0p requeues 0

在tc的输出中,我们看出来了,eth0配置了一个网络模拟排队规则,配置了丢包率为30%

那么我们直接删除掉 netem模块就可以了,执行如下的命令,删除tc中的netem模块

root@nginx:/# tc qdisc del dev eth0 root netem loss 30%

删除后继续执行刚刚的命令,看看还有没有问题

$ hping3 -c 10 -S -p 80 192.168.0.30

HPING 192.168.0.30 (eth0 192.168.0.30): S set, 40 headers + 0 data bytes

len=44 ip=192.168.0.30 ttl=63 DF id=0 sport=80 flags=SA seq=0 win=5120 rtt=7.9 ms

len=44 ip=192.168.0.30 ttl=63 DF id=0 sport=80 flags=SA seq=2 win=5120 rtt=1003.8 ms

len=44 ip=192.168.0.30 ttl=63 DF id=0 sport=80 flags=SA seq=5 win=5120 rtt=7.6 ms

len=44 ip=192.168.0.30 ttl=63 DF id=0 sport=80 flags=SA seq=6 win=5120 rtt=7.4 ms

len=44 ip=192.168.0.30 ttl=63 DF id=0 sport=80 flags=SA seq=9 win=5120 rtt=3.0 ms

— 192.168.0.30 hping statistic —

10 packets transmitted, 5 packets received, 50% packet loss

round-trip min/avg/max = 3.0/205.9/1003.8 ms

从hping3的输出中,可以看出,还是一如既往的丢包,RTT的波动也很大

那么,链路层已经排查完了,我们看下网络层和传输层的问题

在容器内部,查看netstat -s 命令,查看协议的收发汇总,以及错误信息

root@nginx:/# netstat -s

Ip:

Forwarding: 1          //开启转发

31 total packets received    //总收包数

0 forwarded            //转发包数

0 incoming packets discarded  //接收丢包数

25 incoming packets delivered  //接收的数据包数

15 requests sent out      //发出的数据包数

Icmp:

0 ICMP messages received    //收到的ICMP包数

0 input ICMP message failed    //收到ICMP失败数

ICMP input histogram:

0 ICMP messages sent      //ICMP发送数

0 ICMP messages failed      //ICMP失败数

ICMP output histogram:

Tcp:

0 active connection openings  //主动连接数

0 passive connection openings  //被动连接数

11 failed connection attempts  //失败连接尝试数

0 connection resets received  //接收的连接重置数

0 connections established    //建立连接数

25 segments received      //已接收报文数

21 segments sent out      //已发送报文数

4 segments retransmitted    //重传报文数

0 bad segments received      //错误报文数

0 resets sent          //发出的连接重置数

Udp:

0 packets received

TcpExt:

11 resets received for embryonic SYN_RECV sockets  //半连接重置数

0 packet headers predicted

TCPTimeouts: 7    //超时数

TCPSynRetrans: 4  //SYN重传数

netstat汇总了IP ICMP TCP UDP的收发信息

我们观察错误数 丢包数 重传数

对于TCP协议,我们发生了丢包和重传,分别是

11次的失败重传

4次重传

11次半连接重置

4次SYN重传

7次超时

那么TCP出现相对的错误,我们就考虑从TCP入手解决

对于TCP可能出现的问题,首先该考虑的是iptables导致的内核丢包的问题

我们首先查看iptables的配置规则,其中包含了filter nat mangle raw,每个表又包含一系列的链,用于对iptables规则进行分组管理

丢包问题自然要和filter进行队列,我们就要查看filter汇总有没有DROP 和 Reject的规则

我们先查看iptable的相关统计信息

我们可以使用Iptables -nvl,查看其统计信息,

# 在主机中执行

$ 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规则的统计数值,分别在INPUT和OUTPUT的链中,统计的规则是一样的,进行30%的随机丢包

而0.0.0.0匹配所有的源IP和目的IP,导致这条的生效

我们可以进行直接的删除规则

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命令,查看是否正常

$ hping3 -c 10 -S -p 80 192.168.0.30

HPING 192.168.0.30 (eth0 192.168.0.30): S set, 40 headers + 0 data bytes

len=44 ip=192.168.0.30 ttl=63 DF id=0 sport=80 flags=SA seq=0 win=5120 rtt=11.9 ms

len=44 ip=192.168.0.30 ttl=63 DF id=0 sport=80 flags=SA seq=1 win=5120 rtt=7.8 ms

len=44 ip=192.168.0.30 ttl=63 DF id=0 sport=80 flags=SA seq=9 win=5120 rtt=15.0 ms

— 192.168.0.30 hping statistic —

10 packets transmitted, 10 packets received, 0% packet loss

round-trip min/avg/max = 3.3/7.9/15.0 ms

接下来,我们校验一下,在此之后Nginx能不能正常响应HTTP请求了

进行curl命令,检查其响应

可能会发现Nginx没法响应,而tcp的连接还可以正常响应

那么我们需要考虑使用tcpdump进行相关的抓取操作

root@nginx:/# tcpdump -i eth0 -nn port 80

tcpdump: verbose output suppressed, use -v or -vv for full protocol decode

listening on eth0, link-type EN10MB (Ethernet), capture size 262144 bytes

查看tcp的输出

在tcp的三次握手中,前三次没有问题,第四次进行了客户端的连接关闭

14:40:00.589235 IP 10.255.255.5.39058 > 172.17.0.2.80: Flags [S], seq 332257715, win 29200, options [mss 1418,sackOK,TS val 486800541 ecr 0,nop,wscale 7], length 0

14:40:00.589277 IP 172.17.0.2.80 > 10.255.255.5.39058: Flags [S.], seq 1630206251, ack 332257716, win 4880, options [mss 256,sackOK,TS val 2509376001 ecr 486800541,nop,wscale 7], length 0

14:40:00.589894 IP 10.255.255.5.39058 > 172.17.0.2.80: Flags [.], ack 1, win 229, options [nop,nop,TS val 486800541 ecr 2509376001], length 0

14:40:03.589352 IP 10.255.255.5.39058 > 172.17.0.2.80: Flags [F.], seq 76, ack 1, win 229, options [nop,nop,TS val 486803541 ecr 2509376001], length 0

14:40:03.589417 IP 172.17.0.2.80 > 10.255.255.5.39058: Flags [.], ack 1, win 40, options [nop,nop,TS val 2509379001 ecr 486800541,nop,nop,sack 1 {76:77}], length 0

而第三个包和第四个包之间的,存在3秒的延迟,这是因为curl的重置

也就是握手后,发送HTTP GET请求失败了

关于这一个观点的论证,可以重新执行netstat -i 观察网卡有没有丢包

那么我们考虑一下SYN包可以接受,但是TCP包被拒绝了,ip端口一致,是否是MTU配置导致的呢?

因为MTU限制了,导致SYN包能通过,HTTP请求包被拒绝了

我们将MTU设置为1500,进行了相关的命令

这样就解决了这个丢包问题

发表评论

邮箱地址不会被公开。 必填项已用*标注