- API网关
- 云原生
- 负载均衡
- 可观测性
- 微服务
【免费下载链接】kubernetes-ingress
NGINX and NGINX Plus Ingress Controllers for Kubernetes
VirtualServer是 NGINX Ingress Controller(本仓库 kubernetes-ingress)提供的一等公民自定义资源(CRD),用于在标准 Kubernetes Ingress 之上实现高级配置能力:基于内容的精细化路由、流量切分、请求/响应头修改、TLS 终止与 cert-manager 集成、主动健康检查、URI 重写以及 NGINX App Protect 集成等。读完本文,你将掌握 VirtualServer 的完整字段模型、最小可用配置、上游(upstream)调优、路由动作(action)组合、高级路由(matches/splits)、路由委托(VirtualServerRoute)以及它如何被 Ingress Controller 校验并翻译为 NGINX 配置。
VirtualServer 是什么:资源定位与设计意图
VirtualServer归属于 API 组k8s.nginx.org,版本为v1,Kind为VirtualServer,作用域为Namespaced。官方 API 文档定义位于 docs/crd/k8s.nginx.org_virtualservers.md,其 Go 类型定义位于 pkg/apis/configuration/v1/types.go。
与原生 Ingress 相比,VirtualServer 的优势在于:它直接暴露了 NGINX 的server、location、upstream等核心概念,支持在同一资源内完成"域名 → 路由 → 上游 → 动作"的完整声明,而不需要借助大量注解来扩展行为。CRD 还注册了短名vs,并内置状态打印列(State、Host、IP、ExternalHostname、Ports、Age),这些定义可以从 config/crd/bases/k8s.nginx.org_virtualservers.yaml 中看到:
spec: names: kind: VirtualServer listKind: VirtualServerList plural: virtualservers shortNames: - vs singular: virtualserver scope: Namespaced安装 CRD 的方式与安装控制器一致,例如kubectl apply -f deploy/crds.yaml。注意 VirtualServer 与 VirtualServerRoute 在共享 host 时通过host字段进行关联,且 host 必须在所有 Ingress 与 VirtualServer 资源中唯一。
从一个最小可用示例开始
仓库中的 examples/custom-resources/basic-configuration/cafe-virtual-server.yaml 给出了最经典的 cafe 示例——两个上游、两个路径、TLS 终止:
apiVersion: k8s.nginx.org/v1 kind: VirtualServer metadata: name: cafe spec: host: cafe.example.com tls: secret: tls-secret upstreams: - name: tea service: tea-svc port: 80 - name: coffee service: coffee-svc port: 80 routes: - path: /tea action: pass: tea - path: /coffee action: pass: coffee创建后通过kubectl get vs即可查看状态;只有状态为Valid的 VirtualServer 才会被控制器接受并生成 NGINX 配置(详见下文"校验与状态")。
spec 顶层字段总览
.spec支持的顶层字段如下(完整字段表以 docs/crd/k8s.nginx.org_virtualservers.md 为准):
| 字段 | 类型 | 说明 |
|---|---|---|
host | string | 服务器域名,必须是 RFC 1123 合法子域(如my-app、hello.example.com)。使用通配域名*.example.com时必须加双引号包裹。在所有 Ingress 与 VirtualServer 资源中必须唯一 |
ingressClassName | string | 指定由哪个 Ingress Controller 处理该资源,须与引用它的 VirtualServer 的 ingressClassName 一致 |
listener | object | 自定义 HTTP/HTTPS 监听器,listener.http与listener.https须引用 GlobalConfiguration 中定义的监听器名 |
tls | object | TLS 终止配置(详见下文) |
gunzip | boolean | 是否对 gzip 响应解压后转发给客户端,取值on/off、true/false、yes/no,默认 off |
policies | array | 策略引用列表,policies[].name为策略名、policies[].namespace缺省时使用 VirtualServer 所在命名空间。若引用的策略不存在或无效,NGINX 会返回 500 错误响应 |
upstreams | array | 上游(后端服务)列表(详见下文) |
routes | array | 路由列表(详见下文) |
http-snippets | string | 在 http 上下文插入自定义配置片段 |
server-snippets | string | 在 server 上下文插入自定义配置片段,覆盖 ConfigMap 的server-snippets键 |
add-header-inherit | string | 服务器级别的响应头继承行为,取值on/off/merge。merge表示与子上下文头合并;on采用标准 NGINX 继承;off表示不从父上下文继承任何头。Go 类型中通过+kubebuilder:validation:Enum=on;off;merge约束取值 |
dos | string | 对DosProtectedResource的引用,启用后对 VirtualServer 路由生效 DOS 防护 |
externalDNS | object | ExternalDNS 集成配置(详见下文) |
listener:引用 GlobalConfiguration 中的自定义监听器
examples/custom-resources/custom-listeners/cafe-virtual-server.yaml 展示了如何让 VirtualServer 监听在自定义端口上:
spec: listener: http: http-8083 https: https-8443 host: cafe.example.com tls: secret: tls-secret这里的http-8083、https-8443必须是在 GlobalConfiguration 资源中定义过的监听器名。需要同时定义监听器协议与地址,例如http-8083对应监听8083端口的 HTTP 监听器。
tls:终止、重定向与 cert-manager 集成
tls对象包含三个部分:
tls.secret:存放 TLS 证书与私钥的 Secret 名称。Secret 必须与 VirtualServer 同命名空间,推荐类型为kubernetes.io/tls或Opaque,并须以 TLS 角色解析,证书链存放在tls.crt、私钥存放在tls.key。若 Secret 不存在或无效,NGINX 会中断对该 host 的一切 TLS 连接尝试;若未指定 Secret 但配置了通配 TLS Secret,则使用通配 Secret 终止 TLS。
tls.redirect:HTTP→HTTPS 强制跳转配置:
| 字段 | 类型 | 说明 |
|---|---|---|
enable | boolean | 是否启用 TLS 重定向,默认 false |
code | integer | 重定向状态码,允许 301/302/307/308,默认 301 |
basedOn | string | 判断依据:scheme(请求协议)或x-forwarded-proto(X-Forwarded-Proto 头),默认scheme |
tls.cert-manager:让控制器代为签发证书:
| 字段 | 说明 |
|---|---|
issuer | 同命名空间的 Issuer 名;issuer与cluster-issuer二选一且必须且只能定义一个 |
cluster-issuer | ClusterIssuer 名(非命名空间资源,任意命名空间可用) |
issuer-kind/issuer-group | 外部(out-of-tree)Issuer 的 Kind 与 API 组,例如AWSPCAIssuer/awspca.cert-manager.io;不能与cluster-issuer同时定义 |
common-name | 生成证书的spec.commonName,为 x509 证书添加 CN |
duration/renew-before | 证书有效期与续期提前量,使用 Gotime.Duration格式(只能用s/m/h后缀,不能用d天后缀) |
usages | 证书用途,逗号分隔字符串,如key agreement,digital signature, server auth |
issue-temp-cert | 为 true 时,在正式证书签发前先向 cert-manager 申请临时自签名证书,使 HTTPS-only 服务器能在 Secret 尚不存在时完成 ACME HTTP01 挑战 |
仓库示例 examples/custom-resources/certmanager/cafe-virtual-server.yaml 展示了基于cluster-issuer的完整用法。
externalDNS:为 VirtualServer 自动创建 DNS 记录
| 字段 | 类型 | 说明 |
|---|---|---|
enable | boolean | 启用 ExternalDNS 集成,默认 false |
recordType | string | 记录类型,如A、AAAA、CNAME;不定义时根据外部端点自动计算 |
recordTTL | integer | DNS 记录 TTL,默认 0 |
labels | object | 应用到 Endpoint 资源的标签,供 ExternalDNS 消费 |
providerSpecific | array | 各 DNS 提供商的专属属性,每项含name与value |
对应示例见 examples/custom-resources/external-dns/cafe-virtual-server.yaml。该配置由控制器在同步阶段转换为 externaldns 的 Endpoint 资源(相关实现位于 internal/externaldns)。
upstreams:后端服务的完整调优面板
upstreams[]是 VirtualServer 中"服务发现 + 负载均衡 + 连接调优"的核心。其 Go 定义位于 pkg/apis/configuration/v1/types.go。基础字段:
| 字段 | 类型 | 说明 |
|---|---|---|
name | string | 上游名,RFC 1035 合法 DNS 标签(如hello、upstream-123),在资源内唯一 |
service | string | 服务名。跨命名空间时须带命名空间前缀(如tea-namespace/tea)。服务不存在时 NGINX 视为零端点,请求返回 502;NGINX Plus 还支持同命名空间的 ExternalName 服务 |
port | integer | 服务端口,范围 1..65535;若服务未定义该端口,NGINX 视为零端点并返回 502 |
subselector | object | 按标签键值进一步挑选服务内的 Pod(默认选全部)。注意:标签应在 Pod 创建时存在,Pod 标签更新后控制器要等到 Pod 数量变化才能感知 |
type | string | 上游类型,http(默认)或grpc。gRPC 需在 ConfigMap 启用 HTTP/2 并在 VirtualServer 配置 TLS 终止 |
lb-method | string | 负载均衡算法,轮询写法为round_robin;默认取 ConfigMaplb-method键 |
use-cluster-ip | boolean | 使用服务的 Cluster IP:Port 代替 Pod IP:Port。启用后,与多上游相关的字段(lb-method、next-upstream等)不再生效 |
连接、超时与缓冲
| 字段 | 说明 |
|---|---|
connect-timeout/read-timeout/send-timeout | 建立连接、读取响应、发送请求的超时;默认分别取 ConfigMap 的proxy-connect-timeout、proxy-read-timeout、proxy-send-timeout |
fail-timeout/max-fails | 判定服务器不可用所需的失败次数与时间窗;默认取 ConfigMap 对应键 |
next-upstream | 哪些情况下把请求转给下一个上游,默认error timeout |
next-upstream-timeout/next-upstream-tries | 转发的总时间上限与尝试次数上限,0 表示不限制,默认 0 |
buffering | 是否缓冲上游响应,默认取 ConfigMapproxy-buffering |
buffers.number/buffers.size | 单连接读取响应的缓冲数量与大小,默认取 ConfigMapproxy-buffers |
buffer-size | 读取响应首部分的缓冲大小,默认取 ConfigMapproxy-buffer-size |
busy-buffers-size | proxy_buffering开启时用于读取响应的忙缓冲大小,默认取 ConfigMapproxy-busy-buffers-size |
client-max-body-size | 客户端请求体最大允许大小,默认取 ConfigMap 对应键 |
client-body-buffer-size | 读取客户端请求体的缓冲大小,必须为数字加k(KB)或m(MB),如10m、512k(Go 类型中用Pattern=\^\d+[kKmM]?$`` 校验) |
keepalive | 到上游的连接缓存大小,0 表示禁用;默认取 ConfigMapkeepalive |
max-conns | 到单个上游服务器的最大并发活动连接数,默认不限 |
proxy-http-version | 与上游通信的 HTTP 版本,允许1.0/1.1/2。未设置时使用后端 Service 端口的appProtocol(kubernetes.io/h2c视为2),否则默认 HTTP/1.1;grpc类型固定使用 HTTP/2 且忽略该字段,WebSocket 必须用 HTTP/1.1,1.0会向发送上游Connection: close头 |
ntlm | 是否允许 NTLM 认证代理(仅 NGINX Plus),需要配合keepalive使用 |
tls 与健康检查
upstreams[].tls:tls.enable为 true 时对上游启用 HTTPS(默认 false,使用 HTTP)。注意:默认 NGINX 不校验上游证书,如需校验请配置 EgressMTLS Policy。
upstreams[].healthCheck(仅 NGINX Plus 支持):
| 字段 | 说明 |
|---|---|
enable | 启用主动健康检查,默认 false |
path | 健康检查请求路径,默认/(gRPC 上游不可配置) |
interval/jitter | 检查间隔(默认 5s)与随机延迟时间(默认无) |
fails/passes | 判定不健康/恢复健康的连续失败/成功次数,默认均为 1 |
port | 健康检查使用的 Pod 端口(注意:不是 Service 端口),默认使用服务器端口 |
connect-timeout/read-timeout/send-timeout | 健康检查的连接/读/写超时,默认沿用上游对应超时 |
headers[] | 健康检查请求的额外头(Host、User-Agent、Connection 由 NGINX Plus 自动设置) |
statusMatch | 期望的状态码,如200、! 500、301-303 307,默认 2xx/3xx;gRPC 上游不支持 |
grpcService/grpcStatus | 仅 gRPC 上游:被监控的 gRPC 服务与期望的 gRPC 状态码(如服务未实现健康检查协议时配置 12) |
mandatory | 新加入的服务器必须先通过全部健康检查才接收流量;配合slow-start可让新服务器"预热" |
persistent | 重载后若服务器此前健康则保持 up 状态;开启 persistent 要求 mandatory 也为 true |
keepalive-time | 健康检查 keepalive 连接复用时间,默认 60s |
tls.enable | 健康检查是否使用 HTTPS(默认 false,且默认不校验上游证书) |
完整的可运行示例见 examples/custom-resources/health-checks/README.md,其中展示了enable: true、path: /healthz、interval: 20s、fails/passes: 5、port: 8080、自定义Host头与statusMatch: "! 500"的组合。
会话保持、队列、慢启动与备份
sessionCookie:基于 NGINX 生成的会话 Cookie 的会话保持,使同一客户端始终被路由到同一上游:
| 字段 | 说明 |
|---|---|
enable | 启用,默认 false |
name/path/domain | Cookie 名、生效路径、生效域名 |
expires | 浏览器保留 Cookie 的时间;特殊值max表示 Cookie 于 2037-12-31 23:55:55 GMT 过期 |
httpOnly/secure | 添加 HttpOnly / Secure 属性 |
samesite | SameSite 属性,允许strict/lax/none |
queue(仅 NGINX Plus):当无法立即选中上游服务器时,将请求放入队列,queue.size为队列大小,queue.timeout默认 60s,超时后请求不再排队。
slow-start:服务器恢复/可用后,权重从 0 逐渐恢复到标称值;默认禁用。注意:不能与random、hash、ip_hash负载均衡方法同时使用(源码 internal/configs/virtualserver.go 中的incompatibleLBMethodsForSlowStart表明确列出这些冲突方法)。
backup/backupPort:备份服务(ExternalName 类型)名称及其端口(提供备份名时端口必填,范围 1..65535)。主服务器全部不可用时启用备份;同样不能与random、hash、ip_hash混用。
routes 与 action:路径匹配与请求处置
routes[]决定 NGINXlocation的生成。核心字段:
| 字段 | 类型 | 说明 |
|---|---|---|
path | string | 路径匹配规则,支持前缀(/、/path)、最长前缀(^~/images/)、精确匹配(=/exact/match)、大小写不敏感正则(~*^/Bar.*\.jpg)与大小写敏感正则(~^/foo.*\.jpg)。前缀/最长前缀/精确匹配不得含空白、{、}、;;正则须转义双引号且不能以未转义的反斜杠结尾。路径在该 VirtualServer 内必须唯一 |
action | object | 该路由的默认动作 |
splits | array | 默认流量切分,至少 2 个 split,权重 0..100 且总和必须为 100 |
matches | array | 高级内容路由匹配规则(见下文),未匹配的请求走默认 action/splits |
errorPages | array | 对指定错误码的定制响应 |
route | string | 委托给 VirtualServerRoute 资源,跨命名空间时写命名空间/名称(如tea-namespace/tea) |
routeSelector | object | 用标签选择器(matchLabels/matchExpressions,表达式支持In、NotIn、Exists、DoesNotExist,多个条件 AND 连接)选择 VirtualServerRoute |
policies | array | 路由级策略,覆盖 spec 中同类型策略 |
location-snippets | string | location 上下文自定义片段,覆盖 ConfigMaplocation-snippets键 |
add-header-inherit | string | location 级别的头继承行为,取值同 spec 级 |
dos | string | 引用 DosProtectedResource,对该路由启用 DOS 防护 |
action 的四种形态
action只能且必须定义以下四种之一,Go 定义见 pkg/apis/configuration/v1/types.go:
pass(直通转发):action.pass: tea将请求直接传给同资源内定义的名为tea的上游。这是最简单、性能最好的转发方式。
proxy(代理并改造):
| 字段 | 说明 |
|---|---|
upstream | 目标上游名,必须已在资源中定义 |
rewritePath | 重写后的 URI;当路由路径是正则(以~开头)时可用$1-$9捕获组 |
requestHeaders.pass | 是否将原始请求头传给上游,默认 true |
requestHeaders.set[] | 重定义或追加请求头,每项含name、value |
responseHeaders.hide[] | 不传给客户端的响应头 |
responseHeaders.pass[] | 允许透传被隐藏的响应头 |
responseHeaders.ignore[] | 禁用对某些响应头的处理 |
responseHeaders.add[] | 向客户端响应添加头,每项含name、value,always: true时无视响应状态码强制添加(默认 false) |
redirect(重定向):redirect.url为目标 URL,支持 NGINX 变量$scheme、$http_x_forwarded_proto、$request_uri、$host,变量须用花括号包裹(如${host}${request_uri});redirect.code允许 301/302/307/308,默认 301。
return(返回预置响应):return.code允许 2XX/4XX/5XX,默认 200;return.type为 MIME 类型,默认text/plain;return.body支持花括号包裹的 NGINX 变量(如Request is ${request_uri}\n);return.headers[]为自定义响应头。
URI 重写实战
examples/custom-resources/rewrites/README.md 给出了前缀与正则两种重写示例。前缀路径重写:
routes: - path: /tea/ action: proxy: upstream: tea rewritePath: / - path: /coffee action: proxy: upstream: coffee rewritePath: /beans效果:/tea/abc→/abc;/coffee/abc→/beans/abc。正则重写(捕获组传递剩余 URI):
- path: ~ /tea/?(.*) action: proxy: upstream: tea rewritePath: /$1效果:/tea、/tea/→/;/tea/abc→/abc。注意/tea前缀请求会被重定向到/tea/。对应生成逻辑在 internal/configs/virtualserver.go 的generateRewrites与generateProxyPassRewrite中实现。
高级路由:matches、conditions 与 splits
routes[].matches[]实现基于内容的精细化路由(advanced content-based routing)。每个 match 至少包含 1 个conditions,并定义一个action或一组splits。匹配规则按顺序求值,未匹配的请求由路由的默认 action 或 splits 处理。
条件类型(conditions[]),Go 定义见 pkg/apis/configuration/v1/types.go:
| 字段 | 说明 |
|---|---|
header | 请求头名,须由字母数字或-组成 |
cookie | Cookie 名,须由字母数字或_组成 |
argument | 请求参数名,须由字母数字或_组成 |
variable | NGINX 变量名,必须以$开头 |
value | 匹配的目标值 |
examples/custom-resources/advanced-routing/cafe-virtual-server.yaml 同时示范了变量条件与 Cookie 条件:
- path: /tea matches: - conditions: - variable: $request_method value: POST action: pass: tea-post action: pass: tea - path: /coffee matches: - conditions: - cookie: version value: v2 action: pass: coffee-v2 action: pass: coffee-v1这里/tea的 POST 请求走tea-post,其余走tea;/coffee携带version=v2Cookie 的请求走coffee-v2,其余走coffee-v1。匹配逻辑由 internal/configs/virtualserver.go 的generateMatchesConfig翻译为 NGINX 的map+ 内部 location 结构。
流量切分(splits):examples/custom-resources/traffic-splitting/cafe-virtual-server.yaml 演示了金丝雀发布最常用的 90/10 切分:
routes: - path: /coffee splits: - weight: 90 action: pass: coffee-v1 - weight: 10 action: pass: coffee-v2weight取值 0..100,所有 split 权重之和必须等于 100。控制器基于split_clients/keyval 机制实现动态权重(参见 internal/configs/virtualserver.go 的generateSplitsForWeightChangesDynamicReload及 internal/k8s 中关于 DynamicWeightChangesReload 的实现)。
路由委托:VirtualServerRoute 与 routeSelector
当单个 VirtualServer 过大或需要跨团队协作时,可以将路由定义拆分到 VirtualServerRoute 资源中。方式一:通过routes[].route显式引用:
routes: - path: /tea route: default/tea方式二:通过routeSelector按标签批量选择:
routes: - path: / routeSelector: matchLabels: app: coffee完整示例见 examples/custom-resources/basic-configuration-vsr/cafe-virtual-server.yaml。控制器在同步时会把选中的 VirtualServerRoute 合并进 VirtualServer 的生成配置(VirtualServerEx结构中的VirtualServerRoutes与VirtualServerSelectorRoutes字段即用于承载这些路由,见 internal/configs/virtualserver.go)。
校验与状态:从声明到 Valid 的旅程
VirtualServer 提交到集群后,控制器会执行两轮校验:
- CRD 模式校验:基于 config/crd/bases/k8s.nginx.org_virtualservers.yaml 中
openAPIV3Schema的约束(枚举值、正则、必填项等),由 API Server 在写入时强制执行。 - 控制器业务校验:位于 pkg/apis/configuration/validation/virtualserver.go,配合 pkg/apis/configuration/validation/common.go 完成 host 唯一性、路径格式、权重总和、引用存在性等检查,并返回三种状态(定义见 pkg/apis/configuration/v1/types.go):
Valid:校验通过并正常工作;Warning:已接受但可能以降级状态运行;Invalid:校验失败或 NGINX 重载对应配置失败。
状态会写入status.state / status.reason / status.message / status.externalEndpoints(见 pkg/apis/configuration/v1/types.go),并通过kubectl get vs打印列直接可见。
从源码看 VirtualServer 如何变成 NGINX 配置
VirtualServer 的"翻译"工作集中在 internal/configs/virtualserver.go(约 3000 行)。其生成函数与 NGINX 指令的对应关系可以帮你建立从 YAML 到nginx.conf的映射直觉:
| 生成函数 | 对应 NGINX 配置 |
|---|---|
generateUpstreams(L1406) | upstream块:server、keepalive、max_conns、slow_start等 |
generateHealthCheck(L1789) | NGINX Plus 的health_check指令 |
generateLocation/generateLocationForProxying/generateLocationForRedirect/generateLocationForReturn(L2085 起) | 各类location块 |
generateProxySetHeaders、generateProxyHideHeaders、generateProxyAddHeaders(L2108 起) | proxy_set_header、proxy_hide_header、add_header等 |
generateSplits/generateMatchesConfig(L2323、L2517) | split_clients/map+ 内部 location 路由 |
generateErrorPages(L2981) | error_page指令 |
generateTLSRedirectConfig(L2817) | 基于$scheme/$http_x_forwarded_proto的 301/302 重定向 |
这套生成逻辑同样服务于 VirtualServerRoute 合并后的路由与 gRPC 上游(generateGRPCPass)。相关单元测试可在 internal/configs/virtualserver_test.go 中找到,端到端的行为验证则覆盖在 tests/suite/test_virtual_server.py 等测试套件中。
延伸阅读
- VirtualServer 官方字段参考
- VirtualServerRoute 资源参考
- GlobalConfiguration(自定义监听器)参考
- examples/custom-resources 目录下的全部可运行示例,尤其是 basic-configuration、advanced-routing、traffic-splitting 与 rewrites
- Policy 资源参考 与 App Protect DOS 资源参考,用于理解
policies与dos字段的对接对象
- API网关
- 云原生
- 负载均衡
- 可观测性
- 微服务
【免费下载链接】kubernetes-ingress
NGINX and NGINX Plus Ingress Controllers for Kubernetes
相关推荐
NGINX Ingress Controller VirtualServerRoute 完全指南:模块化子路由、内容路由与流量治理
NGINX Ingress Controller VirtualServerRoute 完全指南:模块化子路由、内容路由与流量治理 VirtualServerR
API网关云原生负载均衡可观测性微服务终极指南:NGINX Ingress Controller自定义配置全解析——从Annotations到ConfigMaps
终极指南:NGINX Ingress Controller自定义配置全解析——从Annotations到ConfigMaps NGINX Ingress Con
API网关云原生负载均衡可观测性微服务NGINX Ingress Controller 的 GlobalConfiguration 自定义资源完全指南:自定义监听器(Listener)的配置、校验与实战
NGINX Ingress Controller 的 GlobalConfiguration 自定义资源完全指南:自定义监听器(Listener)的配置、校验与
API网关云原生负载均衡可观测性微服务
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考