第 03 期 / 共 10 期

Istio Pilot 复用与 xDS

看清 Higress 与 Istio 的接缝。20 讲覆盖 ConfigStore / ServiceRegistry / PushContext / DiscoveryServer 与 LDS/RDS/CDS/EDS/SDS 的关键代码路径。

L01

Pilot 是什么

Pilot 是 Istio 控制面里负责"生成并下发 Envoy 配置"的组件。其核心抽象:

  • ConfigStore:拿到 VirtualService、DestinationRule、Gateway 等 CRD。
  • ServiceRegistry:拿到 Service 与 Endpoint。
  • PushContext:把以上两者快照成"可推送的视图"。
  • DiscoveryServer:ADS gRPC 服务端,对每个 Envoy 连接维护 stream。
思考:Higress 把 Pilot 当库用,自己只生成 Config,意味着不需要重写哪部分?答:xDS / Push / Stream 全免。
L02

vendored Istio 目录

仓库根目录 istio/istioistio/apiistio/client-goistio/proxy 分别对应 Istio 的几个仓库。Higress 通过 go.mod replace 把 import 重定向到这里。这意味着:

  • 升级 Istio 版本 = git 替换 istio/ 内容。
  • 所有 patch 都"hot",无需 fork。
  • Higress 可以保留 Istio 老 API 兼容性。
思考:如何在 PR 中评审一个跨 Higress + Istio 的修改?
L03

ConfigStore 模型

type ConfigStore interface {
    Schemas() collection.Schemas
    Get(typ config.GroupVersionKind, name, ns string) *config.Config
    List(typ config.GroupVersionKind, ns string) []config.Config
    Create/Update/Delete(config.Config) (string, error)
}

这是 Pilot 的"配置数据库"接口。Higress 不仅有真实 K8s CRD store,还有动态合成的 IngressConfig——后者也实现这个接口,让 Pilot 误以为有真实 VirtualService。

思考:合成对象用什么字段做 namespace/name?提示:通常以 Ingress 名 + cluster 前缀拼出。
L04

ServiceRegistry 模型

type ServiceDiscovery interface {
    Services() []*model.Service
    InstancesByPort(svc *model.Service, port int) []*model.ServiceInstance
    GetProxyServiceTargets(node *model.Proxy) []model.ServiceTarget
    NetworkGateways() []model.NetworkGateway
    MCSServices() []model.MCSServiceInfo
}

Pilot 用这个接口拿 endpoint。Higress 通过 registry/memory.ServiceRegistry 把 Nacos/ZK/Consul 来的实例统一注入。

思考:实例频繁变更(如金丝雀)通过哪条接口推回 Pilot?
L05

PushContext

PushContext 是"一次推送的不可变快照":从 ConfigStore + ServiceRegistry 拉取当前所有数据,构建好按命名空间/服务的索引,方便 generator 高效查询。每次配置变更后会重建 PushContext,老的快照仍可被进行中的推送使用。

思考:为什么用快照而不是直接读 live data?答:一致性 + 并发安全。
L06

Environment 容器

type Environment struct {
    ConfigStore
    ServiceDiscovery
    Mesh        *meshconfig.MeshConfig
    PushContext *PushContext
    ...
}

这是 Higress 跟 Pilot 沟通的"核心对象",bootstrap.NewServer 一旦填好它,剩下全是 Istio 的事。

思考:如果你想在 Environment 上挂一个自定义字段(如 ai 配额),怎么做?提示:通常通过 PushContext.Plugins 扩展。
L07

DiscoveryServer 入口

xdsServer := xds.NewDiscoveryServer(env, plugins, ...)
xdsServer.Register(grpcServer)

DiscoveryServer 内含:

  • pushChannel:推送任务队列
  • clients:当前连接的 Envoy 列表(按 ADS)
  • generators:按 TypeURL 分发的资源生成器
思考:单控制面能挂多少个 Envoy 连接?瓶颈在哪?
L08

ADS gRPC stream

每个 Envoy 启动后通过 StreamAggregatedResources 建立一条双向流,发 DiscoveryRequest,接 DiscoveryResponse。Pilot 在 ads.go 维护 stream state:已 ack 的 nonce、订阅的资源列表。

思考:Envoy 怎么告诉控制面"配置生效失败"?答:DiscoveryRequest.ErrorDetail。
L09

推送队列与 debounce

多个事件短时间内到达时,Pilot 用 debounce 合并:DebounceAfter 默认 100ms,最长 DebounceMax 10s。Higress 通过 XdsOptions.DebounceAfter/Max 透传修改。

合并后形成一次 PushContext 重建 + 多 client 并行推送。

思考:把 debounce 调得太短/太长各有什么风险?
L10

LDS 生成

LDS 把 Gateway / Sidecar 的 listener 渲染成 Envoy Listener。Higress 主要使用 Istio 的 configgen.BuildListeners。Wasm 插件通过 EnvoyFilter 嵌入到 listener 的 HTTP Filter Chain。

思考:为什么 WasmPlugin 不会触发 LDS 全量重推?答:phase + 增量替换 typed_config。
L11

RDS 生成

RDS 由 VirtualService 决定。Higress 大量工作就是合成 VirtualService,因此 RDS 是受 Ingress 改动影响最大的资源类型。Pilot 会按 listener route_config_name 分包 RDS 单独推送。

思考:100 万条 Ingress 时 RDS 大小会爆炸吗?答:会,按 host 分片 + RDS 增量是优化手段。
L12

CDS 生成

CDS = Cluster,每个上游服务一份。来源:K8s Service、ServiceEntry、DestinationRule 的 subset。Higress 通过翻译 Ingress + McpBridge 间接控制 CDS。

思考:DestinationRule 的 LoadBalancer 设置最终落到 CDS 的哪个字段?
L13

EDS 生成与增量

EDS 推送 ClusterLoadAssignment(每个 cluster 的 endpoint 列表)。这是最高频的资源类型——Pod IP 一动就 push。Istio 支持增量 xDS(delta),Higress 通过 XdsOptions.EnableEDSDebounce 控制是否对 EDS 也加 debounce。

思考:Nacos 实例上下线频率高时怎么避免 push 风暴?
L14

SDS 与证书

SDS 推送 TLS 证书与私钥。Higress 用 pkg/cert 自动签发证书后存入 Secret,Pilot 通过 Secret → SDS 推到 Envoy。Envoy 不落盘私钥,所有都在内存。

思考:Secret 修改触发 SDS 推送的链路?答:Secret Informer → ConfigUpdate → SDS。
L15

Aggregate 聚合器

configaggregateaggregate 分别聚合 ConfigStore 与 ServiceRegistry。聚合器对外暴露统一接口,对内按 cluster / source 分发查询。Higress 多集群 / 多注册中心都靠它。

思考:当两个源声明了同名 Service,聚合器怎么处理?
L16

krt 新式 Informer

istio.io/istio/pkg/kube/krt 是 Istio 1.20+ 推出的声明式 collection:用 krt.NewCollection 把多个底层 informer 组合成可派生的视图,类似 RX。Higress 部分 controller 已迁移到 krt,其它仍是老式 Informer + workqueue。

思考:krt vs 老式 Informer 的优缺点?
L17

Higress 注入点

Higress 与 Istio 的接缝可总结成 3 处:

  1. configaggregate 加入 IngressConfig 作为 ConfigStore。
  2. aggregate.Controller 加入 memory.ServiceRegistry 作为 ServiceDiscovery。
  3. EnvoyFilter 由 IngressConfig 合成,用来塞 Wasm / Lua / 自定义 filter。
思考:能否完全不用 EnvoyFilter,把所有扩展放到 IstioOperator API?提示:成本高、可扩展性差。
L18

推送一次的全流程

Ingress YAML changed
  → Informer event
  → IngressConfig 缓存重算 + EventHandler 通知
  → DiscoveryServer.ConfigUpdate(PushRequest)
  → debounce 合并
  → PushContext.InitContext (重建快照)
  → 对每个连接的 Envoy
       └─ proxy.SidecarScope 更新
       └─ generators 按 TypeURL 生成资源
       └─ 发送 DiscoveryResponse
  → Envoy ACK / NACK
思考:哪一步最重?为什么 PushContext 重建是性能瓶颈?
L19

调试 syncz / configz

常用端点:

  • :15014/debug/syncz:每个 Envoy 当前 ACK 状态。
  • :15014/debug/configz:当前 ConfigStore 内容。
  • :15014/debug/registryz:服务发现内容。
  • :15014/debug/endpointShardz:EDS 切片视图。
思考:用户报"路由没生效",先访问哪个端点?答:syncz 看是否 ACK 最新版本。
L20

与上游 Istio 差异

Higress 主要 patch 点:

  • 禁用 sidecar injector / Citadel CA。
  • 增强 Gateway label 选择器(GatewaySelectorKey/Value)。
  • 支持 KeepConfigLabels / KeepConfigAnnotations 这种透传开关。
  • 嵌入了对 Higress 自定义 CRD 的 schema。

这些都可以在 istio/istio 子目录里 diff 上游来确认。

本期收尾:你应该理解了 Higress = Istio Pilot + 自有 Config 合成器 + 自有 ServiceRegistry。下一期我们深入 K8s controller 范式。