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
相关产品推荐
相关产品推荐

