导读:本期聚焦于鱼儿创作的《Spring Boot 应用在 Kubernetes 中如何实现优雅停机与无缝滚动发布?》,敬请观看详情。滚动更新时Pod被直接杀死,正在处理的请求返回502,这是Spring Boot应用上Kubernetes后最常见的问题之一。本文从Kubernetes删除Pod的完整流程讲起,分析 terminationGracePeriodSeconds 、preStop 钩子与 Spring Boot 的 graceful shutdown 机制如何配合工作,讲解 readiness 探针在摘除流量中的作用,并给出一份经过生产验证的完整配置方案,覆盖 Nacos 注册中心延迟、长事务任务处理以及常见踩坑点,帮助你在发版过程中做到业务零感知。

Kubernetes 的滚动更新机制本身设计得相当完善,但不少团队把 Spring Boot 应用迁移上去之后,依然会在每次发版时观察到一波 502 错误或者注册中心里的调用失败。问题的根源不在于 Kubernetes,而在于应用侧和集群侧的配置没有形成配合。要让滚动发布真正做到业务无感知,需要理解 Kubernetes 杀死一个 Pod 的完整时序,并在此基础上把 Spring Boot 的优雅停机、健康检查探针和 preStop 钩子都调校到位。

Spring Boot 应用在 Kubernetes 中如何实现优雅停机与无缝滚动发布?

一、先搞清楚 Kubernetes 删除一个 Pod 时到底发生了什么

很多人配置优雅停机是凭感觉抄配置,结果线上还是报错。要彻底解决问题,必须从原理入手。当我们执行 kubectl delete pod,或者 Deployment 触发滚动更新时,Kubernetes 对旧 Pod 的处理遵循一套固定的时序。

第一步,Pod 的状态被标记为 Terminating,此时端点控制器会把这个 Pod 从 Service 的 endpoints 列表中移除,同时 kube-proxy 会更新 iptables 或 ipvs 规则。第二步,如果 Pod 定义了 preStop 钩子,kubelet 会先执行它,并且在钩子执行完毕之前,不会发送 SIGTERM 信号。第三步,preStop 执行完后 kubelet 发送 SIGTERM 给容器主进程,容器进入正常关闭流程。第四步,如果超过 terminationGracePeriodSeconds(默认 30 秒)进程还没退出,kubelet 会发送 SIGKILL 强制杀死进程。

这里有一个非常关键的隐藏问题:endpoints 的更新、kube-proxy 规则的同步、客户端负载均衡器的感知,这三件事都是异步发生的。也就是说,即便 Kubernetes 已经把 Pod 从 endpoints 移除了,仍可能有已经在传输路上的流量打到这个 Pod 上。这就是为什么只配置 Spring Boot 的优雅停机还不够,还需要 preStop 钩子来争取缓冲时间。

二、Spring Boot 侧的优雅停机配置

Spring Boot 从 2.3.0 开始原生支持优雅停机,通过 server.shutdown=graceful 开启。开启之后,应用收到 SIGTERM 时不再立刻关闭 HTTP 端口,而是停止接收新请求,把存量请求处理完再退出。等待的上限由 spring.lifecycle.timeout-per-shutdown-phase 控制,默认 30 秒。

推荐的配置如下,以 Spring Boot 2.7+ 为例,写在 application.yml 中:

server:
  shutdown: graceful
spring:
  lifecycle:
    timeout-per-shutdown-phase: 30s
management:
  endpoint:
    health:
      probes:
        enabled: true
  endpoints:
    web:
      exposure:
        include: health,info,metrics

注意几个细节。第一,优雅停机只对 MVC 同步请求生效,如果你用的是 WebFlux,行为略有不同但基本可用;而 SSE、WebSocket 这类长连接, graceful shutdown 并不会替你断开,需要自己在 shutdown 钩子里处理。第二,如果应用里还有定时任务、消息消费者,需要配合 @PreDestroy 或实现 SmartLifecycle 来先停止拉取新消息、再等待任务完成:

@Component
public class ConsumerShutdown implements SmartLifecycle {

    private volatile boolean running = true;

    @Override
    public void stop() {
        running = false; // 先停止消费新消息
    }

    @Override
    public boolean isRunning() {
        return running;
    }
}

三、Kubernetes 侧:readiness 探针与 preStop 钩子的黄金组合

Spring Boot 侧配置好后,接下来要让 Kubernetes 侧正确摘除流量。首先要启用 readiness 探针。readiness 探针失败时,Pod 会被从 Service endpoints 中移除,这是主动摘流量的手段。启动了 management.endpoint.health.probes.enabled 后,可以直接使用 /actuator/health/readiness 路径。

但这里有个反直觉的点:Pod 进入 Terminating 状态时,readiness 探针失败并不能完全解决问题,因为前文提到的异步延迟依然存在。所以生产环境的标准做法是给容器加上 preStop 钩子,强制 sleep 几秒,给 kube-proxy 和上游注册中心足够的反应时间:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: order-service
spec:
  replicas: 3
  template:
    spec:
      terminationGracePeriodSeconds: 45
      containers:
        - name: order-service
          image: registry.ippipp.com/order-service:1.8.0
          lifecycle:
            preStop:
              exec:
                command: ["sh", "-c", "sleep 10"]
          readinessProbe:
            httpGet:
              path: /actuator/health/readiness
              port: 8080
            initialDelaySeconds: 20
            periodSeconds: 5
            failureThreshold: 3

这段配置的完整时序是:Pod 被标记 Terminating → endpoints 移除 + readiness 开始失败 → preStop sleep 10 秒(此期间应用还在正常处理请求,流量已逐渐切走)→ SIGTERM 发出 → Spring Boot graceful shutdown 等待存量请求最多 30 秒 → 进程退出。注意 terminationGracePeriodSeconds 必须大于 preStop 时长加上 shutdown 超时,这里设置为 45 秒,留了少量余量。

四、注册中心延迟与常见踩坑点

如果服务走的是 Nacos、Eureka 这类注册中心,而不是纯 Service 直连,情况会更复杂。注册中心的客户端通常有本地缓存,消费者拉取服务列表存在周期性延迟,比如 Nacos 默认的推送和拉取机制下,一次下线通知到全量生效可能需要几秒。preStop 里的 sleep 时间要覆盖这个延迟,或者更彻底的方案是在 preStop 中主动调用注册中心的下线接口,把实例标记为不健康:

lifecycle:
  preStop:
    exec:
      command:
        - sh
        - -c
        - "curl -X DELETE 'http://nacos:8848/nacos/v1/ns/instance?serviceName=order-service&ip=${POD_IP}&port=8080'; sleep 8"

最后整理几个高频踩坑点:第一,把 terminationGracePeriodSeconds 忘了调大,默认 30 秒,preStop 加 shutdown 很容易超时被 SIGKILL 杀掉,前面的所有配置全部白费。第二,Java 应用打包成镜像时用了 shell 形式的 ENTRYPOINT,导致 1 号进程是 sh 而不是 java,SIGTERM 根本传不到 JVM,一定要用 exec 形式 ENTRYPOINT ["java", "-jar", "app.jar"]第三,readiness 探针的 initialDelaySeconds 太小,应用还没启动完就被反复标记失败,造成发布期间大量请求失败。第四,滚动更新策略建议配合 maxUnavailable: 0 和合理的 maxSurge,保证更新过程中可用副本数不下降。

把以上几层配置串联起来,Spring Boot 应用在 Kubernetes 上的滚动发布基本可以做到请求数为零丢失。核心思路只有一句话:让流量摘除先于进程终止发生,并给这个摘除过程足够的时间窗口。

Spring Boot优雅停机Kubernetes滚动发布preStop钩子修改时间:2026-09-09 01:10:49

免责声明:已尽一切努力确保本网站所含信息的准确性。网站作品多为原创整理与精心创作,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们进行处理Email:chomcom@qq.com。
引用或转载本作品时,请注明当前出处:https://www.ipipp.com/html/20260909/53092.html,基于非商业用途的前提下,欢迎转载或二创本作品。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。