先看现象。FastAPI 的 SSE 接口跑在 Deployment 里,滚动发布时前端 EventSource 每 30 秒断一次,浏览器控制台报 net::ERR_INCOMPLETE_CHUNKED_ENCODING,Pod 日志里只有一句 Shutting down,把前端重连间隔调长只是把报错推迟。
这类报错背后常见的原因有几处,逐个排除就行。terminationGracePeriodSeconds 默认 30 秒。也可能是uvicorn 收到 SIGTERM 就停止接受新连接。
还有一种情况,没有 preStop 等 Endpoint 摘除完成。
另外,网关 keep-alive 复用了即将退出的连接。
先看 Deployment 的宽限期与钩子
kubectl get deploy api -o jsonpath="{.spec.template.spec.terminationGracePeriodSeconds}{'\n'}{.spec.template.spec.containers[0].lifecycle}"
再往下加 preStop 并调大宽限期
kubectl patch deploy api --type=merge -p '{"spec":{"template":{"spec":{"terminationGracePeriodSeconds":120,"containers":[{"name":"api","lifecycle":{"preStop":{"exec":{"command":["sleep","10"]}}}}]}}}}'
起服务时打开优雅关闭超时
uvicorn app:app --host 0.0.0.0 --port 8000 --timeout-graceful-shutdown 60
发布期间挂一条流观察断开时间
curl -N -H "Accept: text/event-stream" http://api.internal/stream
隔了一天再看,现象没变,说明不是偶发。
那几天正好在做别的变更,先把时间线对齐才敢动手。
排查用的命令都是临时敲的,没有写成脚本,事后才整理。
改完先别撤,确认这几项。滚动发布期间 curl 的流在 60 秒后正常结束,前端不再出现 30 秒周期性的重连,错误日志归零。
这里还有个坑。preStop 的 sleep 必须小于宽限期,否则 kubelet 会在钩子执行中直接发 SIGKILL,等于没配。这类问题最好一次改到位,反复小改反而容易把原因搅混。
