配置翻译(重头戏)
Higress 怎么把一个 K8s Ingress(+ 一堆注解)翻译成 Istio 的 VirtualService/Gateway?今天读翻译核心 ConvertHTTPRoute,以及一个精妙的"通知/生成解耦"事件模型。昨天(Day 03)看的是"翻译官的心脏 IngressConfig 怎么组装",今天正式看它怎么翻译一条具体的 Ingress——这是全课最"重头"的一天,翻完的成品(VirtualService)明天(Day 05)会被打包喂给 istiod。
聚合入口 List
// pkg/ingress/config/ingress_config.go List(:288)
// 对每种类型:listFromIngressControllers + listFromGatewayControllers
// 中间对每个 config 跑 templateProcessor.ProcessConfig(模板/Secret 引用替换)
// listFromIngressControllers(:324)分发中心:
// ingressController.List() 收集 Ingress → SortIngressByCreationTime 排序
// → createWrapperConfigs 包装 → 按类型 switch 到 convertGateways/convertVirtualService/…
List(gvk.VirtualService) 时,触发这条翻译链:收集所有 Ingress → 排序(保证确定性)→ 包装 → 按目标类型翻译。配置是"现场翻译"的(Day 03 只读 ConfigStore)——不预存,istiod 要时才算。解析注解
createWrapperConfigs(:387)把每个 Ingress 的 annotations 用 annotationHandler.Parse 解析成结构化的 annotations.Ingress,打包成 WrapperConfig。注解解析器在 pkg/ingress/kube/annotations/(canary/auth/cors/retry/rewrite/header_control/ip_access_control/local_rate_limit…)。
higress.io/canary-weight: "20" 做金丝雀、higress.io/enable-cors: "true" 开 CORS。每个注解对应 annotations/ 里一个解析器,把注解翻译成内部结构。这就是 Higress"比 Istio 易用"的关键——用注解隐藏了 Istio CRD 的复杂性。解析出的结构后续应用到 VirtualService/DestinationRule。convertGateways
// ingress_config.go convertGateways(:433)
// 遍历 wrapperConfigs → ingressController.ConvertGateway
// 为每个 host 产出 config.Config{GroupVersionKind: gvk.Gateway, Spec: gateway.Gateway}
// ConvertGateway(ingressv1/controller.go:339):为每条 rule 的 host 构造 Gateway
// 加 Selector(gatewaySelectorKey/Value)、HTTP/HTTPS Server、端口、SSLPassthrough
Gateway(Istio 课 Day 03 的 Gateway CRD)——定义"在哪个端口、什么协议、什么 host、什么 TLS 暴露"。Selector 把 Gateway 绑定到 Higress 网关 Pod。ConvertHTTPRoute(路由核心)
// pkg/ingress/kube/ingressv1/controller.go ConvertHTTPRoute(:531)
// 逐 rule.host 建 WrapperVirtualService(:590)
// 逐 path 建 WrapperHTTPRoute{HTTPRoute: &networking.HTTPRoute{}}(:605)
// PathType 映射(:612):正则/Exact/Prefix → 内部 PathType
// generateHttpMatches 生成 HTTPMatchRequest(:637)
// backendToRouteDestination(:664):K8s backend service → Istio HTTPRouteDestination
// 冲突路由检测(definedRules/Route2Ingress,:645-691)
HTTPMatchRequest(匹配条件);③backend service 翻译成 HTTPRouteDestination(转发目标)。还检测路由冲突(两条 Ingress 定义了同样的路由)。翻译完的 VirtualService 就是 Istio 课 Day 08 里 istiod 拿去生成 Envoy Route 的输入——两站在这里对接:Higress 生成 VirtualService,istiod 把它变成 Envoy 配置。host: api.foo.com,path: /v1(Prefix),backend: order-svc:8080翻译产物(VirtualService 里的一条 HTTPRoute,示意):
hosts: ["api.foo.com"] → 定位到哪个 VirtualServicematch: [{ uri: { prefix: "/v1" } }] ← path+PathType 变成匹配条件route: [{ destination: { host: order-svc, port: 8080 } }] ← backend 变成转发目标金丝雀 / 权重
convertVirtualService(ingress_config.go:486)在翻译后还做:annotationHandler.ApplyRoute 应用注解、applyCanaryIngresses 处理金丝雀、normalizeWeightedCluster 归一化权重到 100、ApplyDefaultBackend 默认后端。
canary-weight: "20" 注解的 Ingress 指向 v2。applyCanaryIngresses 把它们合并成一条 VirtualService,路由带权重(v1: 80%、v2: 20%)。normalizeWeightedCluster 保证权重加起来是 100。于是 20% 流量走 v2——金丝雀发布。用户只需加个注解,Higress 帮你翻译成 Istio 的加权路由(Istio 课 Day 08 的 route 权重)。这就是注解体系(L02)的威力——复杂能力,简单配置。Http2Rpc / EnvoyFilter
Higress 的高级能力通过生成 EnvoyFilter 实现:constructHttp2RpcEnvoyFilter(:1576,HTTP→Dubbo/gRPC 协议转换)、constructMcpSseStatefulSessionEnvoyFilter(:2102,MCP SSE 会话保持)、configmapMgr.ConstructEnvoyFilters(全局 tracing/gzip)。
Http2Rpc CRD 让你声明"HTTP 请求怎么映射到 Dubbo 方法参数",翻译成 EnvoyFilter。这呼应 Istio 课 Day 17——EnvoyFilter 是 Higress 加高级功能的主要手段之一。通知/生成解耦
// ingressv1/controller.go onEvent(:154):
// Ingress 变更时,并不直接生成配置!
// 而是对 dr/vs/ef/gateway 四类构造带 constants.AlwaysPushLabel:"true" 的空 config.Meta
// 调用对应 handler(:219)触发 istiod 重新拉取
List() → 此时才现场翻译(L01-06)→ 返回给 istiod。为什么这样?因为"生成"是按需的、带 proxy 上下文的(Istio 课 Day 01 的"带 Proxy 翻译")——提前生成没意义。AlwaysPushLabel 强制 istiod 跳过差异比较直接推。这是"事件驱动 + 惰性生成"的解耦:变更只管通知,生成等到被拉取时才做。👶 小白:为什么不干脆改一次 Ingress 就翻译一次、把结果存起来,等 istiod 来直接给?
👨🏫 老师:因为翻译结果和"谁来问、当时的上下文"有关,而且 istiod 什么时候来拉、拉哪几类,是它自己决定的。提前算好存起来,既可能白算(没人来拉),又要额外维护"缓存和输入是否一致"的麻烦。更省心的做法:变更时只喊一嗓子"配置变了,来拉吧",真正翻译推迟到 istiod 发请求、调 List() 那一刻。就像餐厅不会把所有菜提前炒好摆着,而是等你点了才下锅——新鲜、不浪费。
今日小结 + 动手
🧠 今天你应该能回答
- List 触发的翻译链?为什么是"现场翻译"?
- 注解体系是什么?举例它怎么隐藏 Istio 复杂性?
- ConvertHTTPRoute 把 Ingress 的哪三部分翻译成 VirtualService 的什么?
- 金丝雀发布怎么用注解实现?
- Http2Rpc 等高级能力用什么实现?通知/生成解耦是什么?
✋ 动手
cd /Users/bitmart/work/codes/github/higress-group/higress
grep -n 'func.*ConvertHTTPRoute\|func.*ConvertGateway\|func.*onEvent' pkg/ingress/kube/ingressv1/controller.go
grep -n 'convertVirtualService\|applyCanaryIngresses\|AlwaysPushLabel' pkg/ingress/config/ingress_config.go | head
ls pkg/ingress/kube/annotations/
config.Config 打包成 MCP Resource,通过 15051 端口的 gRPC 流下发。这是 Higress→istiod 的接口。