前言
优雅下线的解释是以一种不中断正在进行的工作或服务的方式将服务停止或下线。本文特指服务在容器里时,优雅下线需满足:
-
老容器下线,但需等待已连接请求正常返回(有最长超时时间)后再下线。
-
老容器正在下线中或已下线,但网络流量还在引流过来,导致新请求过来直接报错
本文是这类使用场景中遇到了问题,网络资料多个方式都没解决,自己摸索测试后解决的case。
优雅下线说明
前段时间基于K8s svc的微服务架构,发现每次重新部署或重启都会有服务出现少量failed to respond executing POST/GET ... 、Connection refused...、Connection reset ...等类型报错。参考了很多网上资料和云社区文档发现都没有解决,按照相关资料关键的点:
-
K8s下线或重启容器pod时,先给一个SIGTERM信号给pod
-
pod接收到SIGTERM信号后,需要优雅停服(不直接结束,等待已连接的请求处理完成后再结束)
-
一定时间后,K8s平台再发SIGKILL强杀信号给pod,让pod退出
-
K8s给了SIGTERM信号后,svc不能再引流到待下线的pod
但根据相关资料配置和操作后还是有问题,经过多次测试最终才解决。
最关键的点包括:服务配置优雅停服、K8s脚本配置preStopHook休眠、http连接复用的时间低于休眠时间等。这里汇总下关键的问题和解决方法。
K8S更新网络规则和删除 Pod 是同时进行,按照时间顺序关键问题:
针对问题关键的解决方法如下:
下面用微服务是SpringBoot,并基于K8s SVC服务发现方式,用OpenFeign方式调用,配置如下:
1、SpringBoot配置优雅下线:保证服务方已连接请求,不会因为服务重启或下线而直接终止。
server:
shutdown: graceful
spring:
lifecycle:
# 默认30秒
timeout-per-shutdown-phase: 25s
2、K8s配置优雅下线:preStopHook配置休眠能力(休眠等待网络规则生效后再发送SIGTERM信号到pod)。
比如:
containers:
lifecycle:
preStop:
exec:
command: ["sh", "-c", "sleep 15"]
2.1 注意:发送SIGTERM信号时,可能存在服务收不到信号的情况,需要注意服务的进程是否是子进程,默认SIGTERM信号不会传递到子进程。需要用exec xxxbash方式调用服务,或trap关键字执行命令可以传递信号到子进程
3、HTTP连接复用优雅断连:保证http调用时不因连接复用导致网络流量引流到老pod(没用nacos等注册中心,客户端不能提前感知到pod下线而处理引流逻辑),连接复用时间低于上面sleep时间
比如openFeign内部使用ApacheHttpClient
feign:
httpclient:
enable: true
time-to-live: 10
connection-timer-repeat: 1000
注:如果内部使用OkHttpClient,配置feign.httpclient.time-to-live
4、其他配置
4.1 网关也需要配置连接复用时间,比如
APISIX配置upstream上游服务的
keepalive_pool.idle_timeout 参数
4.2 其他自定义线程池也配置优雅停止等