momo's Blog.

Kube-proxy使用IPVS下TCP长连接问题.

字数统计: 535阅读时长: 2 min
2022/09/28 Share

前言

一个老生常谈的话题。 今天业务又遇到了,特此把情形分析一下,加深一次印象。

业务使用 Redis subscribe 订阅 channel, 分布在多地域多机房,通过内网的nginx代理,然后公网环境读取。

但是却经常发生断线的操作。但实际上,nginx并没有做过什么限制。 而且这2小时断一次很像是有些参数没有调对。

为了查清楚到底是谁发起的 rst, 从nginx和 应用两边抓包。但是并没有看到nginx这边出现rst。 应用这边也没有看到。

问题

起初是有想到是因为 TCP keepalive 这个内核参数, 但是我们应用每30s都会去PING/PONG一下, 理论上是不会触发这个啊。

又查了一段时间,忽然想到PING/PONG 可能跟 subscribe 是两条连接。

在重新复习一下参数.

1
2
3
4
sysctl net.ipv4.tcp_keepalive_time net.ipv4.tcp_keepalive_probes net.ipv4.tcp_keepalive_intvl
net.ipv4.tcp_keepalive_time = 7200
net.ipv4.tcp_keepalive_probes = 9
net.ipv4.tcp_keepalive_intvl = 75

这是默认参数,这段参数的含义:
net.ipv4.tcp_keepalive_time是连接时长,当超过这个时间后,每个net.ipv4.tcp_keepalive_intvl的时间间隔会发送keepalive数据包,net.ipv4.tcp_keepalive_probe是发送keepalived数据包的频率。

根本原因

那问题就很清晰了,ipvsadm -l --timeout 连接默认过期时间是900s,也就是说,900s没消息 ipvs就给你断了。 然后我们redis subscribe 是被动收数据的,也就是说可能会出现超过900s也没有消息收到的情况。 而我们系统默认是 7200s(2小时)没消息后才会发keepalive_probes的包。

整体的流程如下:

  1. server 订阅 redis sub.
  2. redis sub 超过900s 没有消息,ipvs 断开了这个连接。
  3. server 服务器net.ipv4.tcp_keepalive_time 是 7200s,所以并没有发现自己连接已经断开了.
  4. 经过 7200s,没有收到数据, 系统开始执行 tcp_keepalive_probe, 当发送第一个包的时候,才发现连接已经被关闭了。

解决

1
sysctl -w net.ipv4.tcp_keepalive_time=600 && sysctl -w net.ipv4.tcp_keepalive_probes=10 && sysctl -w net.ipv4.tcp_keepalive_intvl=30

参考

  1. Idle connections over overlay network ends up in a broken state after 15 minutes
CATALOG
  1. 1. 前言
  2. 2. 问题
    1. 2.1. 根本原因
  3. 3. 解决
  4. 4. 参考