K8S 生产排查台账:滚动更新发版偶发 502 (流量摘除延迟排查指南)
01 前置理论与核心机制
1. 为什么配了优雅停机依然会 502?
- 两条链路的时间差:K8s 下达删除 Pod 指令时,控制面(摘除路由)和数据面(关闭容器)是并行推进的,但生效时间完全不在一个节奏上。
- 数据面(关进程极快):Kubelet 收到指令后,会立刻向容器内的 1 号进程发送
SIGTERM信号。业务服务收到信号,瞬间关闭端口,闭门谢客。 - 控制面(摘路由极慢):K8s 需要先更新 Endpoints,再通知所有节点的 Kube-proxy 修改 iptables/IPVS,最后同步给 Ingress/网关。这条链路走完可能需要 2~3 秒。
2. 竞态条件引发的 502
- 时间差酿成事故:因为网关摘除路由慢了这两三秒,它还以为旧容器活着,继续把前端进来的新流量往里打。但此时旧容器的端口已经关了,网关连不上,直接向用户抛出 502 Bad Gateway 或 Connection Refused。
02 故障排查与恢复闭环 SOP
遇到发版瞬间流量轻微跌零或报 502,不需要去查网络玄学或修改就绪探针,直接干预 Pod 的退出流程。
步骤一:观测端点摘除的真实状态
查看服务背后的端点映射,了解路由变更过程:
# 持续观察 Service 的 Endpoints 变动
kubectl get endpoints demo-svc -w
- 观测点:看起来只是一个 Pod 被删了,实际上 K8s 内部发生了一连串的端点解绑动作,这段时间足以让旧容器提早关机。
步骤二:给容器配置增加 preStop 钩子
核心解决思路:让旧 Pod 收到终止信号后别急着退出,在原地强行等上十几秒,给网关留足时间把路由摘干净。
编辑业务的 Deployment YAML(如 app-v2.yaml),在容器的 lifecycle 中加入 preStop 配置:
containers:
- name: demo-app
image: demo-app:v2
lifecycle:
preStop:
exec:
# 收到删除指令后,先原地 sleep 15 秒,继续正常接客
command: ["/bin/sh", "-c", "sleep 15"]