ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

Higress 网关架构解析:从 Envoy 数据面到 Wasm 插件与生产实践

Higress 网关架构解析:从 Envoy 数据面到 Wasm 插件与生产实践

Higress 这个项目,最早是我在折腾一套多集群 Ingress 统一入口时被同事安利的。当时我们的痛点很具体:K8s 集群里跑着几十个微服务,南北向流量用 Nginx Ingress 扛着,东西向用 Istio 管着,结果两套体系各玩各的,配置割裂、证书管理分散、灰度发布要写一堆 annotation,运维同学天天在群里喊"这个路由到底谁生效了"。后来接触到 Higress,发现它把 Envoy 的数据面能力、Istio 的控制面思路和 Wasm 的插件扩展性揉到了一起,还兼容 Nginx Ingress 的注解习惯,迁移成本比想象中低很多。这篇文章我不打算写成官方文档的复读机,而是想从一个真正在生产里踩过坑的视角,把 Higress 的架构分层、核心组件、Wasm 插件机制、路由匹配逻辑、和 Envoy/Istio 的关系这些事讲透,顺带把我在压测、灰度、插件热更新里遇到的真实问题摊开说。如果你正在选型 API 网关,或者已经用上了 Higress 但对其内部原理一知半解,这篇应该能帮你少走不少弯路。

1. 从 API 网关的演进看 Higress 的定位

1.1 传统网关的三层割裂问题

要理解 Higress 为什么长成现在这个样子,得先看清楚它要解决的是什么问题。过去几年里,一个中等规模的微服务团队,流量入口通常会演化出三层结构:最外层是 Nginx 或者 HAProxy 做四层负载和 TLS 卸载,中间层是 Kong、Nginx Ingress 或者 Spring Cloud Gateway 做七层路由和鉴权,最内层是 Istio 或者自研 RPC 框架做服务间通信治理。这三层各自有独立的配置体系、独立的证书管理、独立的可观测性埋点,运维成本高得离谱。

我印象最深的一次故障,是某个服务的灰度规则在 Nginx Ingress 的 annotation 里写了一份,在 Istio 的 VirtualService 里又写了一份,两边权重不一致,导致 10% 的流量进了新版本,另外 90% 里还有一部分被 Istio 又切走了 20%,最后实际灰度比例变成了 28%,业务方看到监控曲线直接懵了。这种问题的根源不是配置写错了,而是网关的控制面没有统一,每个组件都只对自己那一层负责。

Higress 的定位就是把这层割裂收拢。它本身是一个基于 Envoy 的云原生网关,同时提供了 Ingress 控制器、Gateway API 控制器和 Istio 控制面的兼容能力,也就是说,你可以用一套 CRD 或者一套控制台,同时管理南北向入口流量和东西向服务间流量。这个思路和 Istio 的 Gateway 有点类似,但 Higress 更强调"开箱即用"和"低迁移成本",它内置了对 Nginx Ingress 注解的兼容层,很多老配置改个 IngressClass 就能直接跑。

1.2 Higress 与 Envoy、Istio 的三角关系

很多人第一次接触 Higress 会问:它和 Envoy、Istio 到底什么关系?我用一句话概括:Envoy 是它的数据面内核,Istio 是它的控制面参考模型,Higress 自己是在这两者之上做的一层产品化封装。

具体来说,Higress 的数据面直接复用了 Envoy 的 HTTP 连接管理器、路由匹配引擎、集群管理和过滤器链机制,所以你在 Higress 里看到的很多概念——Listener、Route、Cluster、Filter——都是 Envoy 的原生概念。控制面方面,Higress 没有直接用 Istio 的 Pilot,而是自己实现了一套配置分发逻辑,但它的 CRD 设计(比如 McpBridge、Http2Rpc、WasmPlugin)明显借鉴了 Istio 的 API 风格,同时兼容 Istio 的 Gateway 和 VirtualService 资源。

这种设计的好处是,你既可以用 Higress 自己的控制台和 CRD 做快速配置,也可以在已有 Istio 体系里把 Higress 当成一个 Gateway 来用,控制面还是 Istio,数据面换成 Higress。我实测过这种混合模式,在 Istio 1.18 环境下把 ingressgateway 替换成 Higress 的 Deployment,VirtualService 和 Gateway 资源完全不用改,流量治理规则照常生效,这个兼容性做得相当扎实。

1.3 为什么选 Wasm 而不是 Lua

网关的插件扩展能力是选型时的关键考量。Kong 用 Lua,APISIX 也用 Lua,Nginx 用 C 模块或者 Lua,而 Higress 选择了 Wasm。这个选择背后有很实际的工程考量。

Lua 的问题在于它和 Nginx 的 OpenResty 生态绑定太深,插件运行在 Nginx 的 worker 进程里,一旦插件里有阻塞操作或者内存泄漏,整个 worker 都会受影响。而且 Lua 插件的热更新能力有限,很多场景下还是要 reload Nginx。Wasm 则不同,它运行在 Envoy 的沙箱里,有独立的内存空间和资源限制,插件崩溃不会拖垮整个数据面。更重要的是,Wasm 支持多语言编译,你可以用 Go、Rust、C++、AssemblyScript 写插件,编译成 Wasm 字节码后通过 HTTP 接口动态下发,不需要重启任何进程。

我在生产里做过一次 Wasm 插件的热更新测试,从控制台点击发布到所有网关节点生效,耗时不到 3 秒,期间没有任何连接中断。这个能力在需要紧急修复鉴权逻辑或者调整限流阈值时非常关键。当然 Wasm 也有代价,它的冷启动比 Lua 慢,单个请求的插件执行开销也略高,但在网关这种长连接、高并发的场景下,这点开销基本可以忽略。

2. Higress 核心架构分层拆解

2.1 数据面:Envoy 的定制化改造

Higress 的数据面虽然基于 Envoy,但并不是直接拿官方镜像来用,而是做了不少定制。最核心的改动集中在三个方面:Wasm 插件的加载机制、配置热更新的效率、以及对 Nginx Ingress 注解的解析支持。

Envoy 原生的 Wasm 插件是通过envoy.filters.http.wasm过滤器加载的,配置里需要指定 Wasm 字节码的远程地址或者本地路径。Higress 在这之上封装了一层 WasmPlugin CRD,你只需要声明插件名称、版本、生效范围(全局、特定路由、特定服务),控制面会自动把字节码推送到各个数据面节点,并生成对应的 Envoy 配置。这个封装看起来简单,但背后涉及字节码分发、版本管理、插件依赖解析等一堆细节。

另一个定制点是配置热更新。Envoy 的 xDS 协议本身支持增量更新,但 Higress 在控制面做了一层聚合和去重,避免频繁的小配置变更导致数据面反复重建 Listener。我观察过 Higress 的日志,在批量更新 50 条路由规则时,数据面只触发了一次 Listener 更新,而不是 50 次,这个优化对大规模集群的稳定性很重要。

2.2 控制面:配置分发与 CRD 设计

Higress 的控制面组件叫higress-controller,它本质上是一个 Kubernetes Operator,监听 Ingress、Gateway、VirtualService、McpBridge、WasmPlugin 等资源的变化,转换成 Envoy 能理解的 xDS 配置,通过 gRPC 推送给数据面。

这里有个设计细节值得说:Higress 的控制面并没有把配置直接推给 Envoy,而是先推给一个叫higress-agent的中间层,由 agent 再转发给本地的 Envoy 进程。这个 agent 的作用是隔离控制面和数据面的网络依赖,同时做配置缓存和本地持久化。好处是即使控制面短暂不可用,数据面也能用缓存的配置继续工作,不会因为控制面抖动导致整个网关瘫痪。

CRD 设计上,Higress 有几个核心资源需要重点理解:

CRD 名称作用对应 Envoy 概念
McpBridge定义外部服务注册中心(Nacos、Consul、Eureka)的桥接Cluster
Http2Rpc定义 HTTP 到 Dubbo 等 RPC 协议的转换规则Route + Filter
WasmPlugin定义 Wasm 插件的加载和生效范围HTTP Filter
Ingress兼容 K8s 原生 Ingress,支持 Nginx 注解VirtualHost + Route

McpBridge 这个资源特别实用,它让 Higress 可以直接从 Nacos 或者 Consul 里同步服务列表,不需要每个服务都手动建 Service 和 Endpoint。我们有个混合云场景,部分服务跑在 K8s 里,部分跑在虚拟机上注册到 Nacos,用 McpBridge 把 Nacos 桥接进来后,网关可以直接按服务名路由到虚拟机上的实例,省掉了中间一层代理。

2.3 插件体系:Wasm 插件的生命周期管理

Higress 的插件体系是我认为它最有竞争力的部分。一个 Wasm 插件从开发到上线,完整生命周期包括:编写代码、编译成 Wasm、构建 OCI 镜像、推送到镜像仓库、创建 WasmPlugin 资源、控制面分发、数据面加载、运行时执行。

这里每一步都有坑。比如编译环节,官方推荐用 Go 的proxy-wasm-go-sdk,但这个 SDK 的版本和 Envoy 的 ABI 版本必须匹配,否则插件加载时会报function not found错误。我踩过一次坑,用 SDK v0.3.0 编译的插件在 Higress 1.4 上跑不起来,换成 v0.2.1 才正常,后来查文档才发现是 ABI 兼容性问题。

再比如 OCI 镜像构建,Higress 要求 Wasm 插件以 OCI artifact 的形式存储,你需要用oras工具推送,而不是普通的docker push。这个流程初次接触会觉得繁琐,但好处是插件版本管理和镜像仓库复用了一套基础设施,回滚和灰度都很方便。

插件生效范围的控制也很灵活。你可以在 WasmPlugin 里指定matchRules,按域名、路径、方法、Header 来匹配,只有匹配的请求才会走这个插件。这个能力在做灰度发布时特别有用,比如新版本的鉴权插件只对/api/v2路径生效,其他路径继续走老逻辑。

3. 路由匹配与流量治理的底层逻辑

3.1 Envoy 路由匹配的优先级规则

Higress 的路由匹配最终是落到 Envoy 的 Route 配置上的,所以理解 Envoy 的匹配优先级是绕不开的。Envoy 的路由匹配顺序是:先匹配 VirtualHost(按域名),再匹配 Route(按路径、Header、Query 参数等),匹配规则遵循"精确 > 前缀 > 正则"的优先级。

具体来说,Envoy 的路径匹配有三种类型:path(精确匹配)、prefix(前缀匹配)、safe_regex(正则匹配)。当多个 Route 都能匹配同一个请求时,Envoy 会选择最具体的那条。比如同时存在/api的前缀匹配和/api/user的前缀匹配,请求/api/user/1会命中后者。如果前缀长度相同,则精确匹配优先于前缀匹配。

这里有个容易踩的坑:Higress 在兼容 Nginx Ingress 注解时,nginx.ingress.kubernetes.io/rewrite-target的行为和 Nginx 原生不完全一致。Nginx 的 rewrite 是在 location 匹配之后执行的,而 Higress 是在路由匹配阶段就把 rewrite 规则转换成了 Envoy 的prefix_rewrite或者regex_rewrite。这意味着如果你的 rewrite 规则依赖 Nginx 的变量(比如$1、$request_uri),在 Higress 里可能表现不同。我建议迁移时把 rewrite 规则单独测一遍,别直接照搬。

3.2 灰度发布与流量切分的实现

Higress 支持多种灰度发布方式,最常用的是基于权重的切分和基于 Header 的切分。权重切分通过 DestinationRule 或者 Higress 自己的TrafficSplit注解实现,Header 切分则通过路由匹配规则实现。

权重切分的底层是 Envoy 的 weighted cluster 机制。控制面会把两个版本的服务定义成两个 Cluster,然后在 Route 的weighted_clusters字段里配置权重。比如 v1 权重 90,v2 权重 10,Envoy 在转发时会按这个比例随机选择。这里要注意,权重是按请求级别随机分配的,不是按连接级别,所以对于长连接场景(比如 gRPC 流式调用),权重切分的实际效果可能和预期有偏差,因为一个连接建立后就固定走某个 Cluster 了。

Header 切分的精度更高,适合做金丝雀发布。你可以在路由规则里加一个 match 条件,比如x-canary: true的请求走 v2,其他走 v1。这种方式的优点是可控性强,测试同学可以精确控制哪些请求进新版本。缺点是需要在客户端或者前置代理里注入 Header,对调用方有侵入性。

我实际用下来,比较稳妥的组合是:先用 Header 切分做小范围验证,确认新版本没问题后,再切到权重模式逐步放量。Higress 的控制台支持这两种模式的图形化配置,不用手写 YAML,对不熟悉 Envoy 配置的同学比较友好。

3.3 限流、熔断与重试的参数调优

流量治理里最容易出问题的就是限流和熔断的参数配置。Higress 的限流支持本地限流和全局限流两种模式,本地限流基于 Envoy 的local_rate_limit过滤器,全局限流则需要配合 Redis 做分布式计数。

本地限流的配置相对简单,在路由或者服务级别设置token_bucket的max_tokens和tokens_per_fill即可。但这里有个细节:max_tokens是桶的容量,tokens_per_fill是每次填充的令牌数,填充间隔由fill_interval决定。如果你设置max_tokens: 100、tokens_per_fill: 100、fill_interval: 1s,那实际效果是每秒允许 100 个请求,但允许瞬间突发 100 个。如果你想要更平滑的限流,应该把tokens_per_fill设小,比如 10,fill_interval设成 100ms,这样每 100ms 填充 10 个令牌,桶容量 100,既能应对突发又能控制平均速率。

熔断的配置主要看circuit_breakers的max_connections、max_pending_requests、max_requests、max_retries这几个参数。我的经验是,max_connections不要设得太小,否则高并发时大量请求会被直接拒绝;max_pending_requests可以设成max_connections的 1.5 倍左右,给突发流量一点缓冲;max_retries要谨慎,设太大可能导致重试风暴,把下游打垮。

重试策略上,Higress 支持基于 HTTP 状态码和 gRPC 状态码的重试。我建议只对幂等接口开启重试,并且设置retry_on为5xx或者reset,不要对4xx重试,因为客户端错误重试没有意义。另外num_retries不要超过 3,per_try_timeout要设得比上游平均响应时间略长,避免正常请求被误判为超时。

4. 生产环境实操与踩坑记录

4.1 从 Nginx Ingress 迁移的完整步骤

我们当时从 Nginx Ingress 迁移到 Higress,大概花了三周时间,中间经历了几次回滚。这里把完整步骤和关键决策点整理出来,供参考。

第一步是环境准备。Higress 支持 Helm 安装和 Operator 安装两种方式,我推荐用 Helm,因为升级和回滚更可控。安装前要确认 K8s 版本在 1.20 以上,集群里有可用的 LoadBalancer 或者 NodePort 服务,并且预留足够的 CPU 和内存。Higress 的数据面默认 2 副本,每个副本建议 2 核 4G 起步,如果 QPS 超过 5000,建议 4 核 8G。

第二步是配置同步。Higress 兼容 Nginx Ingress 的大部分注解,但不是全部。我整理了一份常用注解的兼容性对照表:

Nginx 注解Higress 兼容性备注
rewrite-target部分兼容变量语法有差异
proxy-body-size兼容直接映射到 Envoy 的 buffer 限制
ssl-redirect兼容需要配合 force-ssl-redirect
canary-weight兼容映射到 weighted cluster
auth-url不兼容需要用 Wasm 插件替代
configuration-snippet不兼容安全考虑,禁止注入原生配置

第三步是灰度切流。我们先用 DNS 权重把 1% 的流量切到 Higress,观察一周,确认没有 5xx 增长和延迟抖动后,逐步放大到 10%、50%、100%。这个过程中最关键的监控指标是 P99 延迟、5xx 率、连接数和 Wasm 插件的执行耗时。

第四步是回滚预案。Higress 的回滚比 Nginx Ingress 简单,因为配置是 CRD 形式,直接kubectl apply旧版本的 YAML 即可。但要注意,Wasm 插件的回滚需要同时回滚 WasmPlugin 资源和对应的 OCI 镜像版本,否则可能出现配置和插件不匹配的情况。

4.2 Wasm 插件开发与热更新的实操细节

写一个 Wasm 插件,最简流程是这样的:用 Go 的proxy-wasm-go-sdk写一个OnHttpRequestHeaders回调,编译成.wasm文件,用oras推送到镜像仓库,然后创建 WasmPlugin 资源引用这个镜像。

这里有几个实操细节值得展开。首先是 SDK 版本选择,Higress 1.4 对应 Envoy 1.27,需要用proxy-wasm-go-sdk的 v0.2.x 版本,v0.3.x 是为 Envoy 1.28 准备的,ABI 不兼容。其次是编译参数,Go 编译 Wasm 需要设置GOOS=wasip1 GOARCH=wasm,并且加上-buildmode=c-shared,否则生成的字节码 Envoy 加载不了。

热更新方面,Higress 支持两种方式:一种是更新 WasmPlugin 资源里的镜像 tag,控制面会自动拉取新镜像并通知数据面重新加载;另一种是通过控制台的插件市场直接升级。我实测下来,第一种方式更可控,适合生产环境;第二种方式适合测试环境快速验证。

插件执行耗时是个容易被忽视的指标。Higress 的数据面会暴露wasm_filter_execution_time的 metrics,我建议对这个指标设告警,如果 P99 超过 5ms,就要检查插件里是不是有阻塞操作或者频繁的内存分配。Wasm 插件虽然隔离性好,但性能开销还是比原生 Envoy 过滤器高,所以插件逻辑要尽量轻量,避免在请求路径上做复杂计算。

4.3 常见故障排查速查表

生产环境跑了一年多,遇到的故障大概可以归为几类,这里整理成速查表:

故障现象可能原因排查方法解决方案
路由不生效CRD 未同步到数据面查看 higress-controller 日志和 xDS 推送记录检查 CRD 语法,重启 controller
502 错误上游服务不可达检查 Cluster 的 Endpoint 列表和健康检查状态确认服务注册和网络策略
Wasm 插件不执行插件加载失败查看 Envoy 日志里的 wasm 相关错误检查 ABI 版本和镜像地址
延迟突增限流或熔断触发查看 circuit_breakers 和 rate_limit metrics调整阈值或扩容上游
配置更新不生效数据面缓存未刷新检查 higress-agent 的配置版本号手动触发配置同步或重启 agent

其中"路由不生效"是最常见的问题,大部分情况是 CRD 写错了但kubectl apply没报错。Higress 的 CRD 校验比原生 Ingress 严格,但有些字段是 optional 的,写错了不会立即报错,而是静默忽略。我的经验是,每次改完配置后,用kubectl get wasmplugin -o yaml确认一下实际生效的配置,别只看 apply 的返回结果。

另一个高频问题是 Wasm 插件的内存泄漏。Wasm 虽然有沙箱,但如果你在插件里用了全局变量缓存数据,又没有清理机制,长时间运行后内存会持续增长。Higress 的数据面会限制单个插件的内存使用,超过阈值后会触发插件重启,但重启期间请求会走默认逻辑,可能导致鉴权失效。所以插件里尽量避免全局状态,必须缓存的话,用带 TTL 的 LRU 结构。

5. 性能压测与容量规划经验

5.1 压测环境搭建与基准数据

压测这块我踩过的坑最多,因为网关的性能受配置影响极大,同样的硬件,不同的路由数量、插件数量、TLS 配置,QPS 能差好几倍。我们的压测环境是 3 节点 K8s 集群,每个节点 8 核 16G,Higress 数据面 2 副本,每个副本 4 核 8G,用 wrk 和 k6 做压测工具。

基准场景是:100 条路由规则,无 Wasm 插件,纯 HTTP 转发,上游是一个返回固定 JSON 的 Go 服务。实测下来,单副本 QPS 在 18000 左右,P99 延迟 8ms。加上 TLS 后,QPS 降到 12000 左右,P99 延迟 15ms,因为 TLS 握手和加解密有开销。再加上 3 个 Wasm 插件(鉴权、限流、日志),QPS 降到 9000 左右,P99 延迟 22ms。

这个数据说明一个问题:Wasm 插件对性能的影响比 TLS 还大。所以如果你的场景对延迟极其敏感,插件逻辑要尽可能精简,能合并的插件合并,能异步的操作异步化。比如日志插件,不要在每个请求里同步写日志,而是攒批后异步发送。

5.2 容量规划的参考公式

容量规划没有万能公式,但可以给一个粗略的估算方法。假设你的业务峰值 QPS 是 Q,单个 Higress 副本在目标配置下的实测 QPS 是 C,那么需要的副本数 N = ceil(Q / C * 1.5),其中 1.5 是安全系数,用于应对突发流量和单副本故障。

以我们的场景为例,峰值 QPS 是 30000,单副本实测 9000,那么 N = ceil(30000 / 9000 * 1.5) = ceil(5) = 5。实际部署了 6 个副本,留一个冗余。内存方面,每个副本的基础内存占用约 500MB,加上插件和连接缓冲,建议按 1GB 起步,高并发场景按 2GB 规划。

CPU 方面,Envoy 是多线程模型,默认使用所有可用核心,但 Higress 的数据面配置里可以限制 worker 数量。我的建议是 worker 数量设置为 CPU 核心数,不要超配,因为 Envoy 的线程模型是每个 worker 独立处理连接,超配反而增加上下文切换开销。

5.3 长连接场景的特殊处理

gRPC 和 WebSocket 这类长连接场景,网关的容量规划和短连接完全不同。短连接场景下,QPS 是主要指标;长连接场景下,并发连接数才是关键。Envoy 的每个连接会占用一定的内存和文件描述符,默认的max_connections是 1024,高并发场景需要调大。

我们有个 gRPC 服务,峰值并发连接数在 50000 左右,最初用默认配置,网关频繁报too many connections错误。后来把max_connections调到 100000,同时调整了系统的ulimit -n和somaxconn参数,问题才解决。这里要注意,max_connections调大后,内存占用也会上升,每个连接大约占用 10KB 到 50KB,取决于缓冲区配置,所以容量规划时要把这部分算进去。

另外长连接场景下,负载均衡策略要选对。Envoy 默认的round_robin对长连接不友好,因为连接建立后就固定了,后续请求都走同一个上游实例。建议改成least_request或者random,让新连接尽量分散到负载较低的实例上。Higress 的 DestinationRule 里可以配置loadBalancer策略,改起来很方便。

6. 与 Istio 共存的混合部署实践

6.1 控制面复用的两种模式

Higress 和 Istio 共存有两种模式:一种是 Higress 作为 Istio 的 Gateway 使用,控制面还是 Istio 的 istiod,数据面换成 Higress;另一种是 Higress 独立部署,通过 McpBridge 和 Istio 的服务注册中心对接,两边各自管理自己的流量。

第一种模式适合已经在用 Istio 的团队,迁移成本最低。具体做法是把 Istio 的ingressgatewayDeployment 替换成 Higress 的 Deployment,Service 和 Gateway 资源保持不变。Istiod 会继续给 Higress 推送 xDS 配置,Higress 的数据面照常执行。我实测过这种模式,VirtualService 里的路由规则、DestinationRule 里的熔断配置都能正常生效,唯一需要注意的是 Higress 的 Wasm 插件和 Istio 的 EnvoyFilter 可能有冲突,因为两者都会修改 Envoy 的过滤器链。

第二种模式适合 Higress 和 Istio 分别管理南北向和东西向流量的场景。这种模式下,Higress 通过 McpBridge 从 Istio 的 ServiceEntry 或者 K8s Service 里同步服务列表,独立做路由决策。好处是两套体系解耦,升级互不影响;坏处是配置分散,需要两套监控和告警。

6.2 服务发现同步的配置细节

McpBridge 是 Higress 和外部服务注册中心对接的核心资源。以对接 Nacos 为例,配置大概长这样:

apiVersion: networking.higress.io/v1 kind: McpBridge metadata: name: nacos-bridge namespace: higress-system spec: registries: - name: nacos type: nacos2 domain: nacos-server.default.svc.cluster.local port: 8848 nacosGroups: - DEFAULT_GROUP

这里有几个细节要注意。type字段支持nacos、nacos2、consul、eureka、zookeeper等,Nacos 2.x 必须用nacos2,否则长连接会断。domain要填 Nacos 的地址,如果是集群模式,填 VIP 或者域名。nacosGroups指定要同步的分组,不填默认同步DEFAULT_GROUP。

同步过来的服务会以服务名.分组名.nacos的形式出现在 Higress 的服务列表里,路由配置时直接引用这个名称即可。我遇到过一个坑:Nacos 里的服务实例如果健康检查失败,Higress 默认会把它从 Endpoint 列表里摘除,但如果 Nacos 的健康检查本身有问题,可能导致所有实例都被摘除,网关直接 503。所以建议在 McpBridge 里配置healthCheck的兜底策略,或者用 Higress 自己的主动健康检查。

6.3 混合部署下的可观测性统一

混合部署最大的挑战是可观测性。Higress 和 Istio 各自有独立的 metrics、logs、traces,如果不对齐,排查问题时会很痛苦。我的做法是统一用 OpenTelemetry 采集,Higress 的数据面支持 OTLP 协议,可以直接把 trace 和 metrics 推送到 OTel Collector,再转发到后端的 Jaeger 和 Prometheus。

Trace 的上下文传递是关键。Higress 默认会透传traceparent和tracestateHeader,但如果上游服务用的是 Istio 的 Envoy sidecar,两边对 trace 上下文的解析可能不一致。我建议在 Higress 的 Wasm 插件里加一段逻辑,统一把 trace 上下文转换成 W3C 标准格式,这样跨网关和 sidecar 的链路才能串起来。

Metrics 方面,Higress 暴露的指标名称和 Istio 不完全一样,比如 Higress 用envoy_http_downstream_rq_total,Istio 用istio_requests_total。做统一监控时,需要在 Prometheus 里做一层 relabel,把两边的指标映射到同一套命名规范,否则 Grafana 面板要维护两套。

7. 插件生态与二次开发建议

7.1 官方插件的能力边界

Higress 官方提供了一批开箱即用的 Wasm 插件,覆盖鉴权、限流、日志、转换、安全等常见场景。我常用的几个包括:key-auth做 API Key 鉴权,jwt-auth做 JWT 校验,rate-limit做全局限流,request-block做请求拦截,transformer做请求/响应改写。

这些插件的能力边界比较清晰,适合标准化场景。但如果你有定制化需求,比如对接内部的自定义鉴权服务,或者做特殊的协议转换,就需要自己写插件。官方插件的源码在 GitHub 上开源,可以作为二次开发的参考,特别是jwt-auth和rate-limit这两个,代码结构清晰,适合作为入门模板。

需要注意的是,官方插件的版本和 Higress 的版本有对应关系,升级 Higress 时最好同步升级插件,否则可能出现 API 不兼容。我建议在 WasmPlugin 资源里固定插件版本,不要用latest,避免自动升级引入意外变更。

7.2 自定义插件的开发框架选择

写 Wasm 插件,语言选择上主要有 Go、Rust、C++ 三种。Go 的优势是开发效率高,SDK 成熟,适合业务逻辑复杂的插件;Rust 的优势是性能好,内存安全,适合对延迟敏感的插件;C++ 的优势是能直接复用 Envoy 的原生能力,但开发门槛最高。

我个人的选择是:简单插件用 Go,性能敏感插件用 Rust。Go 的proxy-wasm-go-sdk封装得比较好,OnHttpRequestHeaders、OnHttpResponseBody这些回调都有现成的接口,写起来很快。Rust 的proxy-wasm-rust-sdk更底层一些,但编译出来的 Wasm 体积更小,执行效率更高。

开发框架上,Higress 提供了一个wasm-go的脚手架,可以快速生成插件项目结构。我建议基于这个脚手架开发,因为它已经处理好了 SDK 版本、编译参数、OCI 镜像构建这些琐事,你只需要关注插件逻辑本身。

7.3 插件性能优化的几个技巧

Wasm 插件的性能优化,核心思路是减少请求路径上的计算和内存分配。具体来说有几个技巧:

第一,尽量在OnHttpRequestHeaders阶段完成所有判断,不要等到OnHttpRequestBody。因为 Body 阶段需要缓冲整个请求体,内存开销大,而且会延迟请求转发。

第二,避免在插件里做 DNS 解析或者外部 HTTP 调用。如果必须调用外部服务,用异步方式,并且设置超时和重试上限。我见过一个插件在请求路径上同步调用鉴权服务,结果鉴权服务抖动时整个网关的延迟都上去了。

第三,复用对象,减少 GC 压力。Go 编译成 Wasm 后,GC 行为和在原生环境不太一样,频繁创建临时对象会导致内存增长。建议用sync.Pool或者预分配的 buffer 来复用对象。

第四,控制插件数量。每个插件都会在过滤器链上增加一层开销,3 个插件的延迟大约是 1 个插件的 2 倍。能合并的插件尽量合并,比如鉴权和限流可以写在一个插件里,共享解析后的请求上下文。

8. 版本演进与未来值得关注的方向

8.1 从 1.x 到 2.x 的关键变化

Higress 从 1.x 到 2.x 的演进,我关注到几个关键变化。一是对 Gateway API 的支持更完整了,2.x 版本原生支持 Gateway、HTTPRoute、GRPCRoute 这些标准资源,不再只依赖 Ingress 和自定义 CRD。这对想要标准化配置的团队是个好消息,因为 Gateway API 是 K8s 官方推动的方向,未来兼容性会更好。

二是 Wasm 插件的运行时升级,2.x 用了更新的 Envoy 版本,Wasm ABI 也跟着升级,插件需要重新编译。这个升级过程有点痛苦,但换来的是更好的性能和更多的 API 能力。我建议升级前先在测试环境验证所有插件的兼容性,别直接上生产。

三是控制面的性能优化,2.x 在大规模路由场景下的配置分发效率有明显提升。我们有个集群有 2000 多条路由规则,1.x 版本全量推送一次要 10 秒左右,2.x 降到 3 秒以内,这个改进对频繁变更配置的场景很有价值。

8.2 AI 网关能力的探索

最近 Higress 在 AI 网关方向有不少动作,主要是围绕大模型 API 的代理和治理。核心能力包括:多模型路由(根据请求内容路由到不同的模型服务)、Token 级限流(按 Token 数量而不是请求数限流)、语义缓存(相似请求复用缓存结果)、内容安全过滤(敏感词检测和拦截)。

这些能力对做 AI 应用的团队比较实用。比如你同时接了多个模型供应商,可以用 Higress 做统一入口,按成本或者延迟做路由;再比如 Token 限流,传统的请求级限流对 LLM 场景不适用,因为一个请求可能消耗几千个 Token,按 Token 限流才能准确控制成本。

我试用过语义缓存插件,原理是把请求的 prompt 做向量化,然后在缓存里找相似度超过阈值的记录,命中则直接返回缓存结果。这个能力对客服机器人、知识问答这类场景效果不错,能显著降低模型调用成本。但要注意缓存的失效策略,模型更新或者知识库变更后,旧缓存要及时清理,否则会返回过时答案。

8.3 多集群与边缘场景的适配

多集群管理是 Higress 另一个值得关注的方向。通过 McpBridge 和全局控制面,Higress 可以管理多个 K8s 集群的入口流量,实现跨集群的负载均衡和故障转移。这个能力对做多活架构的团队很有吸引力。

边缘场景方面,Higress 的数据面比较轻量,可以跑在资源受限的环境里,比如边缘节点或者 IoT 网关。我测试过在 2 核 2G 的虚拟机上跑 Higress 数据面,处理 1000 QPS 左右的流量没问题,内存占用稳定在 300MB 左右。当然这种场景下要精简插件,只保留必要的鉴权和日志功能。

多集群场景的难点在于配置同步和故障检测。Higress 的控制面支持多集群注册,但集群间的网络延迟会影响配置推送的实时性。我的建议是每个集群部署独立的控制面,通过 GitOps 做配置的版本管理和同步,而不是依赖单一控制面跨集群推送。这样即使某个集群的控制面故障,其他集群也不受影响。


我个人在实际操作中的体会是,Higress 最大的价值不在于它比 Nginx 快多少或者比 Istio 强多少,而在于它把网关的配置、插件、服务发现、流量治理这几件事收拢到了一套体系里,减少了组件间的胶水代码和认知负担。当然它也不是银弹,Wasm 插件的开发门槛、和 Istio 共存的配置复杂度、版本升级的兼容性问题,都是需要提前评估的成本。如果你的团队规模不大,服务数量在几十个以内,可能 Nginx Ingress 加几个 Lua 脚本就够了;但如果服务数量上百、有多集群和多协议治理需求,Higress 的投入产出比会明显更高。最后分享一个小技巧:Higress 的控制台支持配置导出为 YAML,我习惯把生产环境的配置定期导出并提交到 Git 仓库,这样既做了备份,也能通过 diff 快速定位配置变更,排查问题时特别有用。

返回列表