Service Mesh 在 AI 服务治理中的应用——Istio 能帮 AI 应用做什么
Service Mesh 在 AI 服务治理中的应用——Istio 能帮 AI 应用做什么
有一段时间我对 Service Mesh 是持怀疑态度的。
不是说 Istio 不好,是觉得它太重了。一个 sidecar 容器跑在每个 Pod 旁边,光 Envoy 进程就要吃 100MB 内存,对于普通的微服务来说,这个代价是否值得,我一直在打问号。
但当我们的 AI 应用越来越多,服务治理的复杂度上来之后,我改变了看法。准确说,是我找到了 Istio 真正值钱的场景——AI 服务的流量管理。
普通微服务的流量管理需求相对简单,通常在 API Gateway 层就能解决。AI 服务不一样:我们需要在不同的模型版本之间做灰度(新模型效果没验证之前不能全量上),需要对不同用户群做 A/B 测试(VIP 用户用 GPT-4,普通用户用自己的 7B 模型),需要统一管理各个推理服务的超时和熔断(各服务自己配容易漏),还需要在多个 AI 服务之间追踪请求链路。
这些需求,Istio 都能帮你搞定,而且是在应用层完全无感知的情况下。
Istio 的基本工作原理
不打算深入讲 Istio 的架构,但有几个概念必须先说清楚,否则后面的配置看不懂。
Istio 的核心机制是 sidecar 代理。每个 Pod 旁边都注入一个 Envoy 容器,所有进出 Pod 的流量都经过这个 Envoy。你的应用代码完全不知道 Envoy 的存在——它以为自己在直接和对方通信,实际上流量早就被 iptables 规则劫持走了。
控制平面(Istiod)负责把你写的配置(VirtualService、DestinationRule 等)翻译成 Envoy 的配置,推送给每个 sidecar。
VirtualService:定义流量路由规则。比如 "带 header X-User-Type: vip 的请求,路由到 gpt4-service;其余路由到 qwen-service"。
DestinationRule:定义目标服务的策略。比如熔断阈值、连接池大小、负载均衡算法、以及如何区分同一个 Service 的不同子集(subset)。
Gateway:管理集群入口流量,类似 Ingress 但更强大。
PeerAuthentication / AuthorizationPolicy:mTLS 和访问控制,这里不展开。
AI 场景的金丝雀部署
我们有一个场景:从 Qwen2-7B 升级到 Qwen2-14B。14B 的效果更好,但资源消耗翻倍,而且我们对生产流量的实际效果没把握。直接全量切换太激进,我们想先把 5% 的流量导到新模型,观察效果和稳定性,再逐步扩大比例。
传统做法是在代码里写权重路由,或者在 Nginx 配置里做 upstream 权重。用 Istio 的话,只需要改一个 YAML。
先部署两个 Deployment,分别打上不同的版本 label:
# 7B 模型部署(现有版本)
apiVersion: apps/v1
kind: Deployment
metadata:
name: llm-service-v1
namespace: ai-prod
spec:
replicas: 3
selector:
matchLabels:
app: llm-service
version: v1
template:
metadata:
labels:
app: llm-service
version: v1
spec:
containers:
- name: server
image: registry.internal.com/ai/qwen2-7b-server:latest
# ...资源配置省略
---
# 14B 模型部署(新版本)
apiVersion: apps/v1
kind: Deployment
metadata:
name: llm-service-v2
namespace: ai-prod
spec:
replicas: 1
selector:
matchLabels:
app: llm-service
version: v2
template:
metadata:
labels:
app: llm-service
version: v2
spec:
containers:
- name: server
image: registry.internal.com/ai/qwen2-14b-server:latest
# ...资源配置省略
---
# Service 只选 app=llm-service,不区分版本
apiVersion: v1
kind: Service
metadata:
name: llm-service
namespace: ai-prod
spec:
selector:
app: llm-service
ports:
- port: 80
targetPort: 8000然后用 DestinationRule 定义两个 subset:
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
name: llm-service-dr
namespace: ai-prod
spec:
host: llm-service
# 连接池配置,对所有版本生效
trafficPolicy:
connectionPool:
http:
http1MaxPendingRequests: 100
http2MaxRequests: 1000
maxRequestsPerConnection: 10
# 异常检测(熔断)
outlierDetection:
consecutiveGatewayErrors: 5
interval: 30s
baseEjectionTime: 30s
maxEjectionPercent: 50
subsets:
- name: v1
labels:
version: v1
trafficPolicy:
connectionPool:
http:
http1MaxPendingRequests: 200
- name: v2
labels:
version: v2
trafficPolicy:
connectionPool:
http:
http1MaxPendingRequests: 50最后用 VirtualService 定义流量权重:
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: llm-service-vs
namespace: ai-prod
spec:
hosts:
- llm-service
http:
# 规则1:内部测试用的请求,强制走 v2(通过 header 标记)
- match:
- headers:
x-canary-test:
exact: "true"
route:
- destination:
host: llm-service
subset: v2
weight: 100
# 规则2:VIP 用户,50% 走 v2
- match:
- headers:
x-user-tier:
exact: "vip"
route:
- destination:
host: llm-service
subset: v1
weight: 50
- destination:
host: llm-service
subset: v2
weight: 50
# 规则3:其余流量,5% 走 v2,95% 走 v1
- route:
- destination:
host: llm-service
subset: v1
weight: 95
- destination:
host: llm-service
subset: v2
weight: 5
# 给 AI 服务设置合理的超时,防止长请求占用连接
timeout: 120s
retries:
attempts: 2
perTryTimeout: 60s
# 只在连接失败或 503 时重试,不重试 4xx(用户的 Prompt 问题不该重试)
retryOn: connect-failure,reset,503这个配置做到了:QA 可以通过加 x-canary-test: true header 专门测新版本;VIP 用户有 50% 机会体验新模型;普通用户暂时只有 5% 的流量走新模型。全程不需要改应用代码。
当 v2 的各项指标(错误率、P99 延迟)达到预期后,把 weight 从 5/95 改成 50/50,再改成 100/0,最后删掉 v1 的 Deployment 和 v1 这个 subset。
A/B 测试:让不同用户群用不同的模型
A/B 测试和金丝雀部署的区别在于:金丝雀是验证新版本的稳定性,流量是随机的;A/B 测试是主动区分用户群,不同群体用不同版本,收集效果数据后决定策略。
在 AI 产品里,一个典型的 A/B 测试场景是:我们有自研的 7B 模型和 OpenAI 的 GPT-4o,对某类任务想知道哪个效果更好,让用户体感更满意。
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: ai-assistant-vs
namespace: ai-prod
spec:
hosts:
- ai-assistant-service
http:
# 基于用户 ID 的哈希路由,同一用户始终落到同一组(体验一致性)
- match:
- headers:
x-user-id:
regex: ".*[02468]$" # 用户 ID 末位是偶数的走 A 组
route:
- destination:
host: ai-assistant-service
subset: model-a
weight: 100
headers:
response:
add:
x-ab-group: "A"
- match:
- headers:
x-user-id:
regex: ".*[13579]$" # 用户 ID 末位是奇数的走 B 组
route:
- destination:
host: ai-assistant-service
subset: model-b
weight: 100
headers:
response:
add:
x-ab-group: "B"
# 没有 user-id header 的请求(未登录)走默认组
- route:
- destination:
host: ai-assistant-service
subset: model-a
weight: 100注意 x-ab-group 是在 response header 里加的,这样前端能知道当前用户在哪个实验组,方便在埋点数据里关联。
统一超时和熔断管理
这是我认为 Istio 对 AI 应用最实用的功能之一。
在没有 Service Mesh 的情况下,每个 AI 应用都需要自己配置超时、熔断、重试。这些配置散落在各个服务的代码里或者配置文件里,版本不一致,有的服务忘了配,出问题了翻代码才发现。
用 Istio,这些策略集中在 DestinationRule 里统一管理,而且对应用完全透明。
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
name: ai-services-policy
namespace: ai-prod
spec:
# 对 ai-prod 命名空间内所有服务生效
host: "*.ai-prod.svc.cluster.local"
trafficPolicy:
# 连接池:防止某个服务把连接池打满
connectionPool:
tcp:
maxConnections: 500
connectTimeout: 10s
tcpKeepalive:
time: 7200s
interval: 75s
http:
http1MaxPendingRequests: 200
http2MaxRequests: 500
maxRequestsPerConnection: 0 # 0 表示不限制
idleTimeout: 90s
# 这个很重要:AI 推理经常有长时间处理,关闭 h2 升级避免流式问题
h2UpgradePolicy: DO_NOT_UPGRADE
# 负载均衡:LEAST_REQUEST 对 AI 服务比 ROUND_ROBIN 更合理
# 因为请求耗时差异很大,LEAST_REQUEST 能把新请求发到最空闲的实例
loadBalancer:
simple: LEAST_REQUEST
# 熔断(异常检测)
outlierDetection:
# 连续 3 次网关错误(502/503/504)就把实例从负载均衡池摘除
consecutiveGatewayErrors: 3
# 连续 5 次本地错误(连接超时等)摘除
consecutive5xxErrors: 5
# 每 10 秒扫描一次
interval: 10s
# 第一次摘除:30 秒后重新加入
baseEjectionTime: 30s
# 最多摘除 50% 的实例(防止全部摘除导致雪崩)
maxEjectionPercent: 50
# 最小健康实例比例,低于这个就不再摘除(避免过度熔断)
minHealthPercent: 30熔断的参数设置有讲究。对于 AI 推理服务,consecutiveGatewayErrors 不应该设太小——推理服务偶尔超时是正常的(特别是处理复杂任务时),如果设成 1 次错误就熔断,会导致服务频繁被摘除,反而影响可用性。我们目前 3 次是比较稳定的阈值。
流量镜像:用生产流量测试新模型,不影响用户
这是个相当实用的功能。
我们想测试新模型的效果,但又不想让真实用户看到可能有问题的输出。流量镜像可以把生产流量复制一份发给新模型,新模型的响应直接丢弃(用户看不到),但我们可以从日志里分析新模型的输出质量。
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: llm-service-mirror
namespace: ai-prod
spec:
hosts:
- llm-service
http:
- route:
- destination:
host: llm-service
subset: v1
weight: 100
# 把 20% 的流量镜像到 v2,v2 的响应会被 Istio 丢弃
mirror:
host: llm-service
subset: v2
mirrorPercentage:
value: 20.0镜像流量的 Host header 会被修改成 llm-service-shadow,这样新模型的日志可以和生产流量区分。我们在 v2 服务里专门做了日志记录,把输入 Prompt、输出内容、耗时都存到 ElasticSearch,然后用脚本做效果对比分析。
完整的服务治理架构
可观测性:用 Kiali 看 AI 服务的流量拓扑
Istio 配套的 Kiali 是个好东西,可以实时看到服务之间的流量关系,以及每条链路的成功率、延迟。
对于 AI 应用,有几个指标特别有价值:
P99 延迟:平均延迟掩盖问题,P99 才能看到真实的用户体验。AI 推理的 P99 通常比平均值高 5-10 倍,这是正常的,但如果 P99 超过了业务定义的 SLA(比如 10 秒),就需要干预了。
错误率:Istio 自动统计 4xx/5xx 的比例。AI 服务的 4xx 通常是 Prompt 格式错误(用户问题),5xx 才是服务问题,要分开看。
流量成功率:通过 DestinationRule 的 outlierDetection,Istio 会记录哪些实例被熔断,熔断率高说明某个实例有问题。
Kiali 的 Graph 视图可以直观看到整个 AI 服务的调用关系,特别是 Agent 场景下服务链路很长的时候,这个视图比看日志直观多了。
我的真实判断
用了大半年 Istio,我的结论是:
值得引入 Istio 的场景:你有多个 AI 服务需要统一治理,特别是需要做金丝雀发布、A/B 测试的场景;你想要统一的可观测性而不是每个服务自己实现;你需要服务间的 mTLS。
不值得引入 Istio 的场景:你只有一两个 AI 服务,用 Nginx 或者代码里的简单路由就够了;你的团队对 K8s 还不熟,Istio 的调试难度不低,出了问题很难排查。
Istio 最大的成本不是资源消耗,是运维复杂度。sidecar 注入、证书轮换、控制平面版本升级,这些都需要专门的人来维护。如果你们没有 platform 团队,引入 Istio 要慎重。
但如果你的 AI 应用已经到了需要精细化流量管理的阶段,Istio 是目前最成熟的解决方案,没有之一。
