序言
又要排查问题了,这次的问题是底层网络问题:connection reset by peer。从这次排查中有许多收获,也算是入门了如何通过tcpdump抓包来排查k8s集群中的网络问题。
背景
问题背景是,每天的0点,QA同学都需要运行自动化测试用户。此时就发现,每次运行到某一个接口时就会报错如下,几乎是必现的:
org.apache.http.impl.nio.reactor.AbstractMultiworkerIOReactor$Worker.run(AbstractMultiworkerIOReactor.java:591)\n\t\t\t... 1 more\n\tCaused by: java.net.SocketException: Connection reset\n\t\tat java.base/sun.nio.ch.SocketChannelImpl.throwConnectionReset(SocketChannelImpl.java:394)\n\t\tat java.base/sun.nio.ch.SocketChannelImpl.read(SocketChannelImpl.java:426)\n\t\tat org.apache.http.impl.nio.reactor.SessionInputBufferImpl.fill(SessionInputBufferImpl.java:231)\n\t\tat org.apache.http.impl.nio.codecs.AbstractMessageParser.fillBuffer(AbstractMessageParser.java:136)\n\t\tat org.apache.http.impl.nio.DefaultNHttpClientConnection.consumeInput(DefaultNHttpClientConnection.java:241)\n\t\tat org.apache.http.impl.nio.client.InternalIODispatch.onInputReady(InternalIODispatch.java:81)\n\t\tat org.apache.http.impl.nio.client.InternalIODispatch.onInputReady(InternalIODispatch.java:39)\n\t\tat org.apache.http.impl.nio.reactor.AbstractIODispatch.inputReady(AbstractIODispatch.java:114)\n\t\tat org.apache.http.impl.nio.reactor.BaseIOReactor.readable(BaseIOReactor.java:162)\n\t\tat org.apache.http.impl.nio.reactor.AbstractIOReactor.processEvent(AbstractIOReactor.java:337)\n\t\tat org.apache.http.impl.nio.reactor.AbstractIOReactor.processEvents(AbstractIOReactor.java:315)\n\t\tat org.apache.http.impl.nio.reactor.AbstractIOReactor.execute(AbstractIOReactor.java:276)\n\t\tat org.apache.http.impl.nio.reactor.BaseIOReactor.execute(BaseIOReactor.java:104)\n\t\tat org.apache.http.impl.nio.reactor.AbstractMultiworkerIOReactor$Worker.run(AbstractMultiworkerIOReactor.java:591)\n\t\t... 1 more\nCaused by: java.net.SocketException: Connection reset\n\tat java.base/sun.nio.ch.SocketChannelImpl.throwConnectionReset(SocketChannelImpl.java:394)\n\tat java.base/sun.nio.ch.SocketChannelImpl.read(SocketChannelImpl.java:426)\n\tat org.apache.http.impl.nio.reactor.SessionInputBufferImpl.fill(SessionInputBufferImpl.java:231)\n\tat org.apache.http.impl.nio.codecs.AbstractMessageParser.fillBuffer(AbstractMessageParser.java:136)\n\tat org.apache.http.impl.nio.DefaultNHttpClientConnection.consumeInput(DefaultNHttpClientConnection.java:241)\n\tat org.apache.http.impl.nio.client.InternalIODispatch.onInputReady(InternalIODispatch.java:81)\n\tat org.apache.http.impl.nio.client.InternalIODispatch.onInputReady(InternalIODispatch.java:39)\n\tat org.apache.http.impl.nio.reactor.AbstractIODispatch.inputReady(AbstractIODispatch.java:114)\n\tat org.apache.http.impl.nio.reactor.BaseIOReactor.readable(BaseIOReactor.java:162)\n\tat org.apache.http.impl.nio.reactor.AbstractIOReactor.processEvent(AbstractIOReactor.java:337)\n\tat org.apache.http.impl.nio.reactor.AbstractIOReactor.processEvents(AbstractIOReactor.java:315)\n\tat org.apache.http.impl.nio.reactor.AbstractIOReactor.execute(AbstractIOReactor.java:276)\n\tat org.apache.http.impl.nio.reactor.BaseIOReactor.execute(BaseIOReactor.java:104)\n\tat org.apache.http.impl.nio.reactor.AbstractMultiworkerIOReactor$Worker.run(AbstractMultiworkerIOReactor.java:591)\n\t... 1 more\n']}}
一看上面的错误我就知道有些棘手了,因为之前没有排查这种问题的经验。不是以前没遇到过,而是没有认真对待过,所以我决定利用这个机会好好研究下这个问题。
k8s容器网络介绍
connect reset by peer应该是服务点发送了rst或者客户端发送了fin,总之就是tcp连接已经断了,但是程序不知道,还在往这个连接里写东西,才会报这个错。所以就需要使用tcpdump这个命令来进行抓包,看看具体是哪个原因。但是,如果不通过源\目的IP,源\目的端口对数据进行过滤,tcpdump出来的东西太多,肯定没法分析。所以需要了解怎么缩小抓取范围,这个时候就需要k8s容器网络的知识了,因为我们的java应用服务和es都是在k8s里面的。
我们的网络组件用的flannel,其具体原理就是vxlan,即使用udp隧道传输,具体原理大致是这样,详细大家可以网上搜下。这里有个网络结构图。
![在这里插入图片描述](https://img-blog.csdnimg.cn/9181b4b3417f45efaf83273384afcdd2.png
tcpdump抓包
这里注意,如果要抓物理网卡,根据上面原理,需要抓udp包。
tcpdump udp -i xx -ne and 'src xxx' and 'dst xxx' -vv
如果抓cni0,那么可以指定es的tcp通信端口9200以及POD的ip
tcpdump tcp -i cni0 -nne port 9200 and 'tcp[tcpflags] & (tcp-syn|tcp-rst|tcp-fin)!=0' and 'dst xxx' -tttt -s0 -X -vv
这里的and后面的表达式意思是仅拦截syn、rst、fin三个tcp flag的包
从上面抓的包里的ip就是es的endpoint的ip!
使用wireshark来做进一步分析
直接tcpdump下的信息不太好看到消息体,这时可以使用tcpdump -w来输出到文件,下载到本地使用wireshark来查看。
还可以输入http来过滤http包。
问题排查及解决
问题是es有两个节点未启动,在恢复了es集群后,我还在esClient上配置了soKeepAlive