推理服务多副本主备:流量切换不只是改 DNS — 方向4
2026/7/25 0:04:57 网站建设 项目流程

推理服务多副本主备:流量切换不只是改 DNS — 方向4

一、当一个推理副本挂掉,剩下的事才是重头戏

推理服务的主备架构看起来是一个简单的命题:部署多个副本,主副本挂了备副本顶上。但实际落地时,问题的复杂度远不止"切一下 DNS"这么轻巧。

在 GPU 推理场景下,每个副本都绑定着独占的 GPU 资源。一个 70B 参数的模型推理实例,光是 GPU 显存预热就需要数分钟时间。如果主备切换的决策链路中出现抖动,可能出现的不是"短时间不可用",而是"流量打到还没热起来的新副本上"——请求超时雪崩只是时间问题。

真实的生产环境里,DNS TTL 的传播延迟可能长达数十秒。Infra 团队常见的直觉是调整 TTL 值,但这治标不治本。DNS 缓存分布在各级递归解析器上,你可以控制权威服务器的 TTL,但无法强制中间节点遵守。更关键的是,DNS 切换本身不具备健康探测能力——你的 DNS 指向了新的副本 IP,但那个副本可能因为 GPU OOM 已经僵死了。

推理服务的流量切换,需要的是一个端到端的、感知推理状态的调度方案。

二、推理状态感知的流量调度链路

不同于传统的无状态 Web 服务,推理服务有两个关键状态需要调度系统感知:

GPU 显存状态:模型加载进度、KV Cache 占用率、批次处理能力,这些都直接影响请求能否被正确处理。一个"进程存活但 GPU 不可用"的副本,在传统的 TCP 健康检查下看起来是完全健康的。

请求亲和性:对于流式推理场景,一旦客户端与某个副本建立了 SSE 连接,中途切换意味着上下文丢失。token 级别的连续生成需要会话级别的转发保证。

上述链路的核心在于健康探测与流量分发的解耦。健康探测服务独立于负载均衡器运行,它能直接访问 GPU 节点的 nvidia-smi 接口或 Triton Server 的模型就绪状态端点。负载均衡器只消费探测结果,不参与健康判断本身。

三、多副本主备的状态同步与切换控制

在 Go 服务中实现主备切换,核心是两个组件:状态管理器与切换决策器。

// 推理副本状态定义 type ReplicaState int const ( StateCold ReplicaState = iota // 模型未加载 StateWarming // 模型加载中,GPU显存预热 StateReady // 就绪,可接收请求 StateDegraded // 降级状态,部分GPU故障 StateDead // 不可用 ) // 副本健康信息 type ReplicaHealth struct { NodeID string State ReplicaState GPUMemUsed int64 // 已用显存(MB) GPUMemTotal int64 // 总显存(MB) KVHitRate float64 // KV Cache命中率 LastHeartbeat time.Time } // 切换决策器:基于多维度健康评分决定是否切换 type FailoverDecider struct { mu sync.RWMutex replicas map[string]*ReplicaHealth degradeThreshold float64 // 降级阈值,如KV命中率低于0.5 minReadyReplicas int // 最少就绪副本数 } func (d *FailoverDecider) ShouldFailover(primaryID string) (string, bool) { d.mu.RLock() defer d.mu.RUnlock() primary, exists := d.replicas[primaryID] if !exists || primary.State == StateDead { // 主副本直接失联,紧急切换 return d.pickBestStandby(), true } // 多维度条件判断,而非单一心跳检测 if primary.State == StateDegraded || primary.KVHitRate < d.degradeThreshold || primary.GPUMemUsed > primary.GPUMemTotal*9/10 { return d.pickBestStandby(), true } return "", false } func (d *FailoverDecider) pickBestStandby() string { // 选择StateReady中KV命中率最高、显存最充裕的副本 var bestID string var bestScore float64 = -1 for id, h := range d.replicas { if h.State != StateReady { continue } score := h.KVHitRate + float64(h.GPUMemTotal-h.GPUMemUsed)/float64(h.GPUMemTotal) if score > bestScore { bestScore = score bestID = id } } return bestID }

非空选择不只是看谁活着,而是看谁最能接住流量。一个 KV Cache 命中率极低的备副本,即使进程存活着,切过去也会因为 Cache Miss 导致首 token 延迟飙升。

四、切换的代价:冷启动与亲和性丢失

主备切换并非没有成本。需要正视三个边界:

成本一:模型冷启动。如果备副本是 Cold 状态,模型加载与 KV Cache 预热的时间窗口是硬伤。对于 70B 参数模型,从权重加载到 GPU 显存初始化,在没有预热手段的情况下可能需要 3-5 分钟。这段时间内,备副本实际上是不可用的。

成本二:会话亲和性断裂。对于 SSE 流式推理场景,主备切换意味着连接断开。客户端必须重建连接,从上次中断的 token 位置继续。这需要推理服务端支持 prefix caching。

成本三:集群拓扑感知缺失。在多可用区部署中,如果备副本在另一个 Zone,跨 Zone 的 GPU 通信延迟会让推理延迟增加 10-30%。主备切换时的 RTO 目标必须结合实际的网络拓扑来设定。

适用边界:

  • 适合:非流式的批量推理场景,单次请求延迟可接受百毫秒级抖动。
  • 不适合:强状态依赖的流式对话场景,切换的会话重建成本高于收益。
  • 不适合:只有两个副本的生产环境,一个挂了另一个可能也扛不住完整流量。

五、总结

推理服务的主备架构,本质是状态感知的流量调度问题,而不是简单的 DNS 切换。

落地建议分三步走:第一步,构建 GPU 维度而非进程维度的健康探测体系,用 nvidia-smi 或 Triton 的模型就绪端点替代 TCP 端口检查。第二步,实现多维度切换决策,综合考虑显存、KV Cache 命中率和模型状态。第三步,提前规划冷启动时间窗口,确保备副本数量足够覆盖启动时间内的请求负载。

基础设施不需要漂亮话,需要的是在故障真正发生时,切换逻辑能像预期一样工作。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询