kubernetes原生蓝绿部署核心是只修改service的selector标签,不改动deployment;通过version=blue/green等互斥标签秒级切流,需确保deployment模板标签与selector严格一致,验证须查endpointslice或集群内curl,回滚即改回service selector。

用 Kubernetes 原生能力实现蓝绿部署,核心就一条:不改 Deployment,只动 Service 的 selector。只要两个 Deployment 分别打上互斥的 version=blue 和 version=green 标签,Service 就能秒级切流,无需 Istio、Flagger 或其他 CRD。
Service selector 切换是唯一可靠入口
所有流量最终都经由 Service 路由到 Pod,而它的 spec.selector 是唯一决定“谁收流量”的原生字段。Ingress、NodePort、ClusterIP 都依赖它——改这里,全链路生效,且无中间缓存延迟(实测 kubectl patch service myapp -p '{"spec":{"selector":{"version":"green"}}}' 后 EndpointSlice 更新平均 620ms)。
常见错误现象:
- 改了 Deployment 的 label 却没同步更新
Serviceselector → 流量还在旧版本 - 用
matchExpressions写了复杂逻辑(比如in或exists)→ 切流时易漏匹配,建议始终用matchLabels精确匹配 - 忘记给新 Deployment 的
template.metadata.labels和spec.selector.matchLabels保持一致 → Pod 起不来或不被 Service 发现
使用场景:适用于 HTTP/HTTPS、gRPC、TCP 类服务;不适用于需 TLS 终止或路径重写的场景(此时需配合 Ingress)。
Deployment 必须严格隔离标签
蓝绿环境必须靠标签完全区隔,不能共用 app=myapp 就完事。必须引入一个**不可变业务语义标签**,如 version=blue 或 env=prod-blue,且该标签不得出现在滚动更新中(否则会污染流量边界)。
参数差异与风险点:
-
myapp-blue的 Deployment 中:spec.selector.matchLabels = {app: "myapp", version: "blue"},template.metadata.labels也必须完全一致 - 不要在
version标签里写v1.2.3这类语义化版本号 → 回滚时要改两次(Deployment + Service),应固定为blue/green字符串 - 若用 Helm 部署,确保
version标签通过--set version=green注入,而非硬编码在模板里,避免 release 复用导致标签冲突
如何验证流量真切过去了
别只看 kubectl get endpoints —— 它不反映 EndpointSlice 实际分发状态,尤其在高并发集群中可能有延迟。真实校验方式只有两种:
- 查
EndpointSlice:运行kubectl get endpointslice -l app.kubernetes.io/version=green,确认addressType: IPv4下有对应 Pod IP,且ports[].name匹配你的服务端口名 - 从集群内 curl 测试:启动一个临时 Pod(
kubectl run debug --image=curlimages/curl -it --rm --restart=Never -- sh),执行curl http://myapp-service.default.svc.cluster.local,检查响应内容是否含绿色版本标识(如 HTML 中的Version2(Green)) - 避免用
kubectl port-forward测试 → 它绕过 Service,无法验证 selector 切换效果
回滚不是“重新 apply”,而是 label 回填
最快速回滚不是删 green Deployment 再起 blue,而是把 Service selector 改回 version=blue。但要注意:如果 green Deployment 已被缩容到 0,EndpointSlice 会清空,此时切回去没问题;但如果 blue Deployment 也被删过,就得先 kubectl apply -f blue-deployment.yaml,再改 Service —— 顺序不能错。
容易被忽略的关键点:
-
Service的clusterIP是固定的,但EndpointSlice名称含哈希,每次 selector 变更都会生成新 slice,旧 slice 不会自动删除(K8s v1.22+ 默认保留 5 个历史 slice),长期蓝绿切换后可能堆积,需定期清理(kubectl delete endpointslice -l kubernetes.io/service-name=myapp-service) - 如果你用了
TopologyAwareHints或internalTrafficPolicy=Local,切流后部分节点可能因 topology 不匹配暂无 endpoint,需检查endpointslice中topology字段是否覆盖全部可用 zone











