先看现象。K8s 1.28 上做灰度发布,新旧两个 Deployment 各 5 个副本,回滚时把 Service 的 selector 切回旧版本,切完还有三成请求打到新版本。Envoy 的 upstream 连接池 keep-alive 是 300 秒,客户端复用了升级前建立的长连接,试过逐个删 Pod 和重启客户端,只有重启 Envoy 才恢复。
把日志和配置对完,能解释这个现象的基本就这几处。Envoy 的 upstream 连接池按 cluster 缓存,改路由不会踢掉已有连接。也可能是HTTP/2 连接是多路复用,一条连接会持续承载请求。
另外,Pod 删除时没等 draining 完成,旧副本还在处理存量请求。
少数情况下是客户端有 DNS 缓存,解析到的还是切换前的地址。
上来先看连接池的活跃连接数
curl -s http://127.0.0.1:9901/stats | grep -E 'upstream_cx_(active|total)'
回滚时先摘流量再缩容旧版本
kubectl patch svc api -p '{"spec":{"selector":{"version":"v1"}}}'
给旧 Pod 发 drain 让连接自然排空
kubectl exec -it api-v2-0 -- curl -X POST http://127.0.0.1:8080/healthz/drain
剩下的就是确认连接数归零后再删 Pod
curl -s http://127.0.0.1:9901/clusters | grep -A3 'api-v2'
怎么算真的好了,看的是同一批现象。回滚后新版本的请求计数归零,active 连接降到 0 才缩容,监控里两侧流量曲线干净断开。
连接池和路由是两层,改 selector 不会踢掉已经建立的连接,回滚流程里要留一步排空,否则会看到流量拖尾。同一份配置在测试环境跑通,不代表线上没事,两边的默认值经常不一样。
