土法炼钢兴趣小组的算法知识备份

【Istio 控制面】VirtualService / mesh HTTPRoute → RDS:两条编译路径,一份路由表

文章导航

分类入口
networkkubernetes
标签入口
#istio#istiod#rds#virtualservice#httproute#gateway-api#gamma

目录

Envoy Router 拿到的终局只是一个 cluster 名,这个名字来自 RDS 下发的 RouteConfiguration。生产上,写出这份路由表的 CRD 有两种:Istio 原生的 VirtualService,或 Kubernetes Gateway API 的 HTTPRoute(在 mesh 模式下即 GAMMA)。本文不讲这两种 CRD 的字段语法——那是 Gateway API 深度拆解 的范围——只回答一个问题:两条输入路径最终都要喂给同一个 RdsGenerator,它们是怎么汇合的,汇合时会不会打架。

本文是「Istio / 网格控制面内核」系列第 6 篇。→ 系列目录

上接 第 5 篇:Service/Endpoint → CDS/EDS;下接 第 7 篇:DestinationRule

版本锚定:Istio 1.30.3,源码路径对应 pilot/pkg/xds/rds.gopilot/pkg/config/kube/gateway/(函数签名以 master 分支验证,1.30.x 语义一致)。Gateway API 资源模型与 GAMMA 语义见 k8s-network/18,本文不重复。


一、RDS 生成器:只做全量,按名字取路由

RdsGenerator.Generatepilot/pkg/xds/rds.go)逻辑很短:先判断 rdsNeedsPush,需要时调用 ConfigGenerator.BuildHTTPRoutes(proxy, req, w.ResourceNames)。两处细节值得钉住:

BuildHTTPRoutesw.ResourceNames(代理订阅的 route config 名,通常对应监听端口)逐个查内部路由模型,拼出 RouteConfiguration。这个「内部路由模型」是本文重点——它不是 VirtualService 本身,而是两条输入路径共同产出的中间表示。


二、路径一:VirtualService 原生编译

VirtualService 里的 hostshttp[].match/route/rewritegateways 字段被解析成 config.Config{GroupVersionKind: gvk.VirtualService, ...},存进 istiod 的配置存储(config store),供 BuildHTTPRoutes 按 host 查询。几个直接影响 RDS 的字段语义:

生产上这个「未定义顺序」不是理论问题:issue #58453(截至本文写作仍开放)报告的现象是——两个 VirtualService 分别声明 //api/v1/specific,如果泛匹配 / 先被处理,所有请求都会被它吃掉,即便更具体的路径路由本该优先。路由对不对,取决于资源创建顺序,而不只取决于配置内容本身——这正是「RDS 已生成」不等于「行为符合直觉」的一类典型坑。


三、路径二:Gateway API HTTPRoute 编译成同一种内部模型

pilot/pkg/config/kube/gateway/controller.go 里的 Controllerkrt(Istio 内部的声明式收集器框架)watch HTTPRouteGRPCRoute 等资源,HTTPRouteCollectionroute_collections.go)对每条 Route 调用 conversion.goconvertHTTPRoute,把一条 HTTPRouteRule 转成 istio.HTTPRoute(与原生 VirtualService 里的 http[] 元素同一个 Go 类型),最终包进:

cfg := config.Config{
    Meta: config.Meta{
        GroupVersionKind: gvk.VirtualService,
        Name:             name,
        Namespace:        obj.Namespace,
    },
    Spec: &istio.VirtualService{
        Hosts:    []string{h},
        Gateways: []string{parent.InternalName},
        Http:     routes,
    },
}

这是本篇最关键的一点:HTTPRoute 不是绕开 VirtualService 走另一条通道去 RDS,而是被 Gateway API 控制器编译成一个 GroupVersionKind: VirtualService 的内部对象,再和原生 VirtualService 一起存进同一个配置存储,被同一个 RdsGenerator / BuildHTTPRoutes 处理。RDS 生成器完全不知道某条内部 VirtualService 「原本」是手写的还是从 HTTPRoute 转换出来的。

GAMMA:mesh 模式下 parentRef 指向 Service

Gateway API 支持把 parentRefs 指向一个 kind: Service 而不是 Gateway——这是 GAMMA(Gateway API for Mesh Management and Administration)定义的 mesh 路由模式。conversion.goparentTypes 会区分 r.IsMesh()computeRoute 对 mesh 分支单独求值。效果上,这类 HTTPRoute 编译出的内部 VirtualService 等价于原生 VirtualService 省略 gateways 字段(默认绑到 mesh——同样只影响 sidecar,不挂到任何 ingress gateway。

flowchart TD
  vs["VirtualService CRD"] --> store["Config Store<br/>kind: VirtualService"]
  hr1["HTTPRoute<br/>parentRef: Gateway"] --> conv["gateway controller<br/>convertHTTPRoute"]
  hr2["HTTPRoute<br/>parentRef: Service (GAMMA)"] --> conv
  conv --> store
  store --> rds["RdsGenerator.BuildHTTPRoutes"]
  rds --> rc["RouteConfiguration"]

四、双轨权威:谁的合并规则说了算

两条路径殊途同归到同一个内部模型,但上游的匹配优先级算法完全不同

维度 VirtualService HTTPRoute(Gateway API 规范)
单资源内顺序 http[] 线性列表,第一条匹配的规则生效 同样是列表,但资源间还有确定性 tie-break
跨资源合并(mesh) 不支持,同 host 直接判冲突(IST0109 mesh 模式下多条 HTTPRoute 可按下方规则确定性合并
跨资源合并(ingress/gateway) 允许合并,但顺序未定义#7674 规范强制的确定性优先级
优先级判定依据 依赖创建顺序 / 编写顺序,官方未提供裁决算法 Exact 路径 > 最长 Prefix > Method > header 数量 > query 参数数量 > 创建时间 > namespace/name 字典序(Gateway API 规范原文)

HTTPRoute 的确定性算法不是 Istio 发明的,是 Gateway API 规范(HTTPRouteRule 一节)强制要求所有实现遵守的行为——这也是为什么 #58453 里 Istio 维护者直接建议「切到 HTTPRoute,顺序问题就解决了」,而不是给 VirtualService 补一个等价算法:VirtualService 早期把匹配顺序设计成裸列表,事后加一套跨资源仲裁算法的改造成本高于迁移到新 API(同 issue 中维护者对 #22997 的评论)。

这不只是「两个 CRD 长得像」的表面重复,而是两套关于「谁能确定性地决定路由优先级」的设计在同一个内部模型里共存。Istio 官方文档对此有明确立场:「Istio supports the Kubernetes Gateway API and intends to make it the default API for traffic management in the future」——这句话钉在 Kubernetes Gateway API 任务文档的开篇,不是营销辞令,而是关于未来权威归属的官方声明。


五、运维层面的冲突边界

由于两条路径写入的是同一个 kind 的配置对象,下面这些组合在生产上是真实风险点,不是理论假设:

  1. 同一 host 同时被原生 VirtualService 与 mesh 模式 HTTPRoute 覆盖:两者编译出的内部 VirtualService 会被当成两个独立资源处理,命中前一节「mesh 网关下同 host 冲突判定」——但冲突检测(istioctl analyze)目前只覆盖 VirtualService 视角,看不到「这条内部对象其实来自哪个 HTTPRoute」,排障时容易忽略源头。
  2. HTTPRoute 用了 VirtualService 不支持的字段(或反过来):Gateway API 规范明确「不覆盖 Istio 100% 的功能集」,混用时功能对齐需要逐字段核对,不能假设「反正都编译成 VirtualService,字段能力就一样」。
  3. 迁移期双写:把某个 host 从 VirtualService 迁移到 HTTPRoute 时,若忘记删旧资源,等于制造了上面第一类冲突——这是 GAMMA 落地里最常见的运维事故模式,本质原因是两条编译路径共享目标模型,但没有共享的「谁是权威来源」标记

六、谱系、争论与开放问题

谱系:Istio 的 VirtualService 诞生于 2017 年前后的 Istio 0.x/1.0,是在 Kubernetes Ingress 表达力不足的背景下设计的专有 CRD;Gateway API 由 SIG-Network 主导,吸收了包括 Istio 在内的多家 Ingress 控制器经验,试图做「厂商中立」的标准化后继(k8s-network/18 有完整脉络,这里不重复)。GAMMA 是 Gateway API 工作组内专门解决「网格内路由」场景的扩展方向,parentRef 指向 Service 是其核心机制。

争论:路由匹配的合并顺序该由「配置书写顺序」隐式决定,还是由「协议规定的确定性算法」显式决定——这不是抽象的 API 设计品味问题,#7674#58453 是两个跨越数年的真实工程分歧,前者导致 Istio 在 0.6 之后主动放弃了更激进的自动合并,后者是仍未关闭的生产诉求。Istio 官方选择的解法不是修补 VirtualService 的合并算法,而是把权威地位向 HTTPRoute 转移——这本身也是一种立场,而非纯技术必然。

开放问题:在 Istio 明确「计划让 Gateway API 成为默认」但尚未给出 VirtualService 弃用时间表的当下,两套 API 长期并存期间的冲突检测应该做到什么粒度——目前的 istioctl analyze 规则集是围绕 VirtualService 视角设计的,能否/是否需要感知「这条 VirtualService 由哪个 HTTPRoute 编译而来」并给出跨 API 的冲突诊断,社区没有给出明确路线图。


七、参考资料

源码(A)

规范 / 官方文档(A)

源码问题追踪(B,工程判断佐证)

站内

实验台账


八、小结

  1. RdsGenerator 只支持全量推送skippedRdsConfigs 明确了哪些 Kind 的变化根本不会碰 RDS。
  2. VirtualService 与 HTTPRoute(含 GAMMA mesh 模式)最终编译成同一种内部 VirtualService 表示,喂给同一个生成器——HTTPRoute 不是另一条独立通道。
  3. 两者的合并权威模型不同:VirtualService 跨资源合并顺序未定义(mesh 网关下直接冲突报错),HTTPRoute 有规范强制的确定性优先级算法。
  4. 双写同一 host 是真实的生产风险,因为两条路径写入的是同一个 kind、同一个配置存储,而现有分析工具不区分「谁是原始作者」。
  5. Cluster 名字对了、路由表也生成了,不代表 subset 一定命中——下一篇 DestinationRule 讲这一层怎么再收窄一次。

系列目录 · 上一篇:Service/Endpoint → CDS/EDS · 下一篇:DestinationRule

同主题继续阅读

把当前热点继续串成多页阅读,而不是停在单篇消费。

2026-08-11 · network / kubernetes

Istio / 网格控制面内核:从 CRD 到 xDS

补齐 Envoy 数据面 xDS 消费侧与 Mesh 选型叙事之间的控制面内核:istiod 推送图、CRD/HTTPRoute 翻译、Ambient 多客户端、NACK/warming 责任与排障对齐。


By .