Kubernetes灰度优雅切换
原创2025年4月28日大约 3 分钟
Kubernetes灰度优雅切换
在K8s中实现灰度升级和优雅切换要实现下面目标:
- 已有连接继续用旧版本处理完,不中断;
- 新请求路由到新版本;
- 旧版本没有活动请求后再优雅下线(Kill)。
也即
Connection Draining(连接排空) + 新流量切换 + 优雅终止
实现步骤:
- 新版本启动并 Ready
- 新版本 Pod 创建完成,并标记为 Ready。
- Service 开始把新请求路由到新版本 Pod。
- 旧版本停止接收新请求,但继续处理已有连接
- 将旧 Pod 标记为 "Not Ready"(从 Service 的 Endpoints 移除)。
- 这样新的请求不会再打到旧 Pod,但已经到达旧 Pod 的连接还能继续处理。
- 旧 Pod 检测到没有活动请求后,再优雅退出(Kill)
- 等待一段时间(一般称为grace period);
- 如果还有活跃请求可以继续等,直到超时或者处理完后彻底关闭。
Kubernetes实操
1. 开启 PreStop Hook(重点)
在旧版本 Pod 定义 preStop
钩子,这样在 Pod 被删除(kubectl delete
或 rolling update)时,先执行一些动作,比如:
- 手动将自己从 Service 端点剔除(修改 readiness probe 状态为失败)
- 或者等待一段时间,排空连接。
示例:
lifecycle:
preStop:
exec:
command: ["/bin/sh", "-c", "sleep 30"]
意思是:Pod 被删时,不是立刻杀掉,而是先 sleep 30 秒,给它时间处理完旧连接。
2. 配置 readinessProbe(可选但推荐)
- Pod 里配好
readinessProbe
(就绪探针)。 - 只要
readinessProbe
不 Ready,Service 就不会把流量打过来。 - 在更新时,k8s 自动先等新 Pod Ready,旧 Pod不Ready后再切流量。
示例:
readinessProbe:
httpGet:
path: /healthz
port: 8080
initialDelaySeconds: 5
periodSeconds: 2
3. 设置合理的 terminationGracePeriodSeconds
默认 Pod 删除时,Kubernetes 会给 30 秒时间让进程优雅退出。你可以根据业务需要调整:
terminationGracePeriodSeconds: 60
如果设置 60s,K8s 在删除 Pod 时,会等 60s 后才强制杀掉 Pod。
4. 流量切换控制(高级用法)
如果希望更细粒度地控制,比如:
- 只有某个接口完成当前请求后马上切流
- 每个连接级别排空
可以配合使用:
- NGINX Ingress 的连接排空机制(connection draining)
- Istio 等 Service Mesh 的断流平滑处理。
比如 Istio 支持 Connection Pool Draining,可以做到请求级别的优雅切换。
小结
| 步骤 | 技术点 |
|:-|:|
| 新版本上线 | Deployment 滚动更新 |
| 旧版本停止新流量 | readinessProbe 设置为 Not Ready |
| 旧版本继续处理旧连接 | preStop Hook + terminationGracePeriodSeconds |
| 旧版本处理完后自动退出 | k8s 控制优雅删除流程 |
🎯 Kubernetes 灰度优雅切换 流程图
┌───────────────────────────────────────────┐
│ 用户请求 User Request │
└───────────────────────────────────────────┘
│
▼
┌───────────────────────────────────────────┐
│ Service 负载均衡流量到旧版本 Pod (v1) │
└───────────────────────────────────────────┘
│
▼
┌───────────────────────────────────────────┐
│ 发布新版本 Pod (v2) -> 正在启动 │
└───────────────────────────────────────────┘
│
( readinessProbe 探测中 )
│
(v2 Ready 后,加入 Service 流量池)
│
▼
┌───────────────────────────────────────────┐
│ 新请求部分或全部流向新版本 Pod (v2) │
└───────────────────────────────────────────┘
│
▼
┌───────────────────────────────────────────┐
│ 旧版本 Pod (v1) 开始优雅下线流程 │
│ - 设置 readinessProbe 为 Not Ready │
│ - 被 Service 从流量池剔除 │
└───────────────────────────────────────────┘
│
▼
┌───────────────────────────────────────────┐
│ 旧 Pod (v1) 只处理已有连接,不接受新请求 │
│ - 触发 preStop Hook │
│ - 等待 terminationGracePeriodSeconds 时间 │
└───────────────────────────────────────────┘
│
( 已有请求处理完 or 超时 )
│
▼
┌───────────────────────────────────────────┐
│ Kubernetes 杀掉旧版本 Pod (v1) │
└───────────────────────────────────────────┘
│
▼
┌───────────────────────────────────────────┐
│ 整个集群完全切换到新版本 Pod (v2) │
└───────────────────────────────────────────┘