1. 从流量入口到云原生核心:Higress 到底解决了什么问题
第一次接触 Higress 是在一个微服务网关选型的场景里。当时团队面临的问题很典型:Kubernetes 集群里跑着几十个微服务,南北向流量用 Nginx Ingress 扛着,东西向流量靠 Istio 的 Sidecar 在撑,结果两套体系各管各的,配置分散、协议不统一、排查问题要跨两个控制面。运维同学每次改一条路由规则,都要在 Ingress 注解和 Istio VirtualService 之间来回切换,稍不留神就写错字段。后来有人推荐了 Higress,说它是“下一代云原生网关”,我抱着试试看的心态搭了一套,结果发现它确实把很多原本割裂的东西捏到了一起。
Higress 是阿里巴巴开源的一款云原生 API 网关,底层基于 Envoy 做数据面,控制面则深度整合了 Istio 的 xDS 协议能力,同时原生支持 Wasm 插件扩展。它最核心的定位是:用一套架构同时覆盖南北向入口流量和东西向服务间流量,并且通过 Wasm 插件机制让业务逻辑可以热插拔、热更新,不用重启网关进程。这听起来像是把 Ingress Controller、API Gateway、Service Mesh 控制面三者的能力做了融合,实际用下来确实如此。
这篇文章适合谁看?如果你正在做微服务网关选型,或者已经在用 Nginx Ingress、Kong、APISIX 这类方案但遇到了扩展性瓶颈,又或者你对 Envoy 和 Istio 的底层原理感兴趣、想搞清楚一个云原生网关到底是怎么运转的,那这篇内容应该能给你一些参考。我会从架构设计、核心组件、Wasm 插件机制、实操部署、常见问题排查几个维度展开,尽量把原理讲透,同时给出可以直接复现的操作步骤。
2. 架构拆解:Higress 为什么选择 Envoy + Istio + Wasm 这套组合
2.1 数据面为什么是 Envoy 而不是自研
很多人会问:既然要做网关,为什么不自己写一个数据面?毕竟 Nginx 性能也很好,OpenResty 生态也成熟。Higress 选择 Envoy 作为数据面,背后有几个很实际的考量。
Envoy 是 Lyft 开源的 L7 代理,最大的特点是为云原生场景从零设计。它的线程模型是单进程多线程 + 非阻塞事件循环,每个 Worker 线程独立处理连接,线程之间通过共享内存通信,避免了传统多进程模型下的惊群问题和锁竞争。相比之下,Nginx 的多进程模型虽然成熟,但在动态配置更新、热重启方面需要额外的进程管理逻辑,而 Envoy 原生支持通过 xDS 协议动态下发配置,不需要 reload 进程。
另一个关键点是 Envoy 的 Filter Chain 机制。Envoy 把网络处理抽象成一系列 Filter,从 Listener Filter 到 Network Filter 再到 HTTP Filter,每一层都可以插入自定义逻辑。Higress 的 Wasm 插件本质上就是挂载在 HTTP Filter Chain 上的一个扩展点。这种设计让插件可以在不修改 Envoy 源码的前提下介入请求处理流程,而且 Wasm 沙箱保证了插件的安全隔离——即使插件代码有 bug,也不会把整个网关进程搞崩。
还有一个容易被忽略的优势:Envoy 的 xDS 协议已经成为服务网格领域的事实标准。Istio、Consul、Kuma 等主流服务网格方案都支持 xDS,这意味着 Higress 可以复用 Istio 的控制面能力,而不需要自己从头实现一套服务发现和配置分发机制。
2.2 控制面为什么复用 Istio 而不是另起炉灶
Higress 的控制面架构可以理解为“Istio 的轻量化封装”。它复用了 Istio 的 Pilot 组件来做服务发现和配置转换,但去掉了 Istio 中一些重量级的组件(比如 Mixer,在新版 Istio 中已经被移除)。具体来说,Higress 的控制面负责以下几件事:
- 监听 Kubernetes API Server:监听 Ingress、Gateway、HTTPRoute、Service 等资源的变化
- 配置转换:把 Kubernetes 原生资源转换成 Envoy 能理解的 xDS 配置
- 配置下发:通过 gRPC 流式接口把 xDS 配置推送给数据面
- 证书管理:集成 cert-manager 或内置 CA,自动签发和轮换 TLS 证书
为什么复用 Istio 而不是自己写?因为 Istio 在服务发现、流量管理、安全策略方面已经积累了大量生产验证过的逻辑。比如 VirtualService 的流量切分、DestinationRule 的负载均衡策略、PeerAuthentication 的 mTLS 配置,这些能力如果重新实现,不仅工作量大,而且容易在边界场景上踩坑。Higress 的做法是站在 Istio 的肩膀上,把 Istio 的能力包装成更易用的 API,同时补充 Istio 原生不具备的网关特性(比如 Wasm 插件的动态加载、AI 网关能力等)。
2.3 Wasm 插件机制为什么比 Lua 和 Filter 更合适
在 Higress 之前,很多网关方案用 Lua(OpenResty)或者原生 Filter(Envoy C++ Filter)来做扩展。这两种方式各有问题:
- Lua 脚本:开发门槛低,但性能损耗明显,而且 Lua 沙箱的安全性依赖 OpenResty 的实现,复杂逻辑下容易出问题
- 原生 Filter:性能最好,但需要编译进 Envoy 二进制,每次修改都要重新编译和重启,迭代效率极低
Wasm 插件走的是中间路线。它把插件编译成 Wasm 字节码,运行时由 Envoy 的 Wasm 运行时(通常是 Wasmtime 或 V8)加载执行。这样做的好处是:
- 热更新:插件可以在不重启 Envoy 的情况下动态加载和卸载
- 语言无关:可以用 C++、Rust、Go、AssemblyScript 等多种语言编写,编译成 Wasm 即可
- 安全隔离:Wasm 沙箱限制了插件的内存访问和系统调用,即使插件有恶意代码也跑不出沙箱
- 性能可接受:相比 Lua 有更好的性能表现,虽然不如原生 Filter,但在大多数场景下足够用
Higress 在 Wasm 插件方面做了不少工程优化,比如提供了 Go SDK 让开发者可以用 Go 写插件(通过 TinyGo 编译成 Wasm),还内置了一个插件市场,常用的鉴权、限流、日志、转换类插件可以直接拿来用。
3. 核心组件与请求处理流程:一个请求从进门到出门经历了什么
3.1 核心组件清单
在深入请求流程之前,先把 Higress 的核心组件列清楚,这样后面讲流程时不会迷路。
| 组件名称 | 角色 | 核心职责 |
|---|---|---|
| Higress Controller | 控制面 | 监听 K8s 资源,生成 xDS 配置,下发到数据面 |
| Higress Gateway | 数据面 | 基于 Envoy 的代理进程,处理实际流量 |
| Pilot (Istio) | 控制面 | 服务发现、配置转换、xDS 服务端 |
| Wasm Plugin Runtime | 数据面扩展 | 加载和执行 Wasm 插件 |
| Console | 管理界面 | 可视化配置路由、插件、证书等 |
| Cert Manager | 证书管理 | 自动签发和轮换 TLS 证书 |
控制面和数据面之间通过 gRPC 流式接口通信,控制面监听 K8s API Server 的变化,一旦有资源更新,就重新生成 xDS 配置并推送给数据面。数据面收到配置后,动态更新 Listener、Route、Cluster 等对象,整个过程不需要重启进程。
3.2 一个 HTTP 请求的完整生命周期
假设客户端发起一个请求https://api.example.com/user/123,这个请求在 Higress 内部的流转过程大致如下:
第一步:TCP 连接建立与 TLS 握手
客户端与 Higress Gateway 的 443 端口建立 TCP 连接。Higress 的 Listener 配置了 TLS 证书,双方进行 TLS 握手。如果启用了 mTLS,还会验证客户端证书。TLS 握手完成后,后续 HTTP 请求都在加密通道中传输。
第二步:Listener Filter Chain 处理
Envoy 的 Listener 上可以挂载 Listener Filter,比如 Proxy Protocol Filter、TLS Inspector Filter 等。这些 Filter 在连接建立后、HTTP 解析前执行,主要用于识别连接属性(如源 IP、协议类型)。
第三步:HTTP 连接管理器与 Filter Chain 匹配
连接进入 HTTP Connection Manager(HCM),HCM 根据请求的 Host、Path、Header 等特征匹配对应的 Filter Chain。Higress 会根据路由配置生成多个 Filter Chain,每个 Chain 上挂载不同的 HTTP Filter。
第四步:Wasm 插件执行
如果某个路由上配置了 Wasm 插件,插件会在这个阶段执行。插件的执行时机可以在请求头解析后、请求体读取前、响应头生成后等不同阶段。比如鉴权插件会在请求头阶段校验 Token,限流插件会在请求头阶段检查配额,日志插件会在响应完成后记录日志。
第五步:路由匹配与集群选择
HTTP Filter Chain 执行完毕后,请求进入路由匹配阶段。Envoy 根据 Virtual Host 和 Route 配置,把请求路由到对应的 Cluster。Cluster 代表一组上游实例,Envoy 会根据负载均衡策略(Round Robin、Least Request、Random 等)选择一个实例。
第六步:上游连接与请求转发
Envoy 与选中的上游实例建立连接(如果连接池中有空闲连接则复用),把请求转发过去。如果配置了重试策略,在失败时会自动重试其他实例。
第七步:响应处理与返回
上游返回响应后,Envoy 会再次经过 Filter Chain(这次是响应方向),执行响应相关的插件逻辑(比如响应头修改、响应体脱敏),最后把响应返回给客户端。
整个流程中,Wasm 插件可以在多个阶段介入,而且插件的执行顺序可以通过配置调整。这种灵活性是 Higress 相比传统网关的一大优势。
3.3 配置下发机制:从 K8s 资源到 Envoy 配置
Higress 的配置下发链路可以概括为:K8s 资源 -> Istio 配置模型 -> xDS 配置 -> Envoy 运行时对象。
当你在 K8s 中创建一个 Ingress 或 McpBridge 资源时,Higress Controller 会监听到这个变化,然后调用 Istio Pilot 的配置转换逻辑,把 K8s 资源转换成 Istio 内部的配置模型(比如 VirtualService、Gateway、DestinationRule)。Pilot 再把这些模型转换成 Envoy 的 xDS 配置(Listener、Route、Cluster、Endpoint),通过 gRPC 流式接口推送给数据面。
数据面收到 xDS 配置后,会动态更新 Envoy 的内部状态。这个过程是增量的,只有变化的配置才会被推送,避免全量更新带来的性能抖动。Envoy 在收到新配置后会进行热更新,旧配置在确认新配置生效后才被替换,保证流量不中断。
4. Wasm 插件开发实战:从零写一个自定义鉴权插件
4.1 开发环境准备
Higress 的 Wasm 插件可以用多种语言开发,官方推荐的是 Go(通过 TinyGo 编译)和 Rust。这里以 Go 为例,因为 Go 的生态更成熟,上手门槛更低。
首先需要安装以下工具:
# 安装 TinyGo(用于把 Go 编译成 Wasm) # 具体安装方式参考 TinyGo 官方文档,不同操作系统略有差异 # 安装 Higress 插件开发工具链 go install github.com/higress-group/wasm-go@latest # 安装 Docker(用于本地构建和测试)TinyGo 是 Go 语言的一个编译器变体,专门针对嵌入式场景和 Wasm 目标做了优化。它支持 Go 的大部分语法,但不支持反射和部分标准库。写 Wasm 插件时要注意避免使用这些特性。
4.2 插件代码结构
一个典型的 Higress Wasm 插件包含以下几个部分:
package main import ( "github.com/higress-group/proxy-wasm-go-sdk/proxywasm" "github.com/higress-group/proxy-wasm-go-sdk/proxywasm/types" "github.com/higress-group/wasm-go/pkg/wrapper" ) func main() { wrapper.SetCtx( "my-auth-plugin", wrapper.ParseConfigBy(parseConfig), wrapper.ProcessRequestHeadersBy(onHttpRequestHeaders), ) } type MyConfig struct { TokenHeader string `json:"token_header"` SecretKey string `json:"secret_key"` } func parseConfig(json gjson.Result, config *MyConfig, log wrapper.Log) error { config.TokenHeader = json.Get("token_header").String() config.SecretKey = json.Get("secret_key").String() return nil } func onHttpRequestHeaders(ctx wrapper.HttpContext, config MyConfig, log wrapper.Log) types.Action { token, err := proxywasm.GetHttpRequestHeader(config.TokenHeader) if err != nil || token == "" { proxywasm.SendHttpResponse(401, nil, []byte("unauthorized"), -1) return types.ActionPause } // 校验 token 逻辑 if !validateToken(token, config.SecretKey) { proxywasm.SendHttpResponse(403, nil, []byte("forbidden"), -1) return types.ActionPause } return types.ActionContinue }这段代码定义了一个简单的鉴权插件:从请求头中读取 Token,校验失败则返回 401 或 403。wrapper.SetCtx是 Higress 提供的封装,简化了插件注册和配置解析的流程。
4.3 编译与部署
编译插件需要用到 TinyGo:
tinygo build -o main.wasm -scheduler=none -target=wasi ./main.go编译产物是一个.wasm文件。接下来需要把它打包成 OCI 镜像,推送到镜像仓库,然后在 Higress 中配置插件引用。
# 构建 OCI 镜像 docker build -t my-registry/my-auth-plugin:v1.0 . # 推送镜像 docker push my-registry/my-auth-plugin:v1.0在 Higress 中启用插件有两种方式:一种是通过 Console 界面配置,另一种是直接创建 WasmPlugin 资源:
apiVersion: extensions.higress.io/v1alpha1 kind: WasmPlugin metadata: name: my-auth-plugin namespace: higress-system spec: url: oci://my-registry/my-auth-plugin:v1.0 phase: AUTHN priority: 100 config: token_header: "Authorization" secret_key: "your-secret-key"phase字段指定插件的执行阶段,priority控制多个插件的执行顺序。配置创建后,Higress Controller 会自动拉取镜像并加载插件,整个过程不需要重启网关。
4.4 插件调试技巧
调试 Wasm 插件比调试普通 Go 程序要麻烦一些,因为插件运行在 Envoy 的沙箱里。几个实用的调试方法:
- 日志输出:用
proxywasm.LogInfo等接口输出日志,日志会打到 Envoy 的标准输出,可以通过kubectl logs查看 - 本地测试:Higress 提供了本地测试工具,可以在不部署到 K8s 的情况下验证插件逻辑
- 单元测试:把核心逻辑抽成纯函数,用 Go 的 testing 包做单元测试,这部分不依赖 Wasm 运行时
注意:TinyGo 编译的 Wasm 插件不支持 Go 的
fmt包和反射,日志输出必须用 proxywasm 提供的接口。另外,插件的内存是有限的,不要在插件中缓存大量数据。
5. 常见问题与排查技巧实录
5.1 配置不生效的排查思路
这是最常见的问题:明明改了配置,但网关行为没变化。排查顺序建议如下:
- 确认资源是否被 Controller 识别:查看 Higress Controller 的日志,看是否有监听和转换记录
- 检查 xDS 推送是否成功:查看数据面日志,看是否收到配置更新
- 确认配置优先级:Higress 支持 Ingress、Gateway API、McpBridge 多种配置来源,不同来源的优先级不同,可能被覆盖
- 检查命名空间和标签选择器:Ingress 和 WasmPlugin 都有命名空间和选择器限制,配置可能没匹配到目标工作负载
5.2 Wasm 插件加载失败的常见原因
| 问题现象 | 可能原因 | 解决方法 |
|---|---|---|
| 插件不执行 | 镜像拉取失败 | 检查镜像地址和拉取密钥 |
| 插件报错退出 | Wasm 内存超限 | 优化插件逻辑,减少内存占用 |
| 插件执行顺序不对 | priority 配置冲突 | 调整 priority 值,值越小优先级越高 |
| 配置解析失败 | JSON 字段类型不匹配 | 检查 config 字段与插件定义是否一致 |
| 插件导致 502 | 插件 panic | 查看 Envoy 日志,定位 panic 位置 |
5.3 性能调优经验
Higress 的性能调优主要从几个维度入手:
- Worker 线程数:Envoy 默认使用 CPU 核心数作为 Worker 线程数,在高并发场景下可以适当调整
- 连接池配置:合理设置上游连接池的大小和空闲超时,避免频繁建连
- Wasm 插件数量:每个插件都会增加请求处理延迟,非必要不挂载过多插件
- 日志级别:生产环境把日志级别调到 warn 或 error,避免大量日志影响性能
我在实际压测中观察到,一个空的 Higress 网关(无插件)在 4 核 8G 的机器上可以轻松跑到 3 万 QPS 以上,挂载一个简单的鉴权插件后 QPS 下降到 2.5 万左右,挂载三个插件后降到 2 万左右。这个损耗在大多数业务场景下是可以接受的,但如果对延迟极其敏感,就需要权衡插件的必要性。
5.4 与 Istio 共存的注意事项
如果集群里已经装了 Istio,再装 Higress 需要注意几点:
- 端口冲突:Istio 的 Ingress Gateway 和 Higress Gateway 可能都想占用 80/443 端口,需要错开
- CRD 冲突:两者都依赖 Istio 的 CRD,版本不一致可能导致问题
- Sidecar 注入:Higress 的 Pod 如果被 Istio 注入 Sidecar,可能导致流量绕行,建议给 Higress 命名空间关闭自动注入
6. 一些实操后的个人体会
Higress 最让我满意的地方是它把“网关”和“服务网格”这两个原本割裂的概念真正融合到了一起。以前做微服务架构,南北向和东西向是两套体系,现在用 Higress 可以统一管理,配置模型也一致,运维成本降低了不少。Wasm 插件机制虽然有一定的学习成本,但一旦跑通,扩展效率比改 Nginx 配置或者写 Lua 脚本高很多。
不过也有几个坑需要提前知道。一是 Wasm 插件的调试体验还不够友好,出问题时定位比较麻烦,建议先在本地把逻辑测透再上生产。二是 Higress 的文档虽然比较全,但部分高级特性的示例偏少,遇到问题可能需要翻源码或者去社区提问。三是如果集群规模很大(比如上千个 Service),控制面的配置推送可能会有延迟,需要关注 Pilot 的性能指标。
最后分享一个小技巧:Higress 的 Console 提供了一个配置诊断功能,可以查看当前生效的完整 xDS 配置。当你怀疑配置没生效时,直接去 Console 里看实际下发的配置,比翻日志快得多。这个功能在排查路由匹配问题时特别有用。