☰
Kubernetes Ingress(NGINX)VirtualServer 流量分割实战:90/10 灰度发布的配置与源码原理
2026/10/14 17:26:34 网站建设 项目流程
  • API网关
  • 云原生
  • 负载均衡
  • 可观测性
  • 微服务

【免费下载链接】kubernetes-ingress

NGINX and NGINX Plus Ingress Controllers for Kubernetes

项目地址:https://gitcode.com/gh_mirrors/ku/kubernetes-ingress
点击查看免费下载

本文基于 NGINX Ingress Controller for Kubernetes(kubernetes-ingress)仓库中的 traffic-splitting 示例,讲解如何使用VirtualServer自定义资源(CRD)将/coffee路径的流量按 90% / 10% 权重分配到coffee-v1-svc与coffee-v2-svc两个服务版本上。读完本文,你将能够:独立完成灰度/金丝雀发布的 VirtualServer 配置;理解splits.weight的校验规则与取值约束;并看懂 Controller 底层如何把 splits 翻译成 NGINX 的split_clients指令。

示例场景:在 Basic Configuration 基础上做流量分割

该示例建立在 Basic Configuration 示例 的 cafe 应用之上,但做了两处简化与扩展:

  • coffee 服务不再是单一版本,而是拆分为coffee-v1-svc和coffee-v2-svc两个 Service。90% 的咖啡流量发往coffee-v1-svc,其余 10% 发往coffee-v2-svc;
  • 为简化示例,移除了 TLS 终止(TLS termination)以及 tea 服务。

也就是说,本例演示的是:同一入口、同一路径下,按权重把请求分发到不同后端版本——这正是灰度发布(canary release)与 A/B 测试的典型诉求。

前置条件

  1. 按照安装说明部署 Ingress Controller,并启用自定义资源(custom resources enabled)。安装方式可参考仓库中的 deployments 目录(内含 DaemonSet/Deployment/StatefulSet 多种部署清单)与 rbac 清单。

  2. 将 Ingress Controller 的公网 IP 保存到 shell 变量:

    IC_IP=XXX.YYY.ZZZ.III
  3. 将 Ingress Controller 的 HTTP 端口保存到 shell 变量:

    IC_HTTP_PORT=<port number>

第 1 步:部署 Cafe 应用(双版本后端)

使用 cafe.yaml 创建两个版本的 Deployment 与 Service。文件共定义 4 个对象:

apiVersion: apps/v1 kind: Deployment metadata: name: coffee-v1 spec: replicas: 2 selector: matchLabels: app: coffee-v1 template: metadata: labels: app: coffee-v1 spec: containers: - name: coffee-v1 image: nginxdemos/nginx-hello:plain-text ports: - containerPort: 8080 --- apiVersion: v1 kind: Service metadata: name: coffee-v1-svc spec: ports: - port: 80 targetPort: 8080 protocol: TCP name: http selector: app: coffee-v1 --- # coffee-v2 的 Deployment 与 Service 结构完全相同 # (Deployment: coffee-v2 / Service: coffee-v2-svc,选择器为 app: coffee-v2)

关键细节:

  • 两个版本都使用nginxdemos/nginx-hello:plain-text镜像,该镜像会在响应中返回 Pod 名称与地址,便于在测试阶段直观区分请求落到了哪个版本;
  • 每个 Deployment 默认replicas: 2,Service 对外暴露 80 端口,转发到容器的 8080 端口;
  • 90% / 10% 的权重是"版本级"的:请求命中 coffee-v1 这一 split 后,会在其 2 个 Pod 之间再做常规负载均衡。

部署命令:

kubectl create -f cafe.yaml

第 2 步:配置负载均衡——VirtualServer 与 splits

使用 cafe-virtual-server.yaml 创建 VirtualServer 资源,完整定义如下:

apiVersion: k8s.nginx.org/v1 kind: VirtualServer metadata: name: cafe spec: host: cafe.example.com upstreams: - name: coffee-v1 service: coffee-v1-svc port: 80 - name: coffee-v2 service: coffee-v2-svc port: 80 routes: - path: /coffee splits: - weight: 90 action: pass: coffee-v1 - weight: 10 action: pass: coffee-v2

配置要点:

  • spec.upstreams将逻辑上游名(coffee-v1、coffee-v2)映射到 Kubernetes Service 名称与端口;
  • spec.routes[0].splits是流量分割的核心字段:每个 split 包含一个weight(权重)和一个action(动作)。本例中两个 split 的动作都是pass: <upstream名>,把请求转发给对应的上游;
  • 路由路径为/coffee,命中该路径的请求将按 90 : 10 的比例分流。

splits 字段定义与校验规则(源码佐证)

Split结构体定义在 类型定义文件:

// Split defines a split. type Split struct { // The weight of an action. Must fall into the range 0..100. // The sum of the weights of all splits must be equal to 100. Weight int `json:"weight"` // The action to perform for a request. Action *Action `json:"action"` }

其中Action支持pass、redirect、return、proxy四种形式(见 Action 定义),因此 splits 不仅可以按权重转发到不同上游,也可以按权重返回不同的 canned response 或重定向。

校验逻辑位于 VirtualServerValidator.validateSplits,它强制以下三条约束,违反任意一条资源都会被拒绝:

  1. 至少 2 个 split——len(splits) < 2时直接报must include at least 2 splits;
  2. 每个 weight 必须在 0..100 范围内——使用validation.IsInRange(s.Weight, 0, 100)校验;
  3. 所有 split 的 weight 之和必须恰好等于 100——totalWeight != 100时报the sum of the weights of all splits must be equal to 100。

此外,splits还出现在Match结构中(Match 定义),即高级内容路由(advanced content-based routing)里基于 header/cookie/argument/variable 的每个 match 也可以各自携带一套 splits,实现"先匹配、再按权重分"的组合路由。CRD 中的字段描述与上述一致,见 VirtualServer CRD。

第 3 步:验证配置是否生效

  1. 检查 VirtualServer 的 Events,确认配置已被 Controller 处理:

    kubectl describe virtualserver cafe

    预期输出(节选):

    . . . Events: Type Reason Age From Message ---- ------ ---- ---- ------- Normal AddedOrUpdated 5s nginx-ingress-controller Configuration for default/cafe was added or updated

    若配置不合法,这里会看到Rejected事件,原因即上一节所列的校验失败信息。

  2. 使用 curl 的--resolve选项,把cafe.example.com解析到 Ingress Controller 的 IP 与端口,多次请求观察流量分布:

    curl --resolve cafe.example.com:$IC_HTTP_PORT:$IC_IP http://cafe.example.com:$IC_HTTP_PORT/coffee

    约 90% 的响应来自coffee-v1-svc:

    Server address: 10.16.0.151:80 Server name: coffee-v1-78754bdcfb-7xp27 ...

    约 10% 的响应来自coffee-v2-svc:

    Server address: 10.16.0.152:80 Server name: coffee-v2-7fd446968b-lwhgcd ...

    由于客户端 IP 固定,同一客户端对 split_clients 的哈希结果是稳定的,因此多次请求通常落在同一版本;要直观看到 90/10 的比例,可以更换来源 IP 或借助多个客户端并发压测来统计。

原理深潜:splits 如何变成 NGINX 的 split_clients

从源码结构看,Controller 并不直接生成简单的proxy_pass权重配置,而是把每组分流编译成 NGINX 原生的split_clients模块 +map+ location 的组合。

  1. 配置生成:virtualserver.go 中的generateSplits函数遍历 route 的Splits,为每个 split 分配唯一的 keyval zone / map / split_clients 变量名(命名规则见 VariableNamer 的一系列GetNameOf*ForSplitClientIndex方法),再为每个 split 生成对应的 location(pass/proxy动作)或 return/redirect location。generateDefaultSplitsConfig(第 2414 行)则把 route 的默认 splits 组装成完整的配置片段,包括把 split_clients 结果映射到具体 location 的map块。

  2. 模板渲染:生成的SplitClients列表最终由 VirtualServer 模板 渲染进 nginx.conf:

    {{- range $sc := .SplitClients }} split_clients {{ $sc.Source }} {{ $sc.Variable }} { {{- range $d := $sc.Distributions }} {{ $d.Weight }} {{ $d.Value }}; {{- end }} } {{- end }}

    对本例而言,最终会渲染出类似split_clients <source> <variable> { 90 <v1-loc>; 10 <v2-loc>; }的指令:NGINX 对source(默认按客户端地址等可哈希值)做哈希,按 90/10 权重稳定地映射到两个 split 的结果变量,再由map与 location 完成实际转发。NGINX Plus 版本使用对应的 nginx-plus 模板,渲染逻辑相同。

  3. 权重热更新优化:从源码结构看,当仅修改 split 权重时存在一条"免 reload"的快车道——generateSplitsForWeightChangesDynamicReload(virtualserver.go)会把权重组合编码进 split_clients 的变量名(GetNameOfSplitClientsForWeights),配合 keyval 缓存,使权重变更可以走动态配置通道而非完整的 nginx reload。该行为有专门测试覆盖,例如 split_clients 索引测试 与 weight 动态更新测试、虚拟服务器路由的 weight 变更测试。对操作者的实际含义是:线上调小/调大灰度比例(例如 10 → 20)是低风险操作,无需担心全量 reload 带来的抖动。

小结

  • 流量分割的最小可运行配置就是routes[].splits[]:每个 split 一个weight(0..100)加一个action,权重总和必须等于 100,且至少 2 个 split;
  • 完整可复制的资源文件在 cafe.yaml 与 cafe-virtual-server.yaml;
  • 想在此基础上叠加基于 header/cookie 的条件路由,可参考仓库中 advanced-routing 示例;想按 VirtualServerRoute 拆分子路由,可参考 VS/VSRoute 相关 CRD 文档。
  • API网关
  • 云原生
  • 负载均衡
  • 可观测性
  • 微服务

【免费下载链接】kubernetes-ingress

NGINX and NGINX Plus Ingress Controllers for Kubernetes

项目地址:https://gitcode.com/gh_mirrors/ku/kubernetes-ingress
点击查看免费下载

相关推荐

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询