🐤 什么是 Canary 发布?
Canary 发布(又称灰度发布)是一种软件发布策略,其核心思想是:
将新版本的服务发布给少部分用户进行测试,在确认稳定后再逐步扩大流量,最终替换掉旧版本。
在 Kubernetes + NGINX Ingress 中,canary 发布一般通过 多个 Ingress 对象配合 annotations 来实现分流控制。
⚙️ NGINX Ingress 中的 Canary 实现方式
在使用 Ingress Controller 时,常见的 canary 分流方式包括:
nginx.ingress.kubernetes.io/canary: "true":标识此为 canary Ingress。nginx.ingress.kubernetes.io/canary-weight: "20":将 20% 的流量导向 canary 服务。
此外还有 canary-by-cookie 和 canary-by-header 等方式,但我们本次重点看 weight 分流。
✅ 主服务 + Canary 服务的 YAML 示例(基于 weight)
🟦 主 Ingress(处理大部分流量)
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: echo-main
namespace: default
spec:
rules:
- host: echo.local
http:
paths:
- path: /echo
pathType: Prefix
backend:
service:
name: echo-main-svc
port:
number: 80
🟨 Canary Ingress(处理少部分流量)
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: echo-canary
namespace: default
annotations:
nginx.ingress.kubernetes.io/canary: "true"
nginx.ingress.kubernetes.io/canary-weight: "20" # 20% 灰度流量
spec:
rules:
- host: echo.local
http:
paths:
- path: /echo
pathType: Prefix
backend:
service:
name: echo-canary-svc
port:
number: 80
🧪 分析请求日志行为
假设你启用了如下日志格式(或类似):
log_format upstreamlog '$remote_addr - $remote_user [$time_local] '
'"$request" $status $body_bytes_sent '
'"$http_referer" "$http_user_agent" '
'[$proxy_upstream_name] [$proxy_alternative_upstream_name]';
$proxy_upstream_name:本次请求实际命中的 upstream$proxy_alternative_upstream_name:如果 NGINX 有 fallback upstream,也会记录
🎯 访问主服务命中的情况(80% 概率)
你会在日志中看到:
[default-echo-main-svc-80] []
- ✅ 命中了主服务
- ❌ 不会记录备用 upstream(因为该请求未走包含 canary 的分流逻辑)
🎯 访问 Canary 服务命中的情况(20% 概率)
你会在日志中看到:
[default-echo-canary-svc-80] [default-echo-main-svc-80]
- ✅ 实际走的是 canary 服务
- ✅ 主服务作为备用 upstream 也在 location 中,因此会记录为 alternative upstream
🔍 为什么会出现这种差异?
NGINX Ingress Controller 会这样生成配置逻辑:
🎯 对于主 Ingress(非 canary)
- 生成一个独立的
locationblock - 配置只包含主服务的 upstream
- 所以请求到主服务时,只记录一个 upstream
🎯 对于 canary Ingress(带 weight)
- 控制器会将主 + canary 的转发规则合并到一个 shared location block
- 并添加逻辑进行权重判断,如:
location /echo {
set $target_upstream "default-echo-main-svc-80";
random_number $request_id;
if ($request_id < 20) {
set $target_upstream "default-echo-canary-svc-80";
}
proxy_pass http://$target_upstream;
}
- 所以只有当请求进入了这个 shared block(即命中了 canary)时,才会记录两个 upstream
🧠 总结
| 请求命中服务 | $proxy_upstream_name |
$proxy_alternative_upstream_name |
原因 |
|---|---|---|---|
| 主服务(80%) | ✅ 主服务 | ❌ 空 | 请求走主 ingress 的独立 location |
| Canary 服务(20%) | ✅ canary 服务 | ✅ 主服务 | 请求走合并后的 shared location(主+canary) |
如果你希望更进一步,比如:
- 查看实际 NGINX 的
/etc/nginx/nginx.conf - 模拟大批量请求看命中比例
- 或者切换到
canary-by-header模式做精确分流
















