在大模型(LLM)推理服务中,如何高效地进行流量调度是一个核心挑战。不同于传统的微服务,LLM请求具有长耗时、高并发、对GPU显存敏感等特点。Aibrix 作为一款专为 GenAI 设计的云原生基础设施,巧妙地结合了 Envoy Proxy 的强大流量治理能力,实现了一套智能的 LLM 流量转发机制。

本文将深入探讨 Aibrix 是如何利用 Envoy 及 Envoy Gateway 构建其流量转发层的。

Envoy 基础

Envoy Proxy

Envoy Proxy 是一个开源的高性能边缘和服务代理,最初由 Lyft 开发。它专为云原生应用设计,已成为现代服务网格(Service Mesh)和 API 网关的事实标准。通俗来讲,它就是一个类似于Nginx的反向代理服务器,只不过支持更加贴合Kubernetes的配置,成为云原生时代的宠儿。

在 v1.36.2 版本中,Envoy 继续巩固了其作为“通用数据平面”的地位。对于 LLM 场景,Envoy 的以下特性尤为关键:

  • 动态配置更新:利用xDS协议,支持动态配置更新(利用文件系统inotify机制或配置服务器)
  • 高性能:基于 C++ 开发,处理高并发连接时资源消耗极低。
  • 可扩展性:拥有强大的过滤器链(Filter Chain)机制,允许开发者通过 WebAssembly (Wasm) 或 External Processing (ExtProc) 插入自定义逻辑。
  • L7 流量治理:支持 HTTP/2, gRPC 等协议,能够基于 Header、Path 甚至 Body 内容进行精细化路由。

xDS协议

简单来说,xDS 是 Envoy(数据面)和管理服务器(控制面)之间“对话”的语言。Envoy 通过这个协议,动态地获取它需要的所有配置信息,而不需要重启。

核心概念:为什么叫 xDS?

“x” 代表各种资源,”DS” 代表 Discovery Service(发现服务)。
Envoy 需要发现很多东西,所以有一系列的 API:

  • LDS (Listener):监听器发现服务。告诉 Envoy 要监听哪个端口(比如 80 端口)。
  • RDS (Route):路由发现服务。告诉 Envoy 收到请求后,根据 URL 路径该转发给谁。
  • CDS (Cluster):集群发现服务。告诉 Envoy 后端有哪些服务集群(比如 “user-service”)。
  • EDS (Endpoint):端点发现服务。告诉 Envoy 每个集群里具体的 IP 地址和端口是啥(比如 10.1.1.5:8080)。
  • SDS (Secret):密钥发现服务。分发 TLS 证书等敏感信息。
  • RTDS (Runtime Discovery Service):运行时发现服务。可以理解为是Envoy的Feature Flags。

xDS是怎么工作的?

xDS 最常用的模式是基于 gRPC 长连接 的流式传输。

  1. 订阅 (Request):Envoy 启动后,连上管理服务器,发一个请求:“我是节点 X,我想要最新的配置,我现在手里的版本是空。”
  2. 响应 (Response):服务器把配置打包发给 Envoy:“这是最新的配置,版本号是 V1,暗号(Nonce)是 ABC。”
  3. 确认 (ACK):Envoy 收到并成功应用配置后,回复服务器:“收到!我现在版本是 V1,暗号 ABC。”
  4. 报错 (NACK):如果配置有问题,Envoy 会回复:“版本 V1 有错误!我拒绝应用,我还是保持在旧版本,错误原因是…”

只要配置有更新,服务器就会主动推给 Envoy,不需要 Envoy 一直问。

四种传输变体(Variants)

xDS 协议有四种传输变体来告诉xDS该如何传递各种资源,主要区别在于全量还是增量,以及单路还是多路。

  1. SotW (State of the World - 全量世界状态):特点:简单粗暴。只要有一个配置变了,服务器就把所有配置重新发一遍。缺点:如果配置很多,网络传输量大,计算开销大。目标为单个的资源类型。

  2. Incremental (增量/Delta):特点:只传变化的。比如加了一个 IP,只发这个 IP 的变动。优点:高效,省带宽,适合大规模场景。目标为单个的资源类型。

  3. ADS (Aggregated Discovery Service - 聚合发现服务):特点:最推荐的模式,全量。把 LDS, RDS, CDS, EDS 等所有资源类型的请求都放在同一条 gRPC 连接里跑。为什么需要它?:为了保证顺序。比如,你必须先告诉 Envoy 有个“集群 A”(CDS),才能告诉它“把流量转给集群 A”(RDS)。如果分开跑,可能 RDS 先到,CDS 还没到,Envoy 就会报错。ADS 确保了服务器可以按正确顺序发配置。

  4. Incremental ADS:类似于ADS,只不过它是增量传输的。

两个重要机制

A. 最终一致性 (Eventual Consistency)

xDS 是最终一致的。这意味着在更新过程中,可能会有短暂的时间,配置还没完全同步。为了不丢流量,更新顺序很有讲究。

加服务:先推 CDS (集群) -> 再推 EDS (IP) -> 最后推 RDS (路由指向它)。

下线服务:先推 RDS (路由切走) -> 再删 CDS/EDS。

B. 资源预热 (Warming)

Envoy 很谨慎,不会拿到配置立马就用,而是要先“热身”。

比如收到一个新的 Cluster 配置,Envoy 不会立刻把流量导过去,而是会先去获取这个 Cluster 对应的 Endpoint (IP列表)。只有当 IP 列表也拿到了,Envoy 才会觉得这个 Cluster 准备好了(Warmed),才开始往里面导流量。这避免了把流量导向一个空壳子。

Envoy Gateway

虽然 Envoy 功能强大,但其配置(xDS 协议)极其复杂。为了简化在 Kubernetes 环境下的使用,Envoy Gateway (EG) 应运而生。

Envoy Gateway 是 Kubernetes Gateway API 的一种实现。它扮演控制平面(Control Plane)的角色,将 Kubernetes 的高层资源(如 Gateway, HTTPRoute)翻译成 Envoy 能够理解的底层 xDS 配置。这使得运维人员可以用标准的 K8s YAML 来管理复杂的网关规则,而无需手写数千行的 Envoy 配置文件。

Aibrix 基础

Aibrix 是一个开源的、云原生的 GenAI 推理基础设施。它的核心目标是解决 LLM 推理中的成本、性能和扩展性问题。不同于传统的 Kubernetes 调度器或通用的微服务网关,Aibrix 深入理解 LLM 的运行特征,构建了一套“模型感知”的调度与路由体系。

核心挑战与解决方案

在 LLM 推理场景中,我们面临着独特的挑战:

  • 昂贵的计算成本:GPU 资源稀缺且昂贵。Aibrix 通过支持异构硬件混合部署(如混合使用 A100 和 A10)以及对 Spot 实例的完善支持,显著降低了推理成本。
  • 极高的显存敏感性:LLM 的 KV Cache 占用巨大显存。Aibrix 实现了KV Cache 亲和性路由,将具有相同前缀(Prefix)的请求调度到同一节点,最大化缓存复用率,从而大幅降低首字延迟(TTFT)。
  • 复杂的模型管理:为每个微调模型部署独立服务极其浪费。Aibrix 支持 Multi-LoRA 服务,允许在同一个基础模型实例上动态加载和卸载多个 LoRA 适配器,实现高密度的模型托管。

架构组件

Aibrix 的架构设计遵循控制面与数据面分离的原则,同时引入了专门的“智能层”来处理复杂的调度逻辑:

  • 控制面 (Control Plane):

    • Aibrix Controller:负责 Pod 的生命周期管理、自动扩缩容(Autoscaling)。它不基于简单的 CPU/内存指标,而是基于“请求队列深度”和“KV Cache 占用率”等 LLM 原生指标进行扩缩容。
    • Metadata Service:维护集群的全局状态,包括每个 Pod 的实时负载、已加载的模型/LoRA、以及 KV Cache 的分布情况。
  • 数据面 (Data Plane):

    • Envoy Gateway:作为统一的流量入口,负责高性能的 L7 流量转发。
    • Inference Engine:后端推理引擎(如 vLLM, TGI),负责实际的模型计算。
  • 智能路由层 (Intelligence Layer):

    • Gateway Plugin:这是本文的重点。它作为 Envoy 的扩展(ExtProc),实时拦截流量并根据 Metadata Service 的数据做出智能路由决策。

通过这种架构,Aibrix 实现了从“流量进入”到“模型计算”的全链路优化,而 Envoy 在其中扮演了连接“业务意图”与“底层算力”的关键桥梁角色。

Aibrix 进行流量转发及路由的详细原理与代码分析

Aibrix 并没有重新造轮子去写一个高性能网关,而是站在了 Envoy 的肩膀上。它利用 Envoy Gateway 作为流量入口,并通过 External Processing (ExtProc) 机制注入了“AI 智能”。

Aibrix 的流量转发架构可以概括为:数据面由 Envoy 承载,控制面由 Aibrix Plugin 决策

上图展示了 Aibrix 环境下一次典型的 LLM 推理请求流程:

Envoy Gateway API 的处理

用户发起 HTTP POST 请求(如 /v1/chat/completions),经过集群的负载均衡器到达 Aibrix Gateway (Envoy Proxy)。

Aibrix在安装的时候会定义一个名为 aibrix-eg 的 GatewayClass 来告诉 Kubernetes Gateway API 使用 Envoy Gateway 作为
Aibrix 数据面流量 Gateway API 的控制器(Controller)。 随后创建一个名为 aibrix-eg 的 Gateway,所属 GatewayClass 为之前创建的 aibrix-eg(吐槽一下这命名 :( )。 Envoy的控制器监测到 aibrix-eg Gateway 的创建后,会在自己的命名空间下启动一个 Envoy Proxy Pod 做实际的流量转发(反向代理)

Aibrix 会利用 Envoy Gateway 的 Extensions 机制针对 aibrix-eg Gateway 做了一些配置:

  • 利用 ClientTrafficPolicy 给所有客户端到 aibrix-eg Gateway 的流量设置了 bufferLimit: 4194304 (默认值,4MiB)。它定义了 Envoy 在停止从套接字读取数据(即触发 TCP流控/背压)之前,可以为每个连接缓冲的最大字节数。限制缓冲区大小可以防止单个慢速或恶意的连接消耗过多的网关内存。

  • 利用 EnvoyPatchPolicy 通过 JSON Patch 的方式直接修改底层的 xDS 配置来实现如下 Gateway API 尚未支持的高级路由功能:将包含 routing-strategy 请求头的请求直接转发给 target-pod 请求头中指定的 IP,从而绕过 Kubernetes service 的负载均衡。用于实现 KV Cache Affinity(缓存亲和性)。调度器或网关插件计算出某个请求应该由具体的哪个 Pod 处理(因为那个 Pod 显存里有缓存),然后通过 Header 告诉 Envoy 直接发过去,不要轮询。

Aibrix同样会创建一个名字为 aibrix-reserved-router 的 HTTPRoute 将如下路径的流量路由到 Aibrix Envoy Plugin(兜底用,大部分情况下,流量已经通过 Plugin 及上述的 EnvoyPatchPolicy 被转发到了最优的推理服务)。

  • /v1/chat/completions
  • /v1/completions
  • /v1/embeddings
  • /v1/image/generations
  • /v1/video/generations

创建名为 aibrix-reserved-router-metadata-endpoint 的 HTTPRoute 将如下路径的流量路由到 Aibrix Metadata Service。

  • /v1/models
  • /v1/files
  • /v1/batches

通过 Aibrix Gateway Plugin 进行路由选择

Envoy Proxy 不会立即转发给后端推理服务,而是通过 EnvoyExtensionPolicy 接口拦截请求,将其 Header 及 Body 通过 gRPC 发送给 Aibrix Gateway Plugin(即图中的 “The Brain”)或者 Aibrix Metadata Service。Aibrix根据请求头和请求体及自身存储的信息来决定将流量路由到哪个推理服务,然后设置 routing-strategy 和 target-pod Header,让 Envoy Proxy 将流量路由到指定的推理服务。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
apiVersion: gateway.envoyproxy.io/v1alpha1
kind: EnvoyExtensionPolicy
metadata:
name: aibrix-gateway-plugins-extension-policy
namespace: {{ .Release.Namespace }}
labels:
{{- include "chart.labels" . | nindent 4 }}
app.kubernetes.io/component: aibrix-gateway-plugin
spec:
targetRef:
group: gateway.networking.k8s.io
kind: HTTPRoute
name: aibrix-reserved-router
extProc:
- backendRefs:
- name: aibrix-gateway-plugins
port: 50052
processingMode:
request:
body: Buffered
response:
body: Streamed
messageTimeout: {{ .Values.gatewayPlugin.messageTimeout }}
  • Plugin 解析请求中的模型信息和 Prompt。
1
2
3
4
5
6
7
model, message, stream, errRes := validateRequestBody(requestID, requestPath, body.RequestBody.GetBody(), user)
if errRes != nil {
return errRes, model, routingCtx, stream, term
}
routingCtx.Model = model
routingCtx.Message = message
routingCtx.ReqBody = body.RequestBody.GetBody()

Aibrix Gateway Plugin 的处理

Aibrix Gateway Plugin 中的 RouteManager 作为 Aibrix 路由算法实现的核心模块,其主要职责是管理、注册、初始化和选择不同的路由策略(例如:随机路由、轮询路由、基于缓存的亲和性路由等)。当 Envoy 将请求转发给 Plugin 时,Plugin 并不直接写死处理逻辑,而是通过这个 RouterManager 来动态获取处理方案。

以下是 Aibrix Plugin 处理一个请求时的完整工作流:

  1. 接收请求与上下文构建 (Context Setup)
    当 Envoy 通过 ExtProc 协议将请求发给 Plugin 时,Plugin 首先会解析请求头(Header),提取出用户指定的路由策略(例如 routing-strategy: cache-affinity)。这些信息会被封装成 types.RoutingContext。

  2. 策略选择 (Strategy Selection)
    这是工作流中最关键的一步。Plugin 拿着上下文去询问 RouterManager:“用户想要这个策略,我该用哪个算法处理?”

对应代码中的 Select 方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 运行时核心方法:根据上下文决定使用哪个路由对象
func (rm *RouterManager) Select(ctx *types.RoutingContext) (types.Router, error) {
rm.routerMu.RLock()
defer rm.routerMu.RUnlock()

// 1. 尝试查找用户指定的算法 (例如 "cache-affinity")
if provider, ok := rm.routerFactory[ctx.Algorithm]; ok {
// 2. 如果找到了,返回对应的 Router 实例
return provider(ctx)
} else {
// 3. 【容错机制】如果用户指定的策略不存在,自动降级为随机路由 (Random)
// 保证请求不会因为策略拼写错误而失败
klog.Warningf("Unsupported router strategy: %s, use %s instead.", ctx.Algorithm, RouterRandom)
return RandomRouter, nil
}
}

通过这样的设计,路由选择可实现:

  • 动态性:Plugin 不需要重启就能支持不同的请求策略。
  • 高可用:代码显式处理了 else 分支,强制降级到 RouterRandom。这意味着即使客户端传了一个乱七八糟的策略名,Aibrix 也能保证请求被随机分发到一个健康的 Pod,而不是直接报错。
  1. 路由执行与多级回退 (Execution & Fallback)

拿到具体的 Router 对象后,Plugin 会调用该对象的计算方法。但在 AI 推理场景中,智能路由(如基于缓存的路由)可能会失败(例如:缓存所在的 Pod 挂了,或者显存满了)。这时,工作流会进入 Fallback(回退)流程。对应代码中的 SetFallback 逻辑:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 配置路由失败时的备选方案
func (rm *RouterManager) SetFallback(router types.Router, fallback types.RoutingAlgorithm) error {
// ... 检查类型 ...

// 1. 确保系统已初始化完成
<-rm.routerInited.Done()

// ...

// 2. 为当前路由绑定一个“备胎”
if provider, ok := rm.routerFactory[fallback]; !ok {
return ErrFallbackNotRegistered
} else {
// 告诉主路由:如果你搞不定,就用这个 fallback 算法
r.SetFallback(fallback, provider)
}
return nil
}
  • 链式处理:Aibrix 的路由不是“一锤子买卖”。工作流通常是:CacheAffinity Router (失败) -> LeastRequest Router (失败) -> Random Router。
  • RouterManager 在这里负责在启动阶段就把这些“备胎”关系通过 SetFallback 绑定好,确保运行时能顺滑切换。
  1. 结果返回

最终,选定的 Router 计算出目标 Pod 的 IP。Plugin 将这个 IP 放入 target-pod Header 中,通过 gRPC 返回给 Envoy。Envoy 随后根据我们在 YAML 中看到的 original_destination_cluster 配置,将流量转发给该 Pod。

总结

Aibrix 通过将 Envoy 的高性能数据面自定义 Go 插件的智能控制面 相结合,完美解决了 LLM 推理服务中“状态感知”这一核心难题。

  • Envoy Gateway 屏蔽了底层 xDS 的复杂性,让运维更简单。
  • ExtProc 机制 打开了通向业务逻辑的大门,让流量调度可以感知模型、LoRA 适配器甚至 KV Cache 的状态。
  • 多级回退策略 确保了在追求极致性能(缓存命中)的同时,不丢失服务的高可用性。

这种“双层调度”架构——Kubernetes 负责资源(Pod)的生命周期,Aibrix 负责请求(Request)的智能分发——为构建高效、低成本的 GenAI 基础设施提供了极佳的范例。

参考资料

  1. Envoy Proxy Official Documentation
  2. Envoy Gateway Documentation
  3. Aibrix Documentation
  4. Kubernetes Gateway API
  5. vLLM Project