🐤 什么是 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-cookiecanary-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)

  • 生成一个独立的 location block
  • 配置只包含主服务的 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 模式做精确分流