Kubernetes踩坑(二): Service IP(LVS)间断性TCP连接故障排查

2023-11-06

问题阶段(一):

用户反应某个redis使用卡顿,连接该redis服务使用的是svc代理,即ipvs snat的方式,ipvsadm -L发现,VIP收到的6379端口的数据包,会以rr的方式分别转发到pod的80 6379端口上,相当于会有50%的丢包,不卡才怪:

# ipvsadm | grep -2 10.108.152.210
TCP  10.108.152.210:6379 rr
  -> 172.26.6.185:http            Masq    1      0          0         
  -> 172.26.6.185:6379            Masq    1      3          3

2.检查svc:

apiVersion: v1
kind: Service
metadata:
  creationTimestamp: 2018-12-06T02:45:49Z
  labels:
    app: skuapi-rds-prod
    run: skuapi-rds-prod
  name: skuapi-rds-prod
  namespace: default
  resourceVersion: "41888273"
  selfLink: /api/v1/namespaces/default/services/skuapi-rds-prod
  uid: 09e1a61f-f901-11e8-878f-141877468256
spec:
  clusterIP: 10.108.152.210
  ports:
  - name: redis
    port: 6379
    protocol: TCP
    targetPort: 6379
  selector:
    app: skuapi-rds-prod
  sessionAffinity: None
  type: ClusterIP

可以看出svc配置没有问题,回忆起此前配置此svc时,一开始失误将port和targetPort设置为了默认值80忘记修改了,后面手动kubectl edit svc修改端口为6379。应该是修改保存之后,原本的targetPort 80
依然在ipvs中保留为RIP之一,这应该是一个bug。

3.解决办法:
使用ipvsadm工具删除多余的RIP,或者删除此svc然后重建。

# ipvsadm | grep -2 10.108.152.210
TCP  10.108.152.210:6379 rr
  -> 172.26.6.185:6379            Masq    1      4          0  

问题阶段(二):

现象:

有用户反馈svc vip连接经常失败,不限于redis的多个ip有这种情况,于是开始排查

排查:

通过对ipvs和系统的连接状态排查,发现了两个问题:
1.发现有大量TIME_WAIT的tcp连接,证明系统连接大多是短连接,查看ipvs tcpfin的等待时间为2分钟,两分钟还是过长,有必要修改短一些,30秒是一个比较合理的值,如果比较繁忙的服务,这个值可以改到更低。
2.有不少丢包

[root@p020107 ~]# ipvsadm -lnc |wc -l
23776
[root@p020107 ~]# ipvsadm -lnc |grep TIME_WAIT | wc -l
10003
[root@p020107 ~]# ipvsadm -L --timeout 
Timeout (tcp tcpfin udp): 900 120 300

root@p020107:~# netstat -s | grep timestamp
    17855 packets rejects in established connections because of timestamp

备注:
根据tcp 四次挥手协议,主动发起断开连接请求的的一端(简单理解为客户端),在其发送断开连接请求开始,其连接的生命周期会经历4个阶段分别是FIN-WAIT1 --> FIN_WAIT2 --> TIME_WAIT --> CLOSE,其中2个FIN-WAIT阶段等待的时间就是内核配置的net.ipv4.tcp_fin_timeout 的值,为了快速跳过前两个FIN-WAIT阶段从而进入TIME_WAIT状态,net.ipv4.tcp_fin_timeout值建议缩短。在进入TIME_WAIT状态后,默认等待2个MSL(Max Segment Lifetime)时间,到达最后一步CLOSE状态,关闭tcp连接释放资源。注意:MSL时间在不同平台一般是30s-2min不等,并且基本都是不可修改的(linux将这一时间值写死在了内核中)。

那么为什么要等待2*MSL呢?在stackoverflow中找到了一个较为易懂的解释:

So the TIME_WAIT time is generally set to double the packets maximum age. This value is the maximum age your packets will be allowed to get to before the network discards them.
That guarantees that, before you’re allowed to create a connection with the same tuple, all the packets belonging to previous incarnations of that tuple will be dead.

翻译一下:
time_wait时间设计为tcp分片的最大存活时间的两倍,这么设计的原因是,网络是存在延迟的,同时tcp分片在网络传输中可能出现意外,发送端在确认意外(例如到达MSL时间后)后发出数据分片的重传。假如socket连接不经等待直接关闭了,然后再重新打开了一个端口号一致的连接,可能导致新启动的socket连接,接收到了此前销毁关闭的socket连接的数据。因此,设计TIME_WAIT等待时间为2MSL,是为了保证在等待2MSL之后,此前旧socket的数据分片即使还没有到达接收端,也已经在网络传输中过期消逝了,新启动的socket不会接收到此前的旧数据分片。

优化方式

优化的思路
1.断开连接时加速进入TIME_WAIT状态,以快速提供可用的连接端口
2.解决timestamps丢包问题

#查看ipvs设置的各类连接的超时时间,修改默认的tcpfin 2分钟为30秒

[root@p020107 ~]# ipvsadm -L --timeout 
Timeout (tcp tcpfin udp): 900 120 300
[root@p020107 ~]# ipvsadm --set 900 30 300

[root@p020107 ~]# ipvsadm -L --timeout 
Timeout (tcp tcpfin udp): 900 30 300

#内核参数优化
添加入/etc/sysctl.conf文件中

# 表示开启重用。允许将TIME-WAIT sockets重新用于新的TCP连接,默认为0,表示关闭;
  net.ipv4.tcp_tw_reuse = 1

  # 表示开启TCP连接中TIME-WAIT sockets的快速回收,默认为0,表示关闭。注意,net.ipv4.tcp_timestamps默认为开启,tcp_tw_recycle此选项也开启后,tcp timestamp机制就会激活,在部分场景下,需要关闭tcp_timestamps功能,见下方此选项的说明。
  net.ipv4.tcp_tw_recycle = 1

  # 修改tcp会话进去fin状态后的等待时间,超时则关闭会话
  net.ipv4.tcp_fin_timeout = 30

  # 处于TIME_WAIT最大的socket数量,默认为180 000,超过这个数目的socket立即被清除
  net.ipv4.tcp_max_tw_buckets=180000

  # tcp缓存时间戳,RFC1323中描述了,系统缓存每个ip最新的时间戳,后续请求中该ip的tcp包中的时间戳如果小于缓存的时间戳(即非最新的数据包
),即视为无效,相应的数据包会被丢弃,而且是无声的丢弃。在默认情况下,此机制不会有问题,但在nat模式时,不同的ip被转换成同一个ip再去请
求真正的server端,在server端看来,源ip都是相同的,且此时包的时间戳顺序可能不一定保持递增,由此会出现丢包的现象,因此,如果是nat模式工作,建议关闭此选项。
  net.ipv4.tcp_timestamps = 0

这几个参数之间还有一些关联关系,参考此篇文章,写得非常详细:
http://www.freeoa.net/osuport/cluster/lvs-inuse-problem-sets_3111.html

问题阶段(三):

现象

完成上面的操作后,TIME_WAIT数量下降到了4位数,丢包数量没有再增加。但是过了一些天之后,再一次出现了偶尔个别VIP无法建立连接的情况。挑选了其中一个VIP 10.111.99.131开始排查
client: 192.168.58.36
DIP: 10.111.99.131
RIP: 172.26.8.17

开始排查

三层连接没有问题:

ywq@ywq:~$ traceroute 10.111.99.131
traceroute to 10.111.99.131 (10.111.99.131), 30 hops max, 60 byte packets
 1  192.168.58.254 (192.168.58.254)  7.952 ms  8.510 ms  9.131 ms
 2  10.111.99.131 (10.111.99.131)  0.253 ms  0.243 ms  0.226 ms

ywq@ywq:~$ ping 10.111.99.131
PING 10.111.99.131 (10.111.99.131) 56(84) bytes of data.
64 bytes from 10.111.99.131: icmp_seq=1 ttl=63 time=0.296 ms
64 bytes from 10.111.99.131: icmp_seq=2 ttl=63 time=0.318 ms
^C
--- 10.111.99.131 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1020ms
rtt min/avg/max/mdev = 0.296/0.307/0.318/0.011 ms

tcp连接无法建立

ywq@ywq:~$ telnet 10.111.99.131 80
Trying 10.111.99.131...
telnet: Unable to connect to remote host: Connection timeout

在lvs diretor server上查看连接状态:
[root@p020107 ~]# ipvsadm -lnc | grep 58.36
TCP 00:59 TIME_WAIT 192.168.9.13:58236 10.97.85.43:80 172.26.5.209:80
TCP 00:48 SYN_RECV 192.168.58.36:57964 10.111.99.131:80 172.26.8.17:80
TCP 00:48 SYN_RECV 192.168.58.36:57963 10.111.99.131:80 172.26.8.17:80
TCP 00:48 SYN_RECV 192.168.58.36:57965 10.111.99.131:80 172.26.8.17:80

发现tcp连接状态为SYN_RECV状态,那么根据三次握手协定,再结合lvs的工作流程,说明LVS server接收到了clien的syn包,向client回复了syn+ack然后进入了SYN_RECV状态,同时direct server会向后端的real server发起建立连接的请求。既然现在direct server能与client端交互,那么当前的问题应该在于:
direct Server和real Server之间没有正常地进行数据包交互或者出现了丢包,查阅了很多资料,rp_filter这一内核参数可能导致这一问题。

来看一下官方关于这一参数的解释:

rp_filter - INTEGER
	0 - No source validation.
	1 - Strict mode as defined in RFC3704 Strict Reverse Path
	    Each incoming packet is tested against the FIB and if the interface
	    is not the best reverse path the packet check will fail.
	    By default failed packets are discarded.
	2 - Loose mode as defined in RFC3704 Loose Reverse Path
	    Each incoming packet's source address is also tested against the FIB
	    and if the source address is not reachable via any interface
	    the packet check will fail.

	Current recommended practice in RFC3704 is to enable strict mode
	to prevent IP spoofing from DDos attacks. If using asymmetric routing
	or other complicated routing, then loose mode is recommended.

	The max value from conf/{all,interface}/rp_filter is used
	when doing source validation on the {interface}.

	Default value is 0. Note that some distributions enable it
	in startup scripts.

简单解释一下:
0:表示不开启源检测
1:严格模式,根据数据包的源,通过查FIB表(Forward Information Table,可以理解为路由表),检查数据包进入端口是同时也是出端口,以视为最佳路径,如果不符合最佳路径,则丢弃数据包
2:松散模式,检查数据包的来源,查FIB表,如果通过任意端口都无法到达此源,则丢包
结合使用场景来说:
在LVS (nat)+k8s的工作场景下,LVS Server送往Real Server的包可能走的tunnel接口,而Real Server通过tunnel接口收到包后,查路由表发现回包要走物理eth/bond之类接口,如果rp_filter开启了严格模式,会导致网络异常状况

检查每一台kube node的网卡配置参数,发现centos7.4的几台node默认确实开启了rp_filter,ubuntu大部分则没有:
# 容器内的veth网卡可忽略,因为容器本身只有一块对外的网卡
[root@p020114 ~]# sysctl -a | grep rp_filter | grep -v 'veth'
net.ipv4.conf.all.rp_filter = 1
net.ipv4.conf.bond0.rp_filter = 1
net.ipv4.conf.default.rp_filter = 0
net.ipv4.conf.docker0.rp_filter = 2
net.ipv4.conf.dummy0.rp_filter = 0
net.ipv4.conf.em1.rp_filter = 1
net.ipv4.conf.em2.rp_filter = 1
net.ipv4.conf.em3.rp_filter = 1
net.ipv4.conf.em4.rp_filter = 1
net.ipv4.conf.kube-bridge.rp_filter = 0
net.ipv4.conf.kube-dummy-if.rp_filter = 0
net.ipv4.conf.lo.rp_filter = 0
net.ipv4.conf.tun-192168926.rp_filter = 1
net.ipv4.conf.tun-192168927.rp_filter = 1
net.ipv4.conf.tun-192168928.rp_filter = 1
net.ipv4.conf.tun-192168929.rp_filter = 1
net.ipv4.conf.tunl0.rp_filter = 0

#关闭此功能

echo "
net.ipv4.conf.all.rp_filter = 0
net.ipv4.conf.bond0.rp_filter = 0
net.ipv4.conf.default.rp_filter = 0
net.ipv4.conf.docker0.rp_filter = 2
net.ipv4.conf.dummy0.rp_filter = 0
net.ipv4.conf.em1.rp_filter = 0
net.ipv4.conf.em2.rp_filter = 0
net.ipv4.conf.em3.rp_filter = 0
net.ipv4.conf.em4.rp_filter = 0
net.ipv4.conf.kube-bridge.rp_filter = 0
net.ipv4.conf.kube-dummy-if.rp_filter = 0
net.ipv4.conf.lo.rp_filter = 0
net.ipv4.conf.tun-192168926.rp_filter = 0
net.ipv4.conf.tun-192168927.rp_filter = 0
net.ipv4.conf.tun-192168928.rp_filter = 0
net.ipv4.conf.tun-192168929.rp_filter = 0
net.ipv4.conf.tunl0.rp_filter = 0
" >> /etc/sysctl.conf

#加载生效

sysctl -p

总结

VIP偶尔无法建立TCP连接的问题已解决,一个星期过去了没有再复现,继续观察ing.

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Kubernetes踩坑(二): Service IP(LVS)间断性TCP连接故障排查 的相关文章

随机推荐

  • ms office excel2013教程 - 分类汇总

    分类汇总 目标 将学生表按学院和性别分类对于各科成绩平均分进行汇总 步骤 先分别对学院和性别进行排序 再分别对学院和性别进行分类汇总
  • 深入理解数据库事务隔离特性

    很多同学对数据库事务隔离不是很懂 看了很久好像懂了又好像没懂 我说说我的理解 这里重点强调一点 一定要动手 手动打开sql命令窗口 手动敲命令 一定 一定 首先 什么是事务 一条sql语句算不算事务 只有显示写了begin rollback
  • 51单片机可调时钟

    通过按键实现设置时钟时间 void main LCD Init DS1302 Init Timer0Init LCD ShowString 1 1 静态字符初始化显示 LCD ShowString 2 1 DS1302 SetTime 设置
  • 一行 Python 代码实现并行!

    Python 在程序并行化方面多少有些声名狼藉 撇开技术上的问题 例如线程的实现和 GIL 我觉得错误的教学指导才是主要问题 常见的经典 Python 多线程 多进程教程多显得偏 重 而且往往隔靴搔痒 没有深入探讨日常工作中最有用的内容 传
  • 【计算机视觉】详解 自注意力:Non-local 模块与 Self-attention (视觉注意力机制 (一))

    目录 绪论 一 视觉应用中的 self attention 机制 1 1 self attention 机制 1 2 Self attention 机制应用 Non local Neural Networks 绪论 在计算机视觉中 注意力机
  • Vue笔记_03组件_transition组件(Vue内置组件)

    目录 transition组件 作用 过渡时机 语法1 语法2 使用 animation侦动画 使用 过渡动画 总结 逻辑 前后端配置 动态显示页面 transition组件 作用 transition组件的作用是 给 单个 元素 组件 添
  • Uncaught SyntaxError: Invalid or unexpected token

    报错原因 内含中文字符串或者中文的符号 解决方案 1 去除页面上中文符号 改成英文符号 2 特殊的情况 那我的报错原因来说一下 报错代码和背景 我在做js使用DOM元素做拼接 报了这个错误 Uncaught SyntaxError Inva
  • 使用循环打印三角型以及九九乘法表

    一 循环打印三角形 1 需求分析 需要用到双层for循环来实现 外层循环实现行数 内层循环实现每行的个数 外层每循环一次 就换一次行 内层循环打印的个数与外层循环的行数是相对应的 由于内层循环的个数和外层循环的行数一样 所以内部循环的判断条
  • Linux下C++开发笔记--解决报错error: redefinition of “xxx“

    1 报错截图 2 错误原因 对应的头文件被多次包含 3 解决方法 在头文件最上面加上以下代码 以避免被多次包含 pragma once
  • 原码,反码,补码的使用

    机器码 1 首先得知道 什么是机器码 将符号 数字化 的二进制数就叫做机器码 其中有两点 1 将符号数字化 也就是将 用数字0表示 用数字1表示 最高位表示符号位 2 必须是二进制数 因为计算机中所有的数据都是以二进制形式存储的 2 了解了
  • HBase主要运行机制(物理存储和逻辑架构)

    本节将对 HBase 的主要运行机制进行简单介绍 HBase 的物理存储 HBase 表中的所有行都是按照行键的字典序排列的 因为一张表中包含的行的数量非常多 有时候会高达几亿行 所以需要分布存储到多台服务器上 因此 当一张表的行太多的时候
  • 【MySQL高级篇笔记-数据库备份与恢复(下) 】

    此笔记为尚硅谷MySQL高级篇部分内容 目录 一 物理备份与逻辑备份 二 mysqldump实现逻辑备份 1 备份一个数据库 2 备份全部数据库 3 备份部分数据库 4 备份部分表 5 备份单表的部分数据 6 排除某些表的备份 7 只备份结
  • 【VSCode】Windows系统的WSL无法启动vscode问题

    在WSL环境中无法启动vscode时 有可能是 WSL 插件的影响 可以使用下面的步骤来解决 Open VS Code on Windows Open Extensions and then search on WSL It should
  • Qt使用Qt Designer进行界面设计

    上一章我们使用代码直接进行界面设计 这一章我们使用Qt Designer进行界面设计 简单直接 所见即所得 大大提高了工作效率 特别是对于复杂界面 1熟悉Qt Designer Qt Designer是Qt专为界面设计做的软件 使得用户能够
  • 使用Python和OpenCV进行图像拼接和全景图构建

    使用Python和OpenCV进行图像拼接和全景图构建 1 效果图 2 原理及步骤 3 源码 3 1 拼接类源码 3 2 拼接用到的工具类 3 3 叠加多张图像源码 参考 这篇博客将介绍如何使用OpenCV执行图像拼接和全景构建 即给定两个
  • Hana Studio开发简介

    Hana Studio作为SAP官方的IDE 工具 推出也有一段时间了 就目前使用的情况来看 如果是做常规S 4开发 SAP GUI还是首要选择 一 IDE安装路径 链接 https pan baidu com s 1qMg8duocTa3
  • pyqt5实现按钮单窗口多页面切换

    1 使用QT Designer进行设计 创建一个MainWindow 从左侧选出Push Button Stacked Widget分别拖到我们的MainWindow里 怕看不见Stacked Widget 给他上个色 在QT Design
  • vant-weapp Area 省市区选择的使用及遇到的坑

    json中 导入 van area vant weapp area index 基础用法
  • SpringCloud gateway (史上最全)

    1 1 SpringCloud Gateway 简介 SpringCloud Gateway 是 Spring Cloud 的一个全新项目 该项目是基于 Spring 5 0 Spring Boot 2 0 和 Project Reacto
  • Kubernetes踩坑(二): Service IP(LVS)间断性TCP连接故障排查

    问题阶段 一 用户反应某个redis使用卡顿 连接该redis服务使用的是svc代理 即ipvs snat的方式 ipvsadm L发现 VIP收到的6379端口的数据包 会以rr的方式分别转发到pod的80 6379端口上 相当于会有50