Lattice Hub 文档
最佳实践

灰度发布

配置多灰度与治理规则灰度的实际配置、社区 Demo、触发验证与可观察效果。

灰度发布的核心不是“多一个开关”,而是把编辑态生效态分开:规则或配置先完成编辑与校验,再通过不可变版本进入客户端可见的 active 视图。

gray release practice

适用对象

对象发布单元客户端读取
配置中心Config File ReleaseGetConfigFile / 长轮询拿到的发布版本
治理规则Rule ReleaseDiscover 的 active release 缓存视图

服务实例、MCP Server、A2A Agent 走注册发现与 revision,不套用同一套“草稿 → active release”生命周期。

标准流程

  1. 编辑草稿 — Console / API 改配置或规则本体;运行时不变。
  2. 发布前校验 — 鉴权、参数、资源存在性;失败则不生效。
  3. 选择范围 — 全量或灰度(先定客户端标签 / 环境范围)。
  4. 观察 — 客户端是否拉到新版本、业务错误率、History。
  5. 全量或回滚 — 确认后全量;异常则回滚或按条停灰度。

A. 配置中心多灰度(推荐先跑通)

实际配置是什么

同一文件可有 1 条 active 全量 + 多条 active 灰度(按 releaseName 区分)。灰度发布体示例:

{
  "namespace": "default",
  "group": "docs-multi-gray",
  "file_name": "app.yaml",
  "name": "gray-env-a",
  "release_description": "gray for env=gray-a",
  "release_type": "gray",
  "beta_labels": [{
    "key": "env",
    "value": { "type": 0, "value": "gray-a", "value_type": 0 }
  }]
}

全量把 release_type 设为 normal,且不带 beta_labels。文件内容示例:

版本content
全量mode: baseline / version: 1
灰度 Amode: gray-a / cohort: env=gray-a
灰度 Bmode: gray-b / cohort: env=gray-b

语义细节见 配置中心操作

社区 Demo

入口说明
本站脚本website/scripts/demo-config-multi-gray.sh:登录 → 发 1 全量 + 2 灰度 → 客户端拉配置断言
控制面烟测pole-control-plane/console/web/scripts/smoke-configuration-flow.mjs:多灰 / promote / stop / rollback
文档 GIF配置中心 · 多灰度 中的客户端 curl 演示
# 在 website 仓库根目录
./scripts/demo-config-multi-gray.sh

环境变量:POLE_BASE_URL(Console,默认 http://pole.localhost)、POLE_CLIENT_URL(客户端 HTTP API,默认 http://127.0.0.1:8090)。

怎么触发验证

CLIENT="${POLE_CLIENT_URL:-http://127.0.0.1:8090}"
FILE_Q='namespace=default&group=docs-multi-gray&fileName=app.yaml&version=0'

# 正式全量
curl -sS "$CLIENT/v1/GetConfigFile?$FILE_Q" \
  | jq '{name:.file.name, type:.file.release_type, content:.file.content}'

# 命中灰度 A / B
curl -sS "$CLIENT/v1/GetConfigFile?$FILE_Q&tags=env%3Dgray-a" | jq '{name:.file.name, type:.file.release_type, content:.file.content}'
curl -sS "$CLIENT/v1/GetConfigFile?$FILE_Q&tags=env%3Dgray-b" | jq '{name:.file.name, type:.file.release_type, content:.file.content}'

Console:配置中心 → 分组 docs-multi-gray → app.yaml → 发布记录 → 灰度发布

效果是什么

客户端 tagsrelease_type内容特征
(无)或 env=othernormalmode: baseline
env=gray-agraymode: gray-a
env=gray-bgraymode: gray-b
  • 全量发布不结束已有 active 灰度。
  • 停灰度按单条 releaseName;停 A 不影响 B。
  • 同时命中多条灰度时,控制面按 version → mtime 取最新。

B. 治理规则灰度(路由 / 限流 / 泳道)

实际配置是什么

规则本体与 release 分离。社区 e2e fixture(pole-control-plane/test/e2e/internal/e2e/fixtures.go)中的形状:

路由 — Header x-tenant=vip → 目的实例标签 version=v1

{
  "name": "vip-route-demo",
  "enable": true,
  "priority": 10,
  "routing_config": {
    "@type": "type.googleapis.com/v1.RuleRoutingConfig",
    "caller": { "namespace": "default", "service": "order" },
    "callee": { "namespace": "default", "service": "payment" },
    "rules": [{
      "name": "vip-route",
      "arguments": {
        "matchMode": "AND",
        "arguments": [{
          "type": "HEADER", "key": "x-tenant",
          "value": { "type": "EXACT", "value": "vip", "value_type": "TEXT" }
        }]
      },
      "destinations": [{
        "namespace": "default", "service": "payment",
        "name": "primary", "weight": 100,
        "labels": {
          "version": { "type": "EXACT", "value": "v1", "value_type": "TEXT" }
        }
      }]
    }]
  }
}

限流 — 方法 POST /api/v1/payments + x-tenant=vip,本地 QPS 120,超限 REJECT

{
  "name": "vip-rl-demo",
  "namespace": "default",
  "service": "payment",
  "enable": true,
  "priority": 10,
  "rules": [{
    "method": { "type": "EXACT", "value": "POST /api/v1/payments" },
    "arguments": [{
      "type": "HEADER", "key": "x-tenant",
      "value": { "type": "EXACT", "value": "vip", "value_type": "TEXT" }
    }],
    "amounts": [{ "validDuration": "1s", "maxAmount": 120 }],
    "resource": "QPS", "type": "LOCAL", "action": "REJECT",
    "failover": "FAILOVER_LOCAL"
  }]
}

泳道 — 入口服务匹配后,目的带 lane=blue(完整字段见 fixture LaneGroup)。

灰度 release 体(只让带标签的客户端看见新规则):

{
  "rule_id": "<id>",
  "rule_name": "vip-route-demo",
  "resource": "RouteRules",
  "version": 2,
  "release_name": "e2e-gray",
  "release_type": "ReleaseTypeGray",
  "client_labels": [{
    "key": "e2e-gray",
    "value": { "type": "EXACT", "value": "true", "value_type": "TEXT" }
  }],
  "active": true
}

全量把 release_type 设为 ReleaseTypeNormal,去掉 client_labels

社区 Demo

入口说明
e2e fixturespole-control-plane/test/e2e/internal/e2e/fixtures.goRouteRule / RateLimitRule / LaneGroup / GrayRuleRelease
e2e 用例test/e2e/console_api(创建→发布→灰度→停灰→回滚)、test/e2e/client(Discover 可见性)
Console 操作路由限流泳道
# 在 pole-control-plane 仓库(需可连控制面的 e2e 环境)
CGO_ENABLED=0 go test -tags=e2e ./test/e2e/console_api/... -count=1
CGO_ENABLED=0 go test -tags=e2e ./test/e2e/client/... -count=1

怎么触发验证

  1. Console:治理工作台 → 新建路由/限流/泳道 → 按上表填规则 → 发布(先选灰度 + client_labels)。
  2. 或 HTTP:POST /naming/v1/routings(或 ratelimits / lane/groups)→ POST .../releases
  3. 客户端 Discover:
curl -sS -X POST "$CLIENT/naming/v1/Discover" \
  -H 'Content-Type: application/json' \
  -d '{
    "type": "CUSTOM_ROUTE_RULE",
    "service": { "namespace": "default", "name": "payment" },
    "filter": {}
  }' | jq .

限流把 type 换成 RATE_LIMIT。带灰度标签的客户端应看到灰度 release;未命中标签的仍看全量。

效果是什么

规则触发条件预期效果
路由请求带 x-tenant: vip流量落到 version=v1 实例集合;权重合计 100%
限流VIP 路径 QPS > 120/s被限请求 REJECT;未匹配租户不受影响
泳道入口命中泳道规则下游只选带 lane=blue 的目的
规则灰度客户端带 e2e-gray=trueDiscover 返回灰度版本规则;未带标签仍为正式版

停灰度 / 回滚后,Discover 文本中对应 release 名消失或回到上一 active。


检查清单

  • Namespace 选对,不是误发到生产环境
  • 目标服务 / 配置 / 规则存在且 revision 可追踪
  • 发布人具备对应 Publish 权限
  • 灰度范围可解释、可收回(配置按 releaseName,治理按 client_labels
  • 已用客户端接口验证(配置用 GetConfigFile,治理用 Discover),不只看 Console 列表
  • 回滚路径已确认;History 可看到本次变更

反模式

  • 直接改库或绕过 Console/API 写入“生效数据”。
  • 把未发布草稿当成客户端已生效配置。
  • 只截 Console 空列表/徽章,不做客户端拉配置 / Discover 对照。
  • 无观察窗口的一次性全量发布。
  • 用 Pole Agent 自动发布或删除资源。

深入阅读

On this page