- API网关
- 云原生
- 负载均衡
- 可观测性
- 微服务
【免费下载链接】kubernetes-ingress
NGINX and NGINX Plus Ingress Controllers for Kubernetes
本文基于 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 测试的典型诉求。
前置条件
按照安装说明部署 Ingress Controller,并启用自定义资源(custom resources enabled)。安装方式可参考仓库中的 deployments 目录(内含 DaemonSet/Deployment/StatefulSet 多种部署清单)与 rbac 清单。
将 Ingress Controller 的公网 IP 保存到 shell 变量:
IC_IP=XXX.YYY.ZZZ.III将 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,它强制以下三条约束,违反任意一条资源都会被拒绝:
- 至少 2 个 split——
len(splits) < 2时直接报must include at least 2 splits; - 每个 weight 必须在 0..100 范围内——使用
validation.IsInRange(s.Weight, 0, 100)校验; - 所有 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 步:验证配置是否生效
检查 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事件,原因即上一节所列的校验失败信息。使用 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 的组合。
配置生成: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块。模板渲染:生成的
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 模板,渲染逻辑相同。权重热更新优化:从源码结构看,当仅修改 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
相关推荐
kubernetes-ingress 实战:使用 NGINX Plus 与 NGINX App Protect 为 VirtualServer 配置 WAF 防护
kubernetes ingress 实战:使用 NGINX Plus 与 NGINX App Protect 为 VirtualServer 配置 WAF 防
API网关云原生负载均衡可观测性微服务NGINX Kubernetes Ingress:用 VirtualServer 配置 Ingress MTLS 双向 TLS 认证的完整实战
NGINX Kubernetes Ingress:用 VirtualServer 配置 Ingress MTLS 双向 TLS 认证的完整实战 本文基于 NGI
API网关云原生负载均衡可观测性微服务ingress-nginx灰度发布:精准控制流量分发的艺术
ingress nginx灰度发布:精准控制流量分发的艺术 引言:为什么需要灰度发布? 在现代微服务架构中,应用更新迭代频繁,直接全量发布新版本存在巨大风险。一
后端API网关负载均衡云原生
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考