You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

AWS EKS节点刷新/重启时无法实现零停机,5XX报错如何解决?

问题根因

本次15秒业务中断是多层配置缺失共同导致的流量时序错配,具体拆解:

  • 首先是调度规则缺陷:SCG两个副本全部调度在node1单节点上,没有配置Pod反亲和策略,一旦node1被选中缩容,两个SCG副本都在驱逐范围内。配置的PDB minAvailable=1仅能保证两个副本串行驱逐、不会同时被强杀,无法阻止两个副本先后进入终止状态,直接导致SCG服务的旧副本池被依次清空。
  • 其次是ASG缩容选点逻辑异常:初始状态下node4无任何业务Pod,属于最优缩容对象,但默认缩容策略未优先选择空节点,反而选中了运行核心网关副本的node1,直接触发了单节点上两个网关副本的驱逐流程。
  • 核心触发点是Pod终止与端点同步的时序差:SCG Pod收到终止信号后,Kubernetes会先将Pod标记为Terminating状态,再由Endpoint控制器将Pod IP从Service后端列表中摘除,但这个变更同步到所有节点kube-proxy、更新iptables/ipvs规则,再到上游papi侧的负载均衡客户端刷新可用后端缓存,默认最长需要10-15秒,和观测到的中断时长完全吻合。这段时间内papi仍会向已经停止服务的旧SCG Pod发请求,直接得到503响应。
  • 最后是优雅停机配置缺失:SCG没有配置preStop摘流等待逻辑,收到SIGTERM后直接开始关闭进程,没有预留足够时间等上游缓存刷新;同时没有开启Spring Boot优雅停机,已进入的请求也会被直接中断。

观测到的「scg新副本未就绪阶段无新增503」恰好可以印证这个逻辑:当两个旧SCG副本完全终止后,Service后端列表已经清空旧IP,papi侧缓存也完成同步,请求会暂时等待新副本就绪,不会向已失效的旧IP发流量,自然不会产生503错误。

零停机落地方案

按优先级从高到低落地以下配置,即可覆盖节点重启/缩容/刷新场景的零停机要求:

基础架构层修复

  • 给SCG、papi这类无状态核心服务强制配置Pod反亲和规则,禁止同一应用的多副本调度到同一节点,SCG作为网关可额外配置跨可用区反亲和,把故障域缩小到单副本单节点,从根源避免单节点驱逐影响整个服务。反亲和配置示例片段:
    affinity:
      podAntiAffinity:
        requiredDuringSchedulingIgnoredDuringExecution:
        - labelSelector:
            matchLabels:
              app: spring-cloud-gateway
          topologyKey: kubernetes.io/hostname
    
  • 调整ASG缩容逻辑,配合cluster-autoscaler配置--scale-down-priority=least-waste、--scale-down-utilization-threshold=0.5,优先缩容运行Pod最少、资源利用率最低的节点,让空节点(比如本次场景里的node4)成为第一缩容优先级,从源头避免终止运行业务Pod的节点。
  • 替换现有Lambda触发排空的逻辑,部署官方aws-node-termination-handler组件,在节点收到ASG终止预告的第一时间(比节点实际关机早1-2分钟)就启动drain流程,给足Pod迁移和摘流的时间窗口,不要等节点进入Terminating状态才开始排空。
  • 修正PDB配置,反亲和配置到位后,SCG的PDB保持minAvailable=1即可,确保单节点驱逐时始终有至少1个健康SCG副本承接流量。

应用与K8s层配置

  • 给SCG、papi配置preStop生命周期钩子,第一步先主动下线服务、让readiness探针返回失败,触发快速摘流,然后sleep 15秒(这个时间要大于集群内kube-proxy规则同步+客户端缓存刷新的最大延迟,可根据集群规模调整到10-20秒),等所有上游都把当前Pod从可用后端列表里剔除后,再给进程发终止信号。配置示例:
    lifecycle:
      preStop:
        exec:
          command:
          - /bin/sh
          - -c
          - curl -s -X POST http://127.0.0.1:8080/actuator/health/readiness-down 2>/dev/null; sleep 15
    
  • 开启Spring Boot/SCG的优雅停机能力,配置server.shutdown=graceful,设置spring.lifecycle.timeout-per-shutdown-phase=20s,确保收到终止信号后,已经进来的请求能正常处理完成再退出,不强制中断连接。
  • 调整kube-proxy配置,把iptables-min-sync-period、ipvs-min-sync-period设为1秒,加快端点变更的同步速度,缩短规则同步的时间窗口。
  • 调整kubelet配置,设置--shutdown-grace-period=30s,节点关机时给Pod预留足够的优雅终止时间,不要用默认的短窗口强杀Pod。

流量层兜底

  • papi侧调用SCG的负载均衡客户端,配置缓存刷新间隔为2秒,打开主动健康检查,同时配置重试策略:碰到连接错误、503响应时,自动重试1次其他可用的SCG副本,把端点同步窗口期的偶发错误消化在客户端侧,不透传给上游。
  • 如果入口用AWS ALB,把目标组健康检查间隔设为2秒,不健康阈值设为2,打开目标组慢启动,让新启动的SCG副本能逐步接流,避免刚启动就被打满。

内容的提问来源于stack exchange,提问作者kinglion VistaJIN

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.30 09:00:53