发布系统:发布策略
滚动 / 蓝绿 / 金丝雀 / 灰度 在 Kubernetes & Nginx 中的详细落地操作指南。
apiVersion: apps/v1
kind: Deployment
metadata:
name: app
spec:
replicas: 5
strategy:
type: RollingUpdate
rollingUpdate:
maxUnavailable: 1
maxSurge: 1
template:
spec:
terminationGracePeriodSeconds: 30
containers:
- name: app
image: app:v2
readinessProbe:
httpGet:
path: /health
port: 8080
kubectl set image deployment/app app=app:v2
kubectl rollout status deployment/app
kubectl rollout undo deployment/app
📌 关键点
- readinessProbe 决定是否接流量
- 优雅关闭防止请求中断
- 不支持真正灰度
Nginx 本身不擅长滚动,通常是:
- 后端实例自己重启
- Nginx 无感知
Service selector 切换(最经典)
# 蓝
labels:
app: demo
version: blue
# 绿
labels:
app: demo
version: green
selector:
app: demo
version: blue
kubectl patch svc demo \
-p '{"spec":{"selector":{"app":"demo","version":"green"}}}'
👉 秒级切换,秒级回滚
upstream blue {
server 10.0.0.1;
server 10.0.0.2;
}
upstream green {
server 10.0.0.3;
server 10.0.0.4;
}
location / {
proxy_pass http://green;
}
nginx -s reload
📌 回滚 = 改回 blue + reload
kind: Ingress
metadata:
name: app-stable
spec:
rules:
- host: app.example.com
metadata:
name: app-canary
annotations:
nginx.ingress.kubernetes.io/canary: "true"
nginx.ingress.kubernetes.io/canary-weight: "5"
表示:
- 5% 流量 -> 新版本
canary-weight: "10"
canary-weight: "0"
upstream backend {
server 10.0.0.1 weight=95;
server 10.0.0.2 weight=95;
server 10.0.0.3 weight=5; # 新版本
}
📌 优点
- 简单
- 快
📌 缺点
- 同一用户可能命中不同版本
灰度 = 规则驱动
可以基于 用户 / Header / Cookie / 地域
annotations:
nginx.ingress.kubernetes.io/canary-by-header: "X-Gray"
nginx.ingress.kubernetes.io/canary-by-header-value: "true"
客户端:
X-Gray: true
nginx.ingress.kubernetes.io/canary-by-cookie: "gray"
map $http_x_gray $backend {
default stable;
true canary;
}
upstream stable {
server 10.0.0.1;
}
upstream canary {
server 10.0.0.2;
}
location / {
proxy_pass http://$backend;
}
split_clients "${http_userid}" $gray {
5% canary;
* stable;
}
geo $gray {
default stable;
10.1.0.0/16 canary;
}
| 方式 | K8S 实现 | Nginx 实现 | 回滚 |
|---|---|---|---|
| 滚动 | Deployment | ❌ | 中 |
| 蓝绿 | Service 切 selector | upstream 切换 | 秒级 |
| 金丝雀 | Ingress 权重 | upstream weight | 快 |
| 灰度 | Ingress 规则 | map/split_clients | 极快 |
蓝绿 + 金丝雀
金丝雀 + 灰度规则 + Feature Toggle
- readinessProbe 是否正确
- 是否支持优雅关闭
- 数据库是否向前兼容
- 监控是否按版本区分
- 是否支持一键回滚
Kubernetes 负责“部署形态”,Nginx / Ingress 负责“流量策略”。
这是一个非常核心、非常“生产级”的问题 👍
因为 不解决“命中一致性”,蓝绿 / 金丝雀 / 灰度在真实业务里就是“看运气”。
下面是按 「原理 -> 实现 -> Kubernetes / Nginx 实操 -> 风险点」 的一套可落地、可审计、可回滚的方案。
确保“同一个客户 / 终端 / 用户”,在灰度期间:
- ✅ 始终命中新版本 OR 始终命中旧版本
- ❌ 不随机跳版本
- ❌ 不同请求命中不同实例
这叫:
👉 流量一致性(Traffic Stickiness)
| 方式 | 稳定性 | 推荐级别 |
|---|---|---|
| 用户 ID / 客户 ID Hash | ⭐⭐⭐⭐⭐ | 强烈推荐 |
| Cookie 固化 | ⭐⭐⭐⭐⭐ | 强烈推荐 |
| Header 标识 | ⭐⭐⭐⭐ | 推荐 |
| JWT Claim | ⭐⭐⭐⭐ | 推荐 |
| IP Hash | ⭐⭐⭐ | 兜底 |
| 权重随机 | ⭐⭐ | 不推荐用于灰度 |
✅ 最佳实践总公式
客户标识 -> 稳定映射 -> 后端版本
原理
- 同一用户 -> 同一 hash 值
- hash 命中固定 upstream
配置
split_clients "${http_x_user_id}" $version {
10% canary;
* stable;
}
upstream stable {
server 10.0.0.1;
}
upstream canary {
server 10.0.0.2;
}
location / {
proxy_pass http://$version;
}
📌 特点
- 同一用户永远命中同一版本
- 改比例不影响已命中的用户
写入 Cookie(首次命中)
map $cookie_release $release {
default "";
blue blue;
green green;
}
if ($release = "") {
add_header Set-Cookie "release=green; Path=/; Max-Age=86400";
}
proxy_pass http://$release;
📌 适合
- Web 用户
- 前端可控场景
map $http_x_release $backend {
default stable;
green green;
}
客户端:
X-Release: green
📌 特点
- 人为可控
- 可点对点灰度
示例
map $jwt_claim_release $backend {
default stable;
green green;
}
📌 优点
- 安全
- 不易被篡改
- 非常适合 SaaS
upstream backend {
ip_hash;
server 10.0.0.1;
server 10.0.0.2;
}
📌 缺点
- NAT / 代理下不可靠
- 不适合移动端
Header 灰度
annotations:
nginx.ingress.kubernetes.io/canary: "true"
nginx.ingress.kubernetes.io/canary-by-header: "X-User-Type"
nginx.ingress.kubernetes.io/canary-by-header-value: "vip"
Cookie 灰度
annotations:
nginx.ingress.kubernetes.io/canary-by-cookie: "release"
Ingress 本质也是 Nginx
可通过:
- configuration-snippet
- server-snippet
nginx.ingress.kubernetes.io/configuration-snippet: |
split_clients "${http_x_user_id}" $release {
5% canary;
* stable;
}
- Service 本身 只负责负载
- 不适合灰度规则
- 灰度应在 Ingress / 网关
👉 指定客户命中 = 强规则
推荐:
- Header / Cookie / JWT
- 明确指定版本
客户 -> 蓝
其他 -> 绿
👉 指定客户 + Hash
推荐:
VIP 客户 -> 新版本
普通客户 -> Hash 决定
👉 规则优先级
强制规则(Header / Cookie)
↓
用户 Hash
↓
权重随机(兜底)
- 是否使用 稳定 Key(user_id / client_id)
- 是否避免纯随机 weight
- 是否记录用户 -> 版本映射日志
- 是否支持强制切回
- 是否支持指定客户名单(白名单)
Ingress / Nginx 网关层:
Header / JWT / Cookie -> Hash -> upstream
K8S Deployment:
只负责跑 Pod,不负责灰度逻辑
灰度能不能用,关键不在发布方式,而在“流量是否一致命中”。
这个理解是「完全正确,而且非常成熟」的。
先给结论:
蓝绿 / 金丝雀 / 灰度,本质上不是“运维技术”,而是“系统工程”,单靠运维(Nginx / K8S)只能做一半,必须前后端配合。
下面把这件事讲清楚、讲透、讲到可落地。
| 能力 | 运维能不能单独完成 |
|---|---|
| 滚动发布 | ✅ 可以 |
| 蓝绿(整体切流) | ✅ 可以 |
| 金丝雀(按比例) | ⚠️ 勉强可以 |
| 灰度(指定用户 / 客户) | ❌ 不行 |
| 保证用户命中一致 | ❌ 不行 |
| 精准客户回滚 | ❌ 不行 |
👉 一旦涉及“人 / 用户 / 客户 / 终端”,运维就不够了。
Nginx / Ingress 看到的是:
IP / Header / Cookie / Path
它 不知道:
- 谁是 VIP
- 谁是测试用户
- 谁的数据重要
- 谁可以先升级
例如:
- 客户等级
- 用户角色
- 租户类型
- App 版本
- 渠道来源
👉 这些 只存在于业务系统里
命中 -> 使用 -> 监控 -> 回滚 -> 固化
单靠运维,只能做到:
命中
职责:
- 流量转发
- 规则执行
- 不理解业务
能力上限:
- Header / Cookie / Hash
- 权重
- 路由
职责:
- 用户识别
- 灰度决策
- 写 Cookie / Header
- Feature Toggle
例如:
{
"user_id": 123,
"gray": true,
"release": "green"
}
职责:
- 透传标识
- 控制入口
- 灰度能力感知
例如:
X-Release: green
| 维度 | 是否需要配合 |
|---|---|
| 整体切换 | 不需要 |
| 指定客户 | 需要 |
| 数据兼容 | 需要后端 |
👉 运维能切流,但业务要兜底
| 维度 | 是否需要配合 |
|---|---|
| 比例发布 | 运维可做 |
| 用户一致性 | 需要后端 |
| 指定客户 | 必须前后端 |
| 维度 | 是否需要配合 |
|---|---|
| 灰度规则 | 后端决定 |
| 灰度标识 | 前端透传 |
| 强制回滚 | 后端控制 |
👉 灰度 = 运维 + 后端 + 前端
- 判断是否灰度用户
- 返回灰度标识
- 写 Cookie / JWT Claim
- 保存标识
- 请求携带标识
- 根据标识路由
前端 -> Header / Cookie
后端 -> 决策 & 标识
Nginx -> 路由
因为手工配合迟早崩。
成熟方案一定有:
- 灰度用户表
- 灰度规则配置
- 灰度开关
- 一键回滚
- 监控与报警
👉 这已经不是运维系统,是 发布系统 / 变更系统。
现在的认知已经是:
- ❌ “Nginx 能搞定一切”
- ✅ “发布是系统协作工程”
这是 高级运维 / 架构师思维。
运维解决“怎么发”
后端决定“发给谁”
前端保证“能命中”