已采纳
回答正文
回复
讨论正文
该问题的正文与回答。
12 节点 K8s 1.28.9 + Istio 1.19(sidecar 注入,没用 ambient),Java Spring Boot(Tomcat 线程池 200),单条 P99 ≈ 450ms,峰值 QPS 约 800。滚动更新 maxSurge=1、maxUnavailable=0。
每次发布,旧 Pod 被 kill 后 2~5 秒内,Istio access log 里稳定出现一小撮 503 upstream connect error / 503 upstream request timeout。时间点精确落在 main 容器收到 SIGTERM 之后、Envoy 还在 drain 的窗口里——也就是说 Envoy 还在往 main 转发新请求,但 main 已经开始关闭 Tomcat 线程池了。
不是偶发,是每次发布都有,量级约 0.5%~1% 的请求。
sleep 5 + terminationGracePeriodSeconds=30s → 503 减少但没消除。proxyConfig.drainDuration 默认 45s,parentShutdownDuration 默认 30s。这两个是 Envoy 自己 drain 用的,但 Kubelet 发 SIGTERM 给 main 和给 Envoy 是同一时刻,main 的 preStop 完全不知道 Envoy drain 到哪一步了。curl -s localhost:15020/ready 轮询,但 Envoy 的 /ready 端点反映的是"我还能接新连接",不是"我已经不再往 main 转发了"——语义不对。K8s 对 Pod 内所有容器同时发 SIGTERM、同时执行 preStop,没有"先 drain sidecar、再停 main"的内置顺序。所以:
/healthz/ready?/drain?还是直接 sleep 一个 ≥ drainDuration 的值?)一个具体的配置组合 + 时序说明:
drainDuration / parentShutdownDuration 要不要调、调到多少;restartPolicy: Always 的 init container)能改善这个时序,也欢迎说下差异。最好能画一下(文字描述也行):从 Kubelet 决定 kill 这个 Pod,到最后一个 in-flight 请求返回,中间每一步谁在等谁、超时阈值各是多少。
社区里 preStop / 优雅停机已经讨论了很多,但都聚焦在"Kubelet 侧给多少窗口",没有正面处理"sidecar 和 main 的 drain 是并行、谁等谁"这个时序问题: