SpringCloudAlibaba + k8s实现优雅停机

背景

在k8s+SpringCloudAlibaba技术体系下,实现后台服务的优雅停机。注册中心和配置中心都是nacos,该方案中要使用Nacos的API所以需要Nacos的版本 >= 2.2.0

目的

服务部署上线停机时,不能出现异常。为实现这样的目的,需要同时满足以下两个条件:

  1. 新的请求不能打到要停机的服务上。为实现这样的效果需要先切流量。
  2. 已经持有的请求可以在预期时间内正常返回。这需要在停机前要等待一段时间。

出发点

尽量少的改动

原理

当新Pod就绪之后,k8s会向旧Pod发送停机信号,Pod中的线程要在指定的时间内退出,否则就会被强制杀掉,所以要实现优雅停机具体流程如下:

  1. 接收到停机信号后将服务的权重降为0,为的是不再接收新的请求。通过entrypoint.sh脚本,调用NacosAPI实现,API中的参数通过Dockerfile设置。
  2. 等待客户端的缓存过期。等待的逻辑也在entrypoint.sh脚本。这里的缓存是指负载均衡的缓存,SpringCloudAlibaba体系下默认的是LoadBalancer,通过参数spring.cloud.loadbalancer.cache.ttl设置,默认35s。
  3. 等待已经持有的线程结束。
  4. 执行停机逻辑,从注册中心摘除。

具体实现

出于最小改动的目的,需要了解的参数如下:

  1. k8s参数terminationGracePeriodSeconds,默认为30s,如果向Pod发出停机信号后,在30s内没有退出,会被强制杀掉。
  2. LoadBalancer参数spring.cloud.loadbalancer.cache.ttl,负载缓存更新时间。
  3. 业务允许的最大请求时延,根据业务调整,如果超过30s,就需要调整 参数1
  4. 停机等待时间T。要满足(参数2+参数3) < T < 参数1

假设业务允许的最大请求时延是10s,LoadBalancer的过期时间也可以设置为10s,这样停机等待时间可以设置为23s,这样我们只需要通过Dockerfile设置ENV SERVER_SLEEP_TIME="23"并且添加启动参数--spring.cloud.loadbalancer.cache.ttl=10即可实现服务的优雅停机。所需脚本如下:

Dockerfile 复制代码
FROM registry.cn-hangzhou.aliyuncs.com/supx/openjdk:17-ea-slim
ENV SERVER_NAME="smooth-server"
ENV SERVER_PORT="1002"

ENV NACOS_SERVER_ADDR="main.supx.tech:8848"
ENV NACOS_NAMESPPACE_ID="f5ae6f36-5ed6-46ae-b07a-4c9f86f1f286"

# 权重降为0后等待停机时间
ENV SERVER_SLEEP_TIME="23"

#nacos 基础配置
ENV NACOS_BASE_CONF="--spring.cloud.nacos.server-addr=${NACOS_SERVER_ADDR} --spring.cloud.nacos.config.namespace=${NACOS_NAMESPPACE_ID} --spring.cloud.nacos.discovery.namespace=${NACOS_NAMESPPACE_ID}"
# nacos其他配置
ENV NACOS_CONF=""

ENV JAVA_OPTS="-Xms256m -Xmx256m"
ENV SPRING_OPTS="--spring.cloud.loadbalancer.cache.ttl=10 --spring.application.name=${SERVER_NAME} --server.port=${SERVER_PORT}"

WORKDIR /${SERVER_NAME}
COPY ./target/${SERVER_NAME}-1.0.0-SNAPSHOT.jar ${SERVER_NAME}-1.0.0-SNAPSHOT.jar
COPY entrypoint.sh entrypoint.sh

EXPOSE ${SERVER_PORT}
ENTRYPOINT ["./entrypoint.sh"]
#CMD ["/bin/bash","-c","java -jar ${SERVER_NAME}-1.0.0-SNAPSHOT.jar"]

entrypoint脚本内容如下:

shell 复制代码
#!/bin/bash

# 定义应用启动命令
start_command="java ${JAVA_OPTS} -jar ${SERVER_NAME}-1.0.0-SNAPSHOT.jar ${SPRING_OPTS} ${NACOS_BASE_CONF} ${NACOS_CONF}" "$@"
echo "启动命令:$start_command"
# 启动Java应用并获取其PID
$start_command &
pid=$!

# 定义一个函数来处理信号,并将其发送给Java进程
term_handler() {
    echo "Caught SIGTERM/SIGINT, stopping the application..."
    #服务降级处理
    ifconfig_output=$(ifconfig)
    ip_address=$(echo "$ifconfig_output" | grep -oE 'inet (addr:)?([0-9]{1,3}.){3}[0-9]{1,3}' | grep -oE '([0-9]{1,3}.){3}[0-9]{1,3}' | grep -v '127.0.0.1')
    curl -d "serviceName=${SERVER_NAME}" -d "ip=${ip_address}" -d "port=${SERVER_PORT}" -d "weight=0" -d "namespaceId=${NACOS_NAMESPPACE_ID}" -X PUT "${NACOS_SERVER_ADDR}/nacos/v2/ns/instance"
    echo "等待${SERVER_SLEEP_TIME}s..."
    sleep ${SERVER_SLEEP_TIME}
    #关闭程序
    kill -TERM "$pid"
    wait "$pid"
    exit 0
}

# 捕获SIGTERM和SIGINT信号
trap term_handler SIGTERM SIGINT

# 等待子进程结束或接收到信号
wait "$pid"

echo "Application has stopped."

优缺点

优点 很明显,只需要修改Dockerfile就能实现优雅停机,不需要改动应用。
缺点主要表现在以下几点

  1. NacosAPI,不能保证可用。测试时偶尔会出现API不可用的情况,需要删除/data/protocol数据。如果调用失败了,脚本不能阻止Pod被杀掉,也就不能实现优雅停机。这是这个方案最大的问题,依赖NacosAPI!
  2. 基础镜像需要安装curl net-tools,用来获取IP、调用NacoAPI。
  3. 默认情况下,本地配置无法覆盖配置中心的配置,需要在配置中心添加以下配置
yaml 复制代码
spring:
  cloud:
    config:
      overrideSystemProperties: false

思考

  1. 因为NacosAPI存在偶发问题,可以通过添加自定义接口的方式,用代码来控制停机逻辑。也可以重写spring-boot-starter-actuatorshutdownEndpoint。因为nacos的停机逻辑是在静态代码块中注册的退出钩子,因此只能通过外部调用触发停机逻辑,否则无法实现自定义停机。
  2. 有人建议直接使用反注册的接口,不要先降级,因为这样会有两次交互。但是实际测试发现,NacosAPI的注销实例接口达不到预期效果,请求还是会被路由到要下线的节点上,不知道用代码控制会不会好点。

参考: Nacos 实现服务平滑上下线(Ribbon 和 LB)_java控制nacos服务下线-CSDN博客

相关推荐
毅航18 分钟前
MyBatis 事务管理:一文掌握Mybatis事务管理核心逻辑
java·后端·mybatis
我的golang之路果然有问题33 分钟前
速成GO访问sql,个人笔记
经验分享·笔记·后端·sql·golang·go·database
柏油42 分钟前
MySql InnoDB 事务实现之 undo log 日志
数据库·后端·mysql
写bug写bug2 小时前
Java Streams 中的7个常见错误
java·后端
Luck小吕3 小时前
两天两夜!这个 GB28181 的坑让我差点卸载 VSCode
后端·网络协议
M1A13 小时前
全栈开发必备:Windows安装VS Code全流程
前端·后端·全栈
蜗牛快跑1233 小时前
github 源码阅读神器 deepwiki,自动生成源码架构图和知识库
前端·后端
嘻嘻嘻嘻嘻嘻ys3 小时前
《Vue 3.4响应式超级工厂:Script Setup工程化实战与性能跃迁》
前端·后端
橘猫云计算机设计3 小时前
net+MySQL中小民营企业安全生产管理系统(源码+lw+部署文档+讲解),源码可白嫖!
数据库·后端·爬虫·python·mysql·django·毕业设计
执念3653 小时前
MySQL基础
后端