欢迎光临
我们一直在努力

容器访问应用服务不通问题排查

问题的起点

1、用户反馈同个ECS启动了多个容器,其中一个容器无法与Anytunnel 100.64.0.1通信,TCP建联失败。不通的容器IP是172.16.0.13。其他能通的容器比如 172.16.0.15。

2、在Anytunnel 100.64.0.1的后端RS上抓包,发现有172.16.0.13 TCP 建联成功的的记录。说明有其他VPC也有用172.16.013地址来访问,并且是通的。

PS:AnyTunnel地址指的是每个VPC中100.64.0.0/10内的地址,用于VPC中DNS、YUM、NTP、OSS或SLS等云服务中使用。简单可以理解为在VPC下为了解决不同VPC内网互通的特殊的SLB。这个SLB能被同个Region下所有的VPC访问。

排查过程

1、首先我们要确定VPC1下172.16.0.13的请求有没有到达AnyTunnel后端RS上,最简单的方法就是在172.16.0.13和后端RS上同时抓包进行分析。

客户端和RS上抓包通过“tcp.analysis.retransmission”条件过滤出有问题的重传报文,发都是SYN的重传。

重传报文数量都是7个,并且源端口一致,那么说明请求报文已经到达RS上,但是RS没有响应SYN-ACK,导致TCP建联失败。

2、在RS上查看TCP计数发现:

netstat -ts |grep SYNs   
3631812 SYNs to LISTEN sockets dropped

有大量tcp syn包被丢弃,数值一直在增长,进一步说明是RS收到客户端的SYN请求但是没有响应。

为何RS没有响应SYN请求分析

netstat中的计数统计,里面定义了TCP连接失败统计,具体如下:

  • resets received for embryonic SYN_RECV sockets  —syn_recv状态下,收到非重传的syn包,则返回reset

  • passive connections rejected because of time stamp —开启sysctl_tw_recycle,syn包相应连接的时间戳小于 路由中保存的时间戳;

  • failed connection attempts — syn_recv状态下,socket被关闭, 或者收到syn包(非重传)

  • times the listen queue of a socket overflowed  —收到三次握手ack包,accept队列满

  • SYNs to LISTEN sockets ignored —收到三次握手ack包,因各种原因(包括accept队列满) 创建socket失败

通过netstat -ts查看到passive connections rejected because of time stamp统计数值非常大,并且接近于SYNs to LISTEN sockets dropped的数量。

3631476 passive connections rejected because of time stamp

说明就是由于syn包相应连接的时间戳问题导致的。

于是再次分析RS的抓包文件:

报文序列

是否成功响应SYN

TimeStamp值

1

成功响应SYN

2088983548

2

没有响应SYN

271344470

3

没有响应SYN

271345544

4

没有响应SYN

271346612

5

没有响应SYN

271348509

6

没有响应SYN

271351766

7

成功响应SYN

2088993553

从抓包信息里可以看出,当后面的SYN报文的TimeStamp值小于前面成功响应的SYN报文的TimeStamp值,系统默认就会不响应该SYN请求。

通过上面分析,问题明显和tcp timestmap有关,发现tcp_tw_recycle/tcp_timestamps都开启的条件下,60秒内同一源ip主机的socket connect请求中的timestamp必须是递增的。

源码函数:tcp_v4_conn_request(),该函数是tcp层三次握手syn包的处理函数(服务端);
    源码片段:
       if (tmp_opt.saw_tstamp &&
            tcp_death_row.sysctl_tw_recycle &&
            (dst = inet_csk_route_req(sk, req)) != NULL &&
            (peer = rt_get_peer((struct rtable *)dst)) != NULL &&
            peer->v4daddr == saddr) {
            if (get_seconds() < peer->tcp_ts_stamp + TCP_PAWS_MSL &&
                (s32)(peer->tcp_ts - req->ts_recent) >
                            TCP_PAWS_WINDOW) {
                NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_PAWSPASSIVEREJECTED);
                goto drop_and_release;
            }
        }
        
tmp_opt.saw_tstamp:该socket支持tcp_timestamp
sysctl_tw_recycle:系统开启tcp_tw_recycle选项
TCP_PAWS_MSL:60s,该条件判断表示该源ip的上次tcp通讯发生在60s内
TCP_PAWS_WINDOW:1,该条件判断表示该源ip的上次tcp通讯的timestamp大于本次tcp

Tcp_timestamp 是 RFC1323 定义的优化选项,主要用于 TCP 连接中 RTT(Round Trip Time) 的计算,开启 tcp_timestamp 有利于系统计算更加准确的 RTT,也就有利于 TCP 性能的提升。

tcp_tw_recycle (Boolean; default: disabled; since Linux 2.4)
              Enable fast recycling of TIME_WAIT sockets.  Enabling this option is not recommended since this causes problems when working with NAT (Network Address
              Translation).

开启tcp_tw_recycle会启用tcp time_wait的快速回收,这个参数不建议在NAT环境中启用,它会引起相关问题。

官方定义是在NAT环境中使用会引发问题,但是归根结底就是多个客户端使用同个地址访问服务端会出现该问题。我们的问题符合该场景。

解决方案

有两个方案可选:

  • 客户端关闭tcp_timestamps,将该值设置为0.

  • 服务器端不要将tcp_tw_recycle字段和tcp_timestamps字段同时设为1 

由于客户端掌握在客户的手里,我们无法掌握到每个客户端的配置,所以还是需要服务端进行配置。

因为在tcp timestamp关闭的条件下,开启tcp_tw_recycle是不起作用的;而tcp timestamp可以独立开启并起作用。所以建议服务关闭tcp_tw_recycle.

服务端(RS端)关闭tcp_tw_recycle具体操作方法。

1、临时关闭方法:
echo "0" > /proc/sys/net/ipv4/tcp_tw_recycle

2、永久关闭方法:
在 /etc/sysctl.conf 文件中添加 net.ipv4.tcp_tw_recycle = 0
然后使用 sysctl -p 命令让配置文件生效

引伸

根据上面的原理,出现多个客户端同时使用同一个IP访问同一个服务端的场景要在服务端关闭tcp_tw_recycle。如以下场景:

1、同一个Client访问四层私网SLB的同时,还有绕过SLB直接访问SLB后端ECS

2、相同的ECS同时挂在多个四层SLB之后,并且同一个客户端有同时或者连续访问多个SLB

3、ECS通过公网地址,NAT网关和EIP直接提供服务的。因为由于目前IPV4地址枯竭,绝大部分的客户端都是使用SNAT进行访问。

附录

参考:

http://blog.sina.com.cn/s/blog_781b0c850101pu2q.html

赞(0)
【声明】:本博客不参与任何交易,也非中介,仅记录个人感兴趣的主机测评结果和优惠活动,内容均不作直接、间接、法定、约定的保证。访问本博客请务必遵守有关互联网的相关法律、规定与规则。一旦您访问本博客,即表示您已经知晓并接受了此声明通告。