闲聊k8s的优雅关闭连接

序言

当数据在进行交互的时候,如果连接发生了改变,就必然会涉及到是否是无损关闭连接,主要就是看结束连接的时候是否是四次挥手关闭,短连接其实还好,最关键的是长连接如何关闭。

在k8s中,如果运行的是nginx ingress,就涉及到一个是reload会改变连接,一个是后面的svc发生变化的时候的连接;如果是普通的deployment,就涉及到rollingupdate滚动更新的时候,连接如何进行关闭的问题。

优雅的关闭连接

1 长连接与短连接

所谓的长连接,就是和客户端的tcp连接一直在一个会话上面,当你使用netstat查看的时候,连接的端口一直是同样的一个保持不变;而短连接则是使用netstat查看的时候,连接的端口总是同一个。

css 复制代码
netstat -antp(在命令中的62257一直保持不变,一直在estabished状态)Active Internet connections (servers and established)Proto Recv-Q Send-Q Local Address  Foreign Address  State   PID/Program nametcp  0   0 10.10.1.132:6379  10.0.20.3:62257   ESTABLISHED 9717/redis

站在nginx的角度来看,长连接要分为客户端和nginx之间的连接(还有nginx与upsteam的连接),这个是nginx参数keepalive_timeout来设置的,当客户端空闲多久的时候,就会将这个连接断开,也就是说,如果客户端一直在发送数据,那么这个连接可以到天长地久,只要世界不崩溃,这个连接就一直在。

css 复制代码
Syntax:    keepalive_timeout timeout [header_timeout];Default:    keepalive_timeout 75s;Context:    http, server, location

当然也可以配置为不是无限发送,所以偶尔会有一个长连接上发送的请求数量来进行限制,就类似于数据库的连接池,从而在到了一定的时候,这个连接肯定会进行关闭。

css 复制代码
Syntax:    keepalive_requests number;Default:    keepalive_requests 1000;Context:    http, server, location

要想流量进行无缝的衔接,从而应该有机会让长连接得到释放,不然的话,如果长连接无法释放,那么理论上还是偶尔有损失的。特别是对于websocket连接,grpc的连接,这种都是一个连接到死的那种,不过现在基本上客户端都兼容了,也就是连接断开了,也会自动进行重连。

2 pod的关闭流程

在k8s中,要支持优雅无损的进行关闭连接,首要的条件就是pod是支持对应的信号,看一下pod的关闭流程会发送哪些信号。

pod的状态变化,可以是使用命令kubectl进行手动删除pod,也可以是deployment在进行更新pod镜像的时候,pod的状态会先变成terminating,然后开始执行pod的prestop逻辑,执行一些必要的清理工作,然后当prestop执行完成之后,kubelet就会发送sigterm信号进行关闭pod,然后等待terminationgracetime,默认是30秒,到了时间之后,就会发送sigkill信号强制杀死pod,也就完成了一个pod的生命周期。

在这里,要注意的pod里面,是能够捕获到sigterm信号的,否则永远也无法进行优雅的关闭,除非你运气足够好,验证的方法是进入到你的pod,然后直接执行:

bash 复制代码
默认发送的是sigterm信号kill 1

如果你执行完成之后,退出了这个pod,也就说明你的pod能捕获到sigterm信号,能正确的处理,如果pod未退出,还是老样子,说明pod的主进程无法捕获sigterm信号,你需要检查容器的主进程了,如果是shell脚本,那么你需要自己捕获并传递sigterm信号,而在很多的dockerfile里面写的时候,entrypoint.sh里面使用exec直接取代shell,从而能捕获到sigterm信号,如果不想写,那么可以将tini作为你的主进程,这个能捕获并传递信号,而且能清理掉僵尸进程。

当你发现不能正确关闭的时候咋办,可以来追踪一下是否发送了信号。

powershell 复制代码
登录宿主机,找到pod的主进程strace -f -e trace=signal -p pid

这个时候你就可以看到发送了什么信号,在普通的容器中,一般是收到sigterm信号,但是在有的pod中,收到的sigquit信号,例如nginx,例如openresty,当他们进行reload的时候,其实也是发送的sigquit信号,而且在他们的dockerfile中,有如下的写法。当然,你也可以到node上去,查看kubelet的日志,里面会看到对应的发送信号的日志。

go 复制代码
STOPSIGNAL SIGQUIT

这两个信号有啥区别呢,sigterm信号对于长连接来说,它会一直等,直到客户端关闭连接,也就是说,如果长连接持续发送请求,那么这个连接基本不会关闭;而对于sigquit信号来说,是快速关闭,针对连接上的当前请求处理完成之后,立刻关闭连接。

3 pod关闭时间的设置

在有的时候,不能让pod关闭的太快,或者是快速的收到sigterm或者sigquit信号,那么可以在prestop里面直接sleep一个120秒,然后将terminationGracePeriodSeconds设置为300秒,这样就给了pod充足的时间来消化当前的请求,让请求更加无损的切换到新的pod或者连接上去。

在k8s的各个版本中,在v1.24版本以下都有个bug,配置了上面的两个时间之后,会导致pod删除的时间是120+300秒,但是在v1.26以上好像修复了,也就是基本上到了300秒就会直接删除pod,不会有过多的时间。这种配置带来的坏处就是每次你进行deployment升级或者是kubectl删除pod的时候,你会发现差不多要300秒才会删除这个pod,rolling的整体时间会变长。

在进行长连接销毁的时候,涉及到的是一个是老的连接,一个是新的连接,对于新的连接来说,需要的是pod的状态是ready的,也就是能正常的为客户端提供服务,也就意外着pod的liveness和readyness两个健康检查要进行配置,否则没准新请求就挂了,还没有ready就把流量调度上去了;而对于老的连接来说,主要是多久处理完成,从而对老的pod给予多少宽限的时间,也就是gracetime的设置,一个请求的最长超时时间是多久,太长的请求,其实。。。抛弃也无所谓了。

在nginx ingress里面,reload可能会造成问题,所以很多都开始直接内存进行更新对应的配置了,那么为什么reload会有问题,而直接更新内存不会有问题呢?

因为如果reload了,你会发现老的woker process被关闭了,新的进程起来了,新的连接要和新的进程建连,而如果在内存中更新,这个worker process是不会发生变化的,从而无论是什么连接,都可以保持不变进行继续处理。

4 压测抓包

当上面的都做完之后,就可以进行压测了,例如当你使用的ingress的时候,就可以不停的reload,就可以不停的修改ingress规则,然后查看压测的结果是否有5xx产生,如果有,就要检查下是不是哪个步骤出了问题。

当追踪信号的时候,如果出现了sigkill,那么估计会有连接被杀死,那么可以使用抓包看看,到底是长连接没释放,还是说服务端发送了reset包。由于压测的时候,包其实是很多的,所以一般可以用分段抓包,一个包100m进行抓包,从而减少分析的难度。

go 复制代码
tcpdump -C 100 -w tcp.pcap

风言风语

连接的关闭是否优雅,就看请求是否有失败的可能,如果可能有失败的,那么说明不是优雅的,尽量优雅,但是有些垃圾流量其实也是可以适当抛弃的,用客户端来进行兜底处理。

只要你足够细心,那么你就会发现各种各样的问题,然后就会有无穷的问题来解。抓住发现问题的点,然后一点一点分析下去,总是会找到解决问题的办法,如果实在没招了,那就放弃下一个。

相关推荐
诡异森林。2 小时前
Docker--Docker Container(容器)
docker·容器·eureka
代码浪人2 小时前
docker 基于Debian镜像安装FreeSwitch1.10.7
docker·容器·debian·freeswitch
qichengzong_right2 小时前
CNCF云原生生态版图-分类指南(一)- 观测和分析
linux·云原生
MavenTalk2 小时前
说说聊聊CNCF(云原生计算基金会)
微服务·云原生·架构·kubernetes·cloud native·cncf
成都 - 阿木木3 小时前
全新的命令行自动化测试框架/运用于云原生/中间件/云计算/混沌测试等场景
云原生·中间件·云计算
LeonNo118 小时前
k8s, deployment
java·容器·kubernetes
真上帝的左手14 小时前
十一、容器化 vs 虚拟化-Docker 命令
docker·容器
我的运维人生14 小时前
Docker实践与应用举例:构建高效开发与部署环境
docker·容器·eureka·运维开发·技术共享
小杨童鞋14 小时前
docker中onlyoffice升级指导
运维·docker·云原生·容器·eureka
一掬净土14 小时前
K8S服务突然中断无法访问:报The node had condition: [DiskPressure]异常
容器·kubernetes·k8s·diskpressure·磁盘压力