推理服务多副本主备:流量切换不只是改 DNS — 方向4
推理服务多副本主备流量切换不只是改 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 命中率和模型状态。第三步提前规划冷启动时间窗口确保备副本数量足够覆盖启动时间内的请求负载。基础设施不需要漂亮话需要的是在故障真正发生时切换逻辑能像预期一样工作。