跳到主要内容

a6-recipe-canary

概览

灰度发布会将流量从稳定版本逐步迁移到新版本。先从较小比例(例如 5%)开始,观察错误率和延迟,再逐步提高比例,直到新版本承载 100% 的流量。如果任一阶段出现问题,可以立即回滚。

本方案使用 traffic-split 插件,在稳定版本和灰度版本的上游之间按权重分配流量。

适用场景

  • 以尽可能小的影响范围部署新版本
  • 在全面推出之前,使用实际生产流量验证更改
  • 您需要通过监控检查点逐步推出
  • 您希望在错误检测时自动或脚本化回滚

分步操作:灰度发布

1. 创建稳定版本和灰度版本的上游

a6 upstream create -f - <<'EOF'
{
"id": "stable",
"type": "roundrobin",
"nodes": {
"stable-v1:8080": 1
}
}
EOF

a6 upstream create -f - <<'EOF'
{
"id": "canary",
"type": "roundrobin",
"nodes": {
"canary-v2:8080": 1
}
}
EOF

2. 从 5% 灰度流量开始

a6 route create -f - <<'EOF'
{
"id": "api",
"uri": "/api/*",
"plugins": {
"traffic-split": {
"rules": [
{
"weighted_upstreams": [
{
"upstream_id": "canary",
"weight": 5
},
{
"weight": 95
}
]
}
]
}
},
"upstream_id": "stable"
}
EOF

3. 监控并提高到 25%

检查错误率、延迟和日志。如果正常:

a6 route update api -f - <<'EOF'
{
"plugins": {
"traffic-split": {
"rules": [
{
"weighted_upstreams": [
{
"upstream_id": "canary",
"weight": 25
},
{
"weight": 75
}
]
}
]
}
}
}
EOF

4. 提高到 50%

a6 route update api -f - <<'EOF'
{
"plugins": {
"traffic-split": {
"rules": [
{
"weighted_upstreams": [
{
"upstream_id": "canary",
"weight": 50
},
{
"weight": 50
}
]
}
]
}
}
}
EOF

5. 提高到 100%(完成发布)

移除流量拆分配置,将灰度版本切换为新的稳定版本:

a6 route update api -f - <<'EOF'
{
"plugins": {},
"upstream_id": "canary"
}
EOF

随后更新“稳定版本”上游的节点,为下一次发布做好准备:

a6 upstream update stable -f - <<'EOF'
{
"nodes": {
"canary-v2:8080": 1
}
}
EOF

回滚(适用于任一阶段)

移除流量拆分插件,将全部流量恢复到稳定版本:

a6 route update api -f - <<'EOF'
{
"plugins": {},
"upstream_id": "stable"
}
EOF

高级用法:基于请求头的灰度发布

将特定用户(例如内部测试人员)的请求路由到灰度版本:

a6 route update api -f - <<'EOF'
{
"plugins": {
"traffic-split": {
"rules": [
{
"match": [
{
"vars": [["http_x-canary", "==", "true"]]
}
],
"weighted_upstreams": [
{
"upstream_id": "canary",
"weight": 1
}
]
}
]
}
},
"upstream_id": "stable"
}
EOF

仅带有 x-canary: true 请求头的请求会进入灰度版本,其他请求仍由稳定版本处理。

将主动加入测试的用户路由到灰度版本:

{
"plugins": {
"traffic-split": {
"rules": [
{
"match": [
{
"vars": [["cookie_beta", "==", "1"]]
}
],
"weighted_upstreams": [
{
"upstream_id": "canary",
"weight": 1
}
]
}
]
}
}
}

灰度推进脚本

#!/bin/bash
set -euo pipefail

ROUTE_ID="api"
CANARY_UPSTREAM="canary"
WEIGHTS=(5 25 50 75 100)
HEALTH_URL="http://gateway:9080/api/health"
WAIT_SECONDS=300 # 5 minutes between stages

for w in "${WEIGHTS[@]}"; do
if [ "$w" -eq 100 ]; then
echo "Promoting canary to 100%..."
a6 route update "$ROUTE_ID" -f - <<EOF
{"plugins": {}, "upstream_id": "$CANARY_UPSTREAM"}
EOF
else
stable_w=$((100 - w))
echo "Setting canary to ${w}% (stable ${stable_w}%)..."
a6 route update "$ROUTE_ID" -f - <<EOF
{
"plugins": {
"traffic-split": {
"rules": [{
"weighted_upstreams": [
{"upstream_id": "$CANARY_UPSTREAM", "weight": $w},
{"weight": $stable_w}
]
}]
}
}
}
EOF
fi

echo "Waiting ${WAIT_SECONDS}s and checking health..."
sleep "$WAIT_SECONDS"

if ! curl -sf "$HEALTH_URL" > /dev/null; then
echo "❌ Health check failed at ${w}%. Rolling back."
a6 route update "$ROUTE_ID" -f - <<EOF
{"plugins": {}, "upstream_id": "stable"}
EOF
exit 1
fi
echo "✅ Healthy at ${w}%"
done

echo "🎉 Canary release complete!"

配置同步示例

version: "1"
upstreams:
- id: stable
type: roundrobin
nodes:
"stable-v1:8080": 1
- id: canary
type: roundrobin
nodes:
"canary-v2:8080": 1
routes:
- id: api
uri: /api/*
plugins:
traffic-split:
rules:
- weighted_upstreams:
- upstream_id: canary
weight: 10
- weight: 90
upstream_id: stable

故障排查

症状原因解决方法
流量比例不精确轮询分配产生短期偏差这是预期行为;请求数量增加后,实际比例会逐渐接近配置值
灰度版本未收到流量匹配条件未生效检查请求头或 Cookie 名称,并使用 a6 route get 验证配置
回滚未立即生效插件配置尚在传播APISIX 更新通常在毫秒级生效;使用 a6 route get 核对当前配置
灰度流量返回 502灰度上游不健康发布前检查灰度服务的健康状态
权重更改未生效修改了错误的路由使用 a6 route list 确认路由 ID

本文根据 api7/a6 仓库中的 a6-recipe-canary/SKILL.md 生成。可在 AI Agent Skills 页面浏览全部 Skill。