AIBrix 生产环境模型部署实战指南:路由策略、限流与副本治理
【免费下载链接】aibrixCost-efficient and pluggable Infrastructure components for GenAI inference项目地址: https://gitcode.com/GitHub_Trending/ai/aibrix
本指南面向将 LLM 推理服务部署到生产环境的工程师,完整讲解 AIBrix 中一个模型从开发环境走向生产所需的关键配置:必需的模型标签、路由策略选择、Config Profile 多流量类别支持、模型级与副本级限流(RPS / Inflight)、就绪探针、副本规模计算与滚动更新策略,并给出上线前的可观测性检查清单。读完本文,你将能够为任意一个 vLLM / SGLang 等推理服务正确打上 AIBrix 路由标签,配置按流量类别隔离的限流与路由策略,并安全地完成生产发布与扩容。
本文主体基于 model-deployment.rst 展开,并辅以 AIBrix 网关插件的源码实现作为底层原理佐证。
必需标签与注解:让网关认识你的模型
AIBrix 网关通过 Kubernetes 标签(Label)识别模型并为其路由流量。每个由 AIBrix 管理的 Pod 模板至少需要两个标签,缺少任何一个,网关都无法将流量路由到该 Pod:
| 标签 | 说明 |
|---|---|
model.aibrix.ai/name: <model-name> | 模型标识符,即客户端请求中model字段携带的模型名。每个模型必须唯一。 |
model.aibrix.ai/port: "<port>" | 推理服务监听的容器端口(例如"8000")。注意值是字符串。 |
这两个标签的键在源码中以常量形式定义于 pkg/constants/model.go:
// ModelLabelName is the label for identifying the model name // Example: "model.aibrix.ai/name": "deepseek-llm-7b-chat" ModelLabelName = "model.aibrix.ai/name" // ModelLabelPort is the label for specifying the service port // Example: "model.aibrix.ai/port": "8080" ModelLabelPort = "model.aibrix.ai/port"除这两个必需标签外,同文件中还定义了一组常用的可选标签与注解,值得在生产部署中一并了解:
model.aibrix.ai/engine:推理引擎标识(如vllm)。在 Prefill-Decode 解耦(PD)场景下,网关依赖该标签区分 prefill 与 decode 角色,详见 pd-disaggregation.rst。model.aibrix.ai/metric-port:指标端口(如"8000"),供网关抓取引擎指标。model.aibrix.ai/config:承载 JSON 格式的模型级配置(含多 Profile 与限流参数),本文后续章节将大量使用它。model.aibrix.ai/service-name:当模型对外服务名无法作为 Kubernetes 对象名时,指定其背后的 Service。model.aibrix.ai/model-router-custom-paths:为 HTTPRoute 追加的路径前缀,逗号分隔(如/score,/version)。
需要留意的是,模型名优先从标签读取;当模型名包含标签值不允许的字符(如/)时,也可以通过注解携带,ModelNameFromMetadata 会先查标签再查注解。从源码结构看,这一设计让模型命名在保留 Kubernetes 约束的同时具备灵活性。
选择路由策略:按工作负载类型匹配
AIBrix 支持在模型级设置默认路由策略。推荐通过model.aibrix.ai/config注解显式声明,而不是依赖全局的环境变量默认值——这样意图清晰,且不同的模型可以共存于同一集群并使用不同的策略:
annotations: model.aibrix.ai/config: | { "profiles": { "default": { "routingStrategy": "least-latency" } } }针对常见工作负载,原文档给出了一套实用的策略起点:
| 工作负载 | 推荐策略 | 说明 |
|---|---|---|
| 多轮对话(共享系统提示词) | prefix-cache | 将重复前缀路由到已持有对应 KV cache 的 Pod,减少重复 prefill 开销。 |
| 独立请求(批处理、摘要) | least-request | 将负载均匀分散到各 Pod。 |
| 延迟敏感的交互式场景 | least-latency | 路由到近期平均延迟最低的 Pod。 |
| 高吞吐推理 | pd | 分离 prefill 与 decode,最大化 GPU 利用率。 |
| 多用户 SLO 保障 | vtc-basic | 在用户间平衡公平性,同时保持 Pod 负载饱和。 |
各策略的完整列表与详细行为参见 Deploying Gateway 指南,以及网关插件文档 gateway-plugins.rst 中的 "Routing Strategies" 一节。例如该节对几个关键策略的语义描述为:
least-request:路由到当前 in-flight 请求最少的 Pod;least-latency:路由到平均处理延迟最低的 Pod;throughput:路由到累计处理加权 token 最少的 Pod,倾向欠载 Pod;prefix-cache:将请求路由到已持有与请求前缀匹配的 KV cache 的 Pod,在可配置的 stddev 阈值内选择最佳前缀匹配 Pod,支持本地哈希表与 KV 事件同步两种模式;pd:prefill-decode 解耦路由,将处理拆分到专用 prefill Pod 与 decode Pod。
对应实现散落在 pkg/plugins/gateway/algorithms 目录下(如 least_request.go、least_latency.go、prefix_cache.go、throughput.go、pd 与 vtc),每个策略都配有对应测试文件,可作深入参考。
Config Profile:一个部署服务多类流量
Config Profile 让单个模型部署同时服务多种流量类别,而无需拆分多个 Deployment。生产中的常见模式是按客户端类型各定义一个 Profile:
annotations: model.aibrix.ai/config: | { "defaultProfile": "default", "profiles": { "default": { "routingStrategy": "least-latency" }, "batch": { "routingStrategy": "throughput" }, "pd": { "routingStrategy": "pd" } } }客户端通过config-profile请求头选择 Profile:
curl http://${ENDPOINT}/v1/chat/completions \ -H "config-profile: batch" \ -H "Content-Type: application/json" \ -d '{"model": "my-model", "messages": [{"role": "user", "content": "Summarize: ..."}]}'未设置该请求头时,使用defaultProfile指定的 Profile;若defaultProfile未设置,则回退到名为default的 Profile。
Profile 的源码级解析逻辑
配置解析实现在 pkg/plugins/gateway/configprofiles/configprofiles.go,核心数据结构为:
type ModelConfigProfiles struct { LockedRoutingStrategy string `json:"lockedRoutingStrategy,omitempty"` DefaultProfile string `json:"defaultProfile"` Profiles map[string]ModelConfigProfile `json:"profiles"` } type ModelConfigProfile struct { RoutingStrategy string `json:"routingStrategy"` RoutingConfig json.RawMessage `json:"routingConfig,omitempty"` RequestsPerSecond int64 `json:"requestsPerSecond,omitempty"` RequestsPerSecondPerReplica float64 `json:"requestsPerSecondPerReplica,omitempty"` RequestsInflight int64 `json:"requestsInflight,omitempty"` }该包还支持config-profile: auto的自动选择:每个 Profile 的routingConfig内可声明promptTokensGte、promptTokensLt、maxTokensGte、maxTokensLt等请求级选择提示,网关根据请求的实际 token 特征(RequestFeatures)挑选最匹配的 Profile,提示条件越具体优先级越高(见 ResolveAutoProfileName)。此外,lockedRoutingStrategy可在模型级锁定路由策略,优先级高于请求头、Profile 内策略以及ROUTING_ALGORITHM环境变量。
模型级吞吐上限(RPS 限流)
是什么
requestsPerSecond设置网关转发到某个模型的每秒请求数硬上限。超出上限的请求在路由和推理发生之前就被立即拒绝,返回 HTTP429 Too Many Requests。
这是一个模型级上限(所有用户合计),区别于按用户维度的 RPM/TPM 限制。典型用途:
- 保护模型免受突发流量冲击;
- 在共享集群中为模型执行成本预算(GPU 小时数);
- 为同一网关上更高优先级的模型预留余量。
如何配置
在模型model.aibrix.ai/config注解的对应 Profile 中添加requestsPerSecond:
annotations: model.aibrix.ai/config: | { "profiles": { "default": { "routingStrategy": "least-latency", "requestsPerSecond": 50 } } }要为不同流量类别设置不同上限,可按 Profile 分别配置:
annotations: model.aibrix.ai/config: | { "defaultProfile": "default", "profiles": { "default": { "routingStrategy": "least-latency", "requestsPerSecond": 100 }, "batch": { "routingStrategy": "throughput", "requestsPerSecond": 20 } } }上例中,交互式流量(defaultProfile)上限为 100 RPS,批处理流量(batchProfile)上限为 20 RPS。
触发限流时客户端看到什么
HTTP/1.1 429 Too Many Requests x-error-model-rps-exceeded: true {"error": {"message": "model: my-model has exceeded RPS: 50", "type": "rate_limit_error", "code": "rate_limit_exceeded"}}内部工作原理
计数器存储在 Redis 中,每个请求到达时原子递增,1 秒窗口自动重置。若计数器递增后路由失败(例如没有就绪 Pod),递增会被回滚,保证失败的请求不消耗配额。
源码实现位于 gateway_ratelimit.go 的enforceModelRPS与decrModelRPS:
- 预路由门(
enforceModelRPS):在路由前调用。先通过modelRateLimiter.Incr(..., +1)原子递增并取回新值;若newVal > limit则返回 429,并立即用Incr(..., -1)回滚这次未获准的递增;若newVal <= limit则放行。 - 延迟补偿(
decrModelRPS):预充值成功后随即注册。若后续路由失败,则退还配额(Incr(..., -1));若路由成功且请求记账完成,补偿被取消,预充值计数保留。
采用先递增再检查(incr-then-check)而非先检查再递增,是因为INCRBY是唯一的准入关口,Redis 对其原子执行,每个并发调用者都会拿到唯一的顺序结果,从而消除了 check→increment 窗口期的 TOCTOU 竞态、避免超量准入。
底层限流器接口定义于 pkg/plugins/gateway/ratelimiter/rate_limiter.go,Redis 实现见 pkg/plugins/gateway/ratelimiter/redis.go:采用固定窗口计数器,key 结构为{name}:{key}:{timebin},时间桶按(now / windowSeconds) % 64计算(循环 64 个桶,旧桶自动过期);Incr通过 Lua 脚本incrAndExpireScript原子执行INCRBY并按需设置 TTL(仅当 key 尚无 TTL 时,即PTTL == -1),避免持续重试 429 的客户端反复延长窗口。详细设计见 ratelimiter/README.md。
注意:
requestsPerSecond要求网关插件启用 Redis 才能跨副本生效。未启用 Redis 时计数器仅存于进程内,无法在多个网关副本间共享。参见 Deploying Gateway 指南中的 "Enabling Redis for Multi-Replica Deployments"。
省略requestsPerSecond或将其设为0即可禁用该限制。
随副本数伸缩的 RPS 上限
是什么
requestsPerSecondPerReplica设置每副本的 RPS 上限,而非固定模型级上限。网关将其乘以模型当前的可路由副本数,得出有效的聚合上限,因此限流会随模型扩缩容自动伸缩。当同一 Profile 同时设置了requestsPerSecondPerReplica与requestsPerSecond时,前者优先生效。
设置requestsPerSecondPerReplica还会强制将该 Profile 的路由策略改为least-request(覆盖 Profile 中声明的任何routingStrategy),因为每副本限流只有在流量被均匀分摊到各副本时才能作为聚合值成立。
如何配置
annotations: model.aibrix.ai/config: | { "profiles": { "default": { "routingStrategy": "least-latency", "requestsPerSecondPerReplica": 10 } } }例如:4 个可路由副本时有效聚合上限为 40 RPS;扩容到 8 个副本后自动提升至 80 RPS,无需修改注解。
支持小数取值(如0.5),用于低于 1 RPS 的限流,内部表达为 "每 N 秒 1 个请求"。推导出的限流值总是向下取整,保证实际投递速率不会超过配置值——例如0.18会被转换为每 6 秒 1 个请求(约 0.167 RPS),而不是每 5 秒 1 个(约 0.2 RPS,超出配置)。这一取整逻辑与子 1 RPS 场景的窗口换算在 replica_rps_test.go 等测试中有所覆盖。
触发限流时客户端看到什么
与普通requestsPerSecond相同(见上文),因为每副本数值在强制前已被解析为聚合requestsPerSecond值。
注意:与
requestsPerSecond一样,requestsPerSecondPerReplica需要网关插件启用 Redis 才能在集群范围内强制执行。且requestsPerSecondPerReplica与下文requestsInflight均没有环境变量形式——两者都直接在 Profile 中配置。
每副本并发上限(Inflight 限流)
是什么
requestsInflight限制单个副本上允许的并发(in-flight)请求数。与上述 RPS 限制不同,它按 Pod 而非集群聚合强制,因此无需随副本数伸缩——无论模型有多少副本,该上限都成立。
设置requestsInflight同样会强制路由策略为least-request,原因与requestsPerSecondPerReplica一致:只有当路由策略确实为请求选中单个目标 Pod 时,每 Pod 上限才能被强制。
requestsInflight与requestsPerSecondPerReplica是相互独立的限制,可以同时设置(例如 "每个副本最多 3 个并发请求,且每副本不超过 5 RPS")。若requestsInflight配置值低于解析出的每副本 RPS,网关会记录警告提示 RPS 上限实际上可能无法达到,但保持并发上限不变而不会放宽。
如何配置
annotations: model.aibrix.ai/config: | { "profiles": { "default": { "routingStrategy": "least-latency", "requestsInflight": 3 } } }触发限流时客户端看到什么
当每个可路由副本都达到 inflight 上限时,网关返回:
HTTP/1.1 429 Too Many Requests x-error-model-replica-inflight-exceeded: true {"error": {"message": "model: my-model has exceeded replica inflight limit: 3", "type": "overloaded_error", "code": "replica_inflight_exceeded"}}内部工作原理
每个 Pod 的进行中请求数记录在 Redis 中,随请求开始与结束原子更新,因此跨共享同一 Redis 实例的所有网关副本都成立。准入是原子的(一次往返内完成递增与检查),并发落在同一 Pod 的请求即便来自不同网关实例,也无法在同一轮检查中全部越过上限。
实现见 gateway_inflight.go:
enforceReplicaInflight在目标 Pod 已选出后调用,通过s.cache.AdmitPodRunningRequest(pod.Name, pod.Namespace, limit)原子地预留该请求的并发额度(而非先读后写,避免并发请求观察到同一份未递增计数而全部被放行);准入成功即置位ReplicaInflightAdmitted,避免后续请求记账重复计数。filterSaturatedReplicaInflight作为尽力而为的预过滤:一次 Redis 往返批量读取候选 Pod 的 running-request 数,将已达上限的 Pod 从候选中剔除,引导选择避开饱和副本;真正的硬上限仍由enforceReplicaInflight的原子准入把关,因此预过滤即使因 Redis 抖动失败(fail-open),至多只会路由到饱和 Pod 再被拒绝,不会绕过上限本身。- 触发时由
replicaInflightExceededResponse构造 429 响应,并携带x-error-model-replica-inflight-exceeded: true头。
注意:网关插件未启用 Redis 时,
requestsInflight回退为本地进程内计数,仅按单个网关副本强制,而非针对模型副本的集群级强制。
省略requestsInflight或将其设为0即可禁用该限制。
就绪与健康检查
网关只将流量路由到 Kubernetes 判定为Ready的 Pod。请确保就绪探针在推理服务完整加载模型之后才将 Pod 标记为就绪——对于大模型,这可能耗时数分钟。
针对 vLLM 的实用就绪探针示例:
readinessProbe: httpGet: path: /health port: 8000 initialDelaySeconds: 60 periodSeconds: 10 failureThreshold: 30 # allow up to 5 minutes for model load若 Pod 在曾处于就绪状态后未能通过就绪探针(例如发生 OOM),网关会立即停止向其路由,同时不中断已 in-flight 的请求。
副本规模估算
不存在放之四海皆准的公式,但下面是一个实用的起点:
- 测量单副本容量——以递增的 QPS 运行短时压测,直至延迟或错误率劣化,记录可持续的 QPS;
- 留出安全余量——以测得峰值的 60%–70% 为目标,为突发留出空间;
- 计算副本数——
replicas = ceil(target_QPS / sustainable_QPS_per_replica)。
对于 PD 解耦部署,需要分别为 prefill 与 decode Pod 计算规模:prefill Pod 是计算密集型(高输入 token 负载时应增加),decode Pod 是内存带宽密集型(长输出负载时应增加)。PD 的角色划分与桶配置细节可参见 pd-disaggregation.rst。
滚动更新策略
LLM Pod 启动耗时很长。在滚动发布时配置maxUnavailable: 0与maxSurge: 1(或更高),确保发布期间不损失容量:
spec: strategy: type: RollingUpdate rollingUpdate: maxUnavailable: 0 maxSurge: 1maxUnavailable: 0意味着 Kubernetes只有在新 Pod 通过就绪探针后才终止旧 Pod,从而防止网关把流量路由到尚未完成模型加载的 Pod。
对于 PD 解耦部署,应分开发布prefill 与 decode Pod——两者同时更新可能使部分 roleset 暂时不完整,导致网关跳过它们。
上线前可观测性检查清单
在生产发布前,确保你对以下指标具备可见性:
- 每模型请求速率——
aibrix_gateway_requests_total计数器,按model标签细分; - 路由延迟——请求到达与 Pod 选择之间的耗时;
- RPS 限流拒绝——关注
x-error-model-rps-exceeded响应头或对应指标; - Pod 就绪抖动——对在 Ready 与 NotReady 之间反复切换的 Pod 设置告警,这通常意味着 OOM 或不稳定;
- Prefill 超时率(仅 PD)——
pd-prefill-request-error日志条目,高比率表明 prefill Pod 过载。
完整指标参考见 Observability 指南。
生产发布路径速览
综合本文要点,一次标准的生产模型发布流程为:
- 在 Pod 模板上打上
model.aibrix.ai/name与model.aibrix.ai/port两个必需标签; - 通过
model.aibrix.ai/config注解声明profiles,为每类流量设置routingStrategy,并按需配置requestsPerSecond/requestsPerSecondPerReplica/requestsInflight; - 确认网关插件已启用 Redis(多副本部署时为跨副本一致的限流与路由决策所必需),并参考 Deploying Gateway 调整网关插件与 Envoy Proxy 的副本数和资源;
- 为推理服务配置就绪探针(等待模型加载完成),设置合理的
initialDelaySeconds、periodSeconds与failureThreshold; - 按 60%–70% 峰值余量估算副本数,为滚动更新配置
maxUnavailable: 0; - 上线后按可观测性检查清单逐项核对指标与告警。
后续还可结合 autoscaling 为部署配置自动扩缩容,或通过 kvcache-offloading 复用 KV cache 降低 prefill 成本。
【免费下载链接】aibrixCost-efficient and pluggable Infrastructure components for GenAI inference项目地址: https://gitcode.com/GitHub_Trending/ai/aibrix
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考