285 lines
9.8 KiB
Markdown
285 lines
9.8 KiB
Markdown
|
|
---
|
|||
|
|
tags: [microservice, canary-release, blue-green, traffic-routing, istio, load-balancing]
|
|||
|
|
create time: 2026-05-05 10:30
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
# 流量治理
|
|||
|
|
|
|||
|
|
## 概述
|
|||
|
|
|
|||
|
|
灰度发布(也叫金丝雀发布)是降低变更风险的核心手段。它让你将新版本逐步推向一小部分用户,观察指标正常后再全量。
|
|||
|
|
|
|||
|
|
> [!question] 如何安全地上线?
|
|||
|
|
> 你修复了一个紧急 Bug,但这次改动涉及核心支付链路。直接全量发布一旦出问题损失巨大。有没有办法先小范围验证,确认没问题再放量?
|
|||
|
|
|
|||
|
|
答案就是**按规则拆分流量**——将请求按照不同维度分配到新旧版本,用真实用户的流量来检验新代码,而不是靠测试环境的人为模拟。
|
|||
|
|
|
|||
|
|
```mermaid
|
|||
|
|
flowchart LR
|
|||
|
|
A["🟢 v1 (稳定版)<br/>95% 流量"] --> B["🔵 v2 (测试版)<br/>5% 流量"]
|
|||
|
|
B --> C{"监控指标?"}
|
|||
|
|
C -- "✅ 一切正常" --> D["逐步放量<br/>10% → 25% → 50%"]
|
|||
|
|
C -- "❌ 错误率飙升" --> E["自动回滚到 v1 🔄"]
|
|||
|
|
D --> F{"全部放量至 100%"}
|
|||
|
|
F --> G["🟢 v2 成为新稳定版"]
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 一、灰度策略全景
|
|||
|
|
|
|||
|
|
### 常见灰度策略对比
|
|||
|
|
|
|||
|
|
| 策略 | 描述 | 优点 | 缺点 | 适用场景 |
|
|||
|
|
|------|------|------|------|---------|
|
|||
|
|
| **按百分比** | 随机分配 N% 流量到新版本 | 简单粗暴,覆盖面广 | 可能只命中特定用户 | 通用场景 |
|
|||
|
|
| **按用户 ID** | 指定用户/内部账号走新版 | 可精准控制测试范围 | 样本不够代表性 | 内部验收阶段 |
|
|||
|
|
| **按 Header** | `x-canary: true` 路由到新版本 | 灵活性最高 | 需要客户端配合 | QA / AB 测试 |
|
|||
|
|
| **按地域** | 某个城市/机房的新版本 | 地域性问题的理想试验场 | 地域偏差大 | 全球化部署 |
|
|||
|
|
| **按设备类型** | iOS 新用户先用新版 | 覆盖目标人群 | 样本有限 | 移动端发版 |
|
|||
|
|
|
|||
|
|
> [!tip] 实战建议:多策略组合
|
|||
|
|
> 生产环境中通常不会只用单一规则。推荐的做法是分层判断:
|
|||
|
|
>
|
|||
|
|
> ```
|
|||
|
|
> 第1层:内部员工 IP → 100% 进灰度(快速发现 bug)
|
|||
|
|
> 第2层:header x-canary=true → 100% 进灰度(QA 验证)
|
|||
|
|
> 第3层:新注册用户 → 50% 进灰度(扩大样本)
|
|||
|
|
> 第4层:其余用户 → 默认 v1
|
|||
|
|
> ```
|
|||
|
|
|
|||
|
|
### 按权重分流的 Go 实现
|
|||
|
|
|
|||
|
|
在应用层面(如 go-zero 或自研网关),流量分配往往通过中间件实现:
|
|||
|
|
|
|||
|
|
```go
|
|||
|
|
// CanaryMiddleware: header 携带 canary=true 的请求直通灰度版本
|
|||
|
|
func (m *CanaryMiddleware) Next(ctx context.Context, req any, reply any, rpc func(context.Context, any, any) error) error {
|
|||
|
|
if m.r.isGray(ctx) { // 从 header/context 提取灰度标记
|
|||
|
|
return m.grayHandler(ctx, req, reply) // 路由到灰度服务实例
|
|||
|
|
}
|
|||
|
|
return m.next(ctx, req, reply) // 走正常路径
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 客户端负载均衡中的灰度
|
|||
|
|
|
|||
|
|
客户端侧的灰度通常通过自定义负载算法实现,以加权轮询为例:
|
|||
|
|
|
|||
|
|
```go
|
|||
|
|
// WeightedRoundRobin: 权重感知轮询
|
|||
|
|
type WeightedRoundRobin struct {
|
|||
|
|
servers []*Server // 包含 weight 字段
|
|||
|
|
totalWeight int
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func (w *WeightedRoundRobin) Next() (*Server, error) {
|
|||
|
|
// 每次选取当前权重最高的可用服务器
|
|||
|
|
// v1 权重 95, v2 权重 5 → 平均每 20 次请求 1 次命中 v2
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 二、蓝绿 vs 灰度
|
|||
|
|
|
|||
|
|
> [!summary] 两种发布策略对比
|
|||
|
|
>
|
|||
|
|
> | 维度 | 蓝绿部署 | 灰度发布 (金丝雀) |
|
|||
|
|
> |------|---------|-----------------|
|
|||
|
|
> | **切换方式** | 一次性全切 | 逐步放量 |
|
|||
|
|
> | **资源消耗** | 双倍(新旧并行) | 初期少量副本 |
|
|||
|
|
> | **回滚速度** | 秒级(切回 LB 即可) | 同样秒级 |
|
|||
|
|
> | **风险等级** | 中高(全量暴露问题) | 低(渐进式) |
|
|||
|
|
> | **适合场景** | 大型变更、重大版本 | 日常迭代、高风险链路 |
|
|||
|
|
|
|||
|
|
### 蓝绿部署流程
|
|||
|
|
|
|||
|
|
```mermaid
|
|||
|
|
stateDiagram-v2
|
|||
|
|
[*] --> Blue: 初始状态
|
|||
|
|
Blue --> DeployGreen: 部署 v2 到 Green 环境
|
|||
|
|
DeployGreen --> TestGreen: 验证 Green
|
|||
|
|
TestGreen --> Switch: 切换入口流量
|
|||
|
|
Switch --> Green: 全部流量到 Green/v2
|
|||
|
|
Green --> CleanupBlue: 删除 Blue/v1
|
|||
|
|
CleanupBlue --> [*]
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 金丝雀发布流程
|
|||
|
|
|
|||
|
|
```mermaid
|
|||
|
|
graph TB
|
|||
|
|
S1["v1 运行中<br/>100% 流量"]
|
|||
|
|
S2["部署 v2<br/>5% 流量"]
|
|||
|
|
S3["监控 15min"]
|
|||
|
|
S4{"P99 延迟 < SLA?"}
|
|||
|
|
|
|||
|
|
S4 -- 否 --> Rollback["回滚 v2 🔄"]
|
|||
|
|
S4 -- 是 --> S5["放量 25%"]
|
|||
|
|
S5 --> S6["监控 30min"]
|
|||
|
|
S6 --> S7{"错误率 < 0.1%?"}
|
|||
|
|
S7 -- 否 --> Rollback
|
|||
|
|
S7 -- 是 --> S8["放量 50%"]
|
|||
|
|
S8 --> S9["监控 1h"]
|
|||
|
|
S9 --> S10{"全项达标?"}
|
|||
|
|
S10 -- 是 --> S11["100% 全量 ✅"]
|
|||
|
|
S10 -- 否 --> Rollback
|
|||
|
|
|
|||
|
|
style Rollback fill:#ffebee
|
|||
|
|
style S11 fill:#e8f5e9
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
> [!warning] 关键决策点
|
|||
|
|
> 每个放量节点都是一个"继续 or 回滚"的决策门控。不要跳过任何一步,哪怕之前几轮都顺利通过。历史教训表明:**很多事故发生在最后一跳**。
|
|||
|
|
|
|||
|
|
## 三、高级路由规则
|
|||
|
|
|
|||
|
|
### Istio VirtualService
|
|||
|
|
|
|||
|
|
Istio 作为 Service Mesh 方案,流量治理能力强大但侵入性也更高。适合已有 K8s + Istio 基础设施的团队。
|
|||
|
|
|
|||
|
|
```yaml
|
|||
|
|
# 灰度规则:header 携带 canary=true 的用户走 v2
|
|||
|
|
apiVersion: networking.istio.io/v1beta1
|
|||
|
|
kind: VirtualService
|
|||
|
|
metadata:
|
|||
|
|
name: order-route
|
|||
|
|
spec:
|
|||
|
|
hosts: ["order-service"]
|
|||
|
|
http:
|
|||
|
|
# 灰度规则
|
|||
|
|
- match:
|
|||
|
|
- headers:
|
|||
|
|
x-canary:
|
|||
|
|
exact: "true"
|
|||
|
|
route:
|
|||
|
|
- destination:
|
|||
|
|
host: order-service
|
|||
|
|
subset: v2
|
|||
|
|
weight: 100
|
|||
|
|
# 默认:全部走 v1
|
|||
|
|
- route:
|
|||
|
|
- destination:
|
|||
|
|
host: order-service
|
|||
|
|
subset: v1
|
|||
|
|
weight: 100
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 权重路由示例
|
|||
|
|
|
|||
|
|
```yaml
|
|||
|
|
# 按比例分流
|
|||
|
|
http:
|
|||
|
|
- route:
|
|||
|
|
- destination:
|
|||
|
|
host: order-service
|
|||
|
|
subset: v1
|
|||
|
|
weight: 90
|
|||
|
|
- destination:
|
|||
|
|
host: order-service
|
|||
|
|
subset: v2
|
|||
|
|
weight: 10
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 网关层 vs Sidecar 层
|
|||
|
|
|
|||
|
|
流量治理可以在两个不同的层级实现,选择取决于团队的基础设施成熟度:
|
|||
|
|
|
|||
|
|
```mermaid
|
|||
|
|
flowchart TD
|
|||
|
|
subgraph "网关层(中心化)"
|
|||
|
|
GW["API Gateway<br/>集中管理路由规则"]
|
|||
|
|
GW --> V1[[v1 实例集群]]
|
|||
|
|
GW --> V2[[v2 实例集群]]
|
|||
|
|
end
|
|||
|
|
|
|||
|
|
subgraph "Sidecar 层(分布式)"
|
|||
|
|
GW2["API Gateway<br/>只做转发"]
|
|||
|
|
GW2 --> ProxyV1["Envoy Sidecar<br/>本地执行路由规则"]
|
|||
|
|
GW2 --> ProxyV2["Envoy Sidecar<br/>本地执行路由规则"]
|
|||
|
|
ProxyV1 --> ContainerV1[[v1 容器]]
|
|||
|
|
ProxyV2 --> ContainerV2[[v2 容器]]
|
|||
|
|
end
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
| 维度 | 网关层治理 | Sidecar 层治理 |
|
|||
|
|
|------|-----------|---------------|
|
|||
|
|
| **复杂度** | 配置统一,易维护 | 需理解 Mesh 概念 |
|
|||
|
|
| **性能** | 单点瓶颈风险 | 就近处理,开销更低 |
|
|||
|
|
| **灵活性** | 依赖网关插件能力 | Istio 支持更丰富的规则 |
|
|||
|
|
| **学习曲线** | 低 | 高 |
|
|||
|
|
| **适用团队** | 中小型、快速起步 | 大规模微服务集群 |
|
|||
|
|
|
|||
|
|
> [!note] 选型建议
|
|||
|
|
> - **初创期**:直接在网关或应用层做路由分发即可,不必引入 Service Mesh
|
|||
|
|
> - **成长期**:当服务数量超过 30+、跨团队协作复杂时,考虑用 Istio 统一管理
|
|||
|
|
> - **混合模式**:网关层做简单的按路径/权重分发,Sidecar 层做精细的 header/metadata 路由
|
|||
|
|
|
|||
|
|
## 四、流量治理的其他维度
|
|||
|
|
|
|||
|
|
除了发布策略,流量治理还包括:
|
|||
|
|
|
|||
|
|
| 能力 | 说明 | 关联文档 |
|
|||
|
|
|------|------|---------|
|
|||
|
|
| **熔断降级** | 下游不可用时自动降级 | [[02-服务治理/06-容错模式]] |
|
|||
|
|
| **限流** | 保护上游不因过量请求被打垮 | [[02-服务治理/06-容错模式]] |
|
|||
|
|
| **重试** | 对瞬态故障自动恢复 | [[02-服务治理/06-容错模式]] |
|
|||
|
|
| **黑白名单** | IP 级别访问控制 | — |
|
|||
|
|
| **A/B Testing** | 基于用户分组的长期实验 | — |
|
|||
|
|
|
|||
|
|
## 五、灰度发布的量化决策依据
|
|||
|
|
|
|||
|
|
> [!tip] 必须有可量化的观测指标作为决策依据
|
|||
|
|
>
|
|||
|
|
> 不要凭感觉放量,让数据说话:
|
|||
|
|
>
|
|||
|
|
> | 指标 | 阈值参考 | 告警级别 |
|
|||
|
|
> |------|---------|---------|
|
|||
|
|
> | **错误率** | < 0.1% (v1 baseline 对比) | > 1% 立即回滚 |
|
|||
|
|
> | **P99 延迟** | ≤ v1 × 1.1 | > v1 × 1.3 暂停放量 |
|
|||
|
|
> | **CPU/Memory** | 在 limits 的 70% 以内 | > 85% 扩容 |
|
|||
|
|
> | **下游依赖成功率** | > 99.5% | < 95% 熔断 |
|
|||
|
|
|
|||
|
|
### 灰度监控面板设计要点
|
|||
|
|
|
|||
|
|
一个完整的灰度面板应该能一眼看出新旧版本的差异:
|
|||
|
|
|
|||
|
|
```mermaid
|
|||
|
|
flowchart LR
|
|||
|
|
subgraph RealTime["实时对比视图"]
|
|||
|
|
R1["📊 P99 延迟趋势<br/>v1 vs v2 双线对比"]
|
|||
|
|
R2["🔴 错误率柱状图<br/>区分 HTTP 状态码分类"]
|
|||
|
|
R3["⚡ QPS 流量分布<br/>按版本分堆叠面积图"]
|
|||
|
|
end
|
|||
|
|
|
|||
|
|
subgraph HealthCheck["健康检查"]
|
|||
|
|
H1["✅ 版本 v1 正常运行<br/>副本数: 10/10"]
|
|||
|
|
H2["⚠️ 版本 v2 异常<br/>副本数: 2/5 · 失败: 3"]
|
|||
|
|
end
|
|||
|
|
|
|||
|
|
RealTime --> Decision["🎯 是否继续放量?"]
|
|||
|
|
HealthCheck --> Decision
|
|||
|
|
|
|||
|
|
Decision -- "指标超标" --> Rollback["触发自动回滚"]
|
|||
|
|
Decision -- "指标正常" --> Expand["推进下一放量阶段"]
|
|||
|
|
|
|||
|
|
style Rollback fill:#ffebee,color:#000
|
|||
|
|
style Expand fill:#e8f5e9,color:#000
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 六、实践 checklist
|
|||
|
|
|
|||
|
|
> [!example] 灰度上线前必查清单
|
|||
|
|
>
|
|||
|
|
> - [ ] 旧版本保持至少一个副本不销毁(随时回滚)
|
|||
|
|
> - [ ] 灰度期间禁止其他无关变更上线
|
|||
|
|
> - [ ] 已配置好 v1 vs v2 的双线对比面板
|
|||
|
|
> - [ ] 定义了明确的放量阶段和触发条件(何时扩、何时停)
|
|||
|
|
> - [ ] 准备了回滚脚本 / 一键回滚操作
|
|||
|
|
> - [ ] 通知了相关干系人(运维、产品、客服)
|
|||
|
|
> - [ ] 选择了影响面最小的时间段开始灰度
|
|||
|
|
|
|||
|
|
## 关联笔记
|
|||
|
|
|
|||
|
|
- [[02-服务治理/01-API网关]] — API Gateway 的路由和权重分发
|
|||
|
|
- [[02-服务治理/06-容错模式]] — 熔断、限流等配套治理手段
|
|||
|
|
- [[04-可观测性/01-Metrics监控]] — 灰度期间的监控面板设计
|
|||
|
|
- [[05-部署运维/04-SRE实践]] — 基于 SLO 的发布决策
|