发布策略
滚动更新、金丝雀发布(灰度发布)的实现与局限性。
1. 发布策略概述
| 策略 | 说明 | 风险 | 适用场景 |
|---|---|---|---|
| 滚动更新(Rolling Update) | 逐步替换旧版本 Pod | 低 | Deployment 默认策略,适用于大多数场景 |
| 金丝雀发布(Canary) | 小范围部署新版本,逐步扩大 | 中 | 需要按比例验证新版本的场景 |
| 蓝绿部署(Blue-Green) | 新旧两套环境,流量一次性切换 | 低(可秒级回滚) | 需要瞬时切换的场景 |
| A/B 测试 | 按用户特征分流到不同版本 | 中 | 需要按用户属性分流的场景 |
2. 滚动更新(Rolling Update)
Deployment 的默认更新策略。
spec:
replicas: 3
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1 # 更新期间最多允许多出几个 Pod
maxUnavailable: 1 # 更新期间最多允许几个 Pod 不可用| 参数 | 说明 | 默认值 |
|---|---|---|
maxSurge |
峰值时超出 replicas 的 Pod 数量 | 25% |
maxUnavailable |
更新中不可用的 Pod 最大数量 | 25% |
示例流程(replicas=3, maxSurge=1, maxUnavailable=1):
初始状态: [v1] [v1] [v1]
步骤1: [v1] [v1] [v1] [v2] (创建 1 个新 Pod)
步骤2: [v1] [v1] [v2] (删除 1 个旧 Pod)
步骤3: [v1] [v1] [v2] [v2] (创建 1 个新 Pod)
步骤4: [v1] [v2] [v2] (删除 1 个旧 Pod)
...
终态: [v2] [v2] [v2]相关命令:
# 更新镜像
kubectl set image deploy/nginx-deploy nginx=nginx:1.23
# 查看更新状态
kubectl rollout status deploy/nginx-deploy
# 查看历史版本
kubectl rollout history deploy/nginx-deploy
# 回滚
kubectl rollout undo deploy/nginx-deploy
kubectl rollout undo deploy/nginx-deploy --to-revision=1
# 暂停/恢复更新
kubectl rollout pause deploy/nginx-deploy
kubectl rollout resume deploy/nginx-deploy3. 金丝雀发布(Canary Deployment)
3.1 命名来源
早期矿工下井前,会先放入一只金丝雀检测井下是否有毒气。金丝雀对有毒气体更敏感,若金丝雀死亡则立即撤离。
对应到软件发布:先用少量用户验证新版本,确认无问题后再逐步推广到全部用户。
3.2 部署过程
1. 发布 v1 版本(100% 流量)
2. 发布 v2 版本(1 个实例,≈10-20% 流量)
3. 观察 v2 运行情况,逐步扩容 v2
4. 确认无误后,将 v1 缩容至 0
5. 清理 v1,v2 接替全部流量3.3 基于 Service Label Selector 的简化实现
核心思路:两个 Deployment 使用相同的 Label,Service 通过 Selector 自动将流量分发给两者。
步骤 1:部署 v1
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment-v1
namespace: dev
spec:
replicas: 3
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
version: v1
spec:
containers:
- name: nginx
image: nginx:1.22
ports:
- containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: canary-demo
namespace: dev
spec:
type: NodePort
selector:
app: nginx # 选所有 app=nginx 的 Pod
ports:
- port: 80
targetPort: 80
nodePort: 30008步骤 2:部署金丝雀版本(v2)
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment-canary
namespace: dev
spec:
replicas: 1 # 只有 1 个副本(金丝雀)
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx # 与 v1 相同的 Selector 标签
version: v2
track: canary # 区分标签
spec:
containers:
- name: new-nginx
image: docker/getting-started
ports:
- containerPort: 80步骤 3:逐步扩大 v2 占比
# 扩容 v2
kubectl scale deploy nginx-deployment-canary --replicas=3 -n=dev
# 观察 v2 运行正常后,下线 v1
kubectl scale deploy nginx-deployment-v1 --replicas=0 -n=dev3.4 流量分配原理
请求 → Service(selector: app=nginx) → 随机分配到 app=nginx 的所有 Pod
├── v1 Pod × 3 (75%)
└── v2 Pod × 1 (25%)由于 Service 的负载均衡是随机分配的,通过调整两个 Deployment 的副本数比例来控制流量分配。
4. 金丝雀发布的局限性(🚨 重要)
| 局限性 | 说明 |
|---|---|
| 无法基于请求内容分流 | Service 只在 TCP 层做负载均衡,不解析 HTTP 内容 |
| 无法按用户属性分流 | 不能根据用户 ID、地区、注册时间等分配流量 |
| 用户粘性缺失 | 同一用户的多次请求可能路由到不同版本 |
| 无精细流量控制 | 只能按 Pod 数量比例(粗粒度),不能精确到百分比 |
4.1 局限性示例
用户 A 第 1 次请求 → Service → v1 Pod
用户 A 第 2 次请求 → Service → v2 Pod (体验不一致)4.2 解决方案
对于需要精细流量控制的场景,应使用 Service Mesh(如 Istio、Linkerd)或 API 网关:
| 方案 | 能力 |
|---|---|
| Istio | 基于 HTTP Header/Cookie 的流量路由、权重百分比 |
| Linkerd | 金丝雀发布、流量分割 |
| Argo Rollouts | 声明式渐进式交付,支持蓝绿和金丝雀 |
| Nginx Ingress | 基于 Cookie/Header 的流量分割 |
| Traefik | K3s 内置 Ingress 控制器,支持金丝雀发布 |
5. 回滚策略
# 查看发布历史
kubectl rollout history deploy/nginx-deploy
# 查看指定版本详情
kubectl rollout history deploy/nginx-deploy --revision=3
# 快速回滚到上一版本
kubectl rollout undo deploy/nginx-deploy
# 回滚到指定版本
kubectl rollout undo deploy/nginx-deploy --to-revision=2💡 最佳实践:每次发布时使用
kubectl annotate记录变更原因,便于后续排查:kubectl annotate deploy/nginx-deploy kubernetes.io/change-cause="Upgrade nginx to 1.23"