Docker服务在一段时间后停止通信

16

我在Docker Swarm中运行了6个容器。其中包括Kafka+Zookeeper、MongoDB、A、B、C和接口。接口是公共访问的主要入口点 - 只有这个容器发布了端口5683。启动时,接口容器连接到A、B和C。我使用docker-compose文件和docker stack deploy,每个服务都有一个名称,用作接口的主机名。一切都成功启动并正常工作。但是在一段时间后(20分钟、1小时等),我无法向接口发送请求。接口会收到我的请求,但应用程序与服务A、B、C或全部失去连接。如果我重启接口,则可以重新连接到服务A、B、C。

我最初认为这是应用程序的问题,所以我在每个服务(接口、A、B、C)上公开了2个新端口,并连接了分析器和调试器。应用程序正常运行,没有泄漏,没有阻塞的线程,正常工作并等待连接。调试器显示当我向接口发出请求时,接口尝试请求服务A时会抛出“Connection reset by peer”的异常。

在调试期间,我发现了一些有趣的事情。当服务启动时,我将调试器附加到接口,但之后调试器会断开连接。我无法重新连接它,直到我向容器的应用程序发出请求。问题-握手失败。

我发现的另一个有趣的事情是,我也无法请求接口。因此,我使用Wireshark查看了发生了什么:SYN-ACK很好。然后应用程序发布一些数据,接口响应FIN,ACK。我认为当接口尝试请求服务A时,它也会终止连接。接口、A、B和C的代码库在Netty服务器方面是相同的。

最后,我不认为这是应用程序的问题。为什么?我尝试将容器部署为非服务。

我单独运行每个容器,公开每个容器的端口,并将服务端点设置为localhost(而不是覆盖网络)。这样做是可以工作的。容器可以正常运行。另外,在Docker中运行的Java应用程序(接口、A、B、C)作为独立应用程序运行时也没有问题。

请问您能帮我看一下问题出在哪里吗?为什么使用交叉网络时Docker会关闭套接字?

我正在使用最新版本的Docker,之前也用过较旧的版本。


“Interface”,“A”,“B”和“C”都是您自己的Java应用程序吗?我不确定“Interface”是什么。此外,您是否尝试过在Docker Compose中使用而不是Docker Swarm(仅单个主机)?我的猜测是应用程序在Docker Swarm路由器上运行时出现了奇怪的问题,因此尝试不使用Docker Swarm可能是个好主意。 - Andy Shinn
是的,A、B、C和接口都是我的Java应用程序,具有相同的基础代码。共享的基础代码包含了netty等内容。接口只是服务的名称,以便更容易地解释问题——它只是公共接口,并作为通往其他服务的网关,这些服务不发布端口。此外,它还负责身份验证和授权,但这是业务逻辑。 - Ondrej Tomcik
正如我所写的,我尝试分别运行每个容器,每个容器都发布了它所暴露的端口,并且我使用本地主机作为IP地址,因此接口连接的不是DNS主机名而是本地主机。这样做没有任何问题。 - Ondrej Tomcik
另一个有趣的观点是接口正在连接到服务A,并在日志中显示:“已连接,地址:iotivity-accountserver/10.0.0.2:5685”。但是,当我检查网络时,此容器iotivity-accountserver具有“IPv4Address”:“10.0.0.3/24”。并且在网络检查中,没有提到10.0.0.2地址。此外,与10.0.0.2的连接已断开。 - Ondrej Tomcik
你在只有一个节点的群集上执行了检查吗? - gesellix
显示剩余2条评论
2个回答

22

终于,我成功地解决了这个问题。

再来一遍出现的情况:接口打开了到A、B、C的永久TCP连接。当你试图将这些服务A、B、C作为独立的Java应用程序运行时,一切正常。但是当我们将它们Docker化并在Swarm中运行时,它只能工作几分钟。奇怪的是,当你从客户端对接口发起请求时,接口与另一个服务之间的连接会中断。

经过许多许多次不成功的测试和调试每个容器后,我尝试单独运行每个Docker容器,映射端口,并将localhost指定为端点(每个容器都公开了端口,界面连接到localhost)。有趣的事情发生了,它能够正常工作。当您以这种方式运行容器时,使用不同的容器网络驱动程序。桥接驱动程序。如果在Swarm中运行,则使用覆盖网络驱动程序。

因此,它必须与Docker网络有关,而不是应用本身。下一步是在几分钟后从每个容器进行tcpdump,当应该停止工作时。这非常有趣。

  • 客户端 -> 接口(OK,请求已接受)
  • 接口 ->(转发请求,因为它属于A)A
    • 接口 -> A [POST]
    • A -> 接口 [RESET]

A会在几分钟后没有通信的情况下重置已打开的TCP通信。为什么?

Docker使用IP虚拟服务器,而IPVS维护自己的连接表。IPVS表中CLOSE_WAIT连接的默认超时时间为60秒。因此,当服务器在60秒后发送某些内容时,IPVS连接将不再可用,并且数据包对于新的TCP会话看起来无效并被RST。在客户端方面,连接始终处于FIN_WAIT2状态,因为应用程序仍然保持套接字打开;内核的fin_wait计时器仅针对孤立的TCP套接字。

这就是我关于它的阅读和理解方法。我不确定我的问题解释是否正确,但基于这些假设,我在Interface和A、B、C服务之间实现了“乒乓”,以防止<60秒没有通信。而且,它有效。


非常有用的研究!谢谢。但是我这里有一个更深层次的问题。我的其中一个容器是MongoDB,它会随机重置连接,我显然无法实现ping-pong... - Elessar.perm
我也在使用MongoDB,对此没有任何问题。我建议尝试不同的客户端库。 - Ondrej Tomcik
1
这是Mongo团队的官方客户端库,我成功理解了一些重现条件。如果我的容器通过覆盖网络进行通信,则可以正常工作。如果尝试通过发布端口(通过入口网络)进行通信,则存在风险。 而这与客户端库无关,因为我尝试启动两个官方容器:一个带有数据库,另一个运行mongodump(相同的镜像,只是不同的启动命令),并且它以相同的方式工作。 当我使用发布端口时,它会随机重置连接,并且每当我使用内部网络地址时,它都可以正常工作。 - Elessar.perm
它并不总是会出错。我曾经成功通过公开端口mongodump了一个20GB的集合(1200万个文档),但当我试图转储类似于200GB的集合时,它几乎总是会出问题。 - Elessar.perm

0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接