Agent 系统的零停机升级:蓝绿部署与金丝雀发布在 AI 服务中的应用
Agent 系统的零停机升级:蓝绿部署与金丝雀发布在 AI 服务中的应用
一、AI 服务部署的特殊挑战
传统微服务的零停机升级相对成熟:滚动更新 + 健康检查 + 优雅关闭,基本能满足需求。但 AI 服务有额外的复杂度:
- 模型状态的连续性:Agent 在对话中维持上下文,升级会导致上下文丢失
- 推理的不可中断性:一次 LLM 推理可能持续数秒,中断意味着 Token 浪费
- 供应商侧的模型切换:升级可能涉及模型版本变更,新模型的输出格式可能不同
- 工具调用的幂等性问题:Agent 在执行多步任务时,中断后重试可能重复执行同一步骤
这些特点让"直接滚动重启"的方案不可行——你无法在中断一个正在执行 10 步任务的 Agent 后,简单地让新版本接手。
当新版本 Agent 就绪后,我们需要在蓝绿部署、金丝雀发布和滚动更新之间做出策略选择。蓝绿部署侧重于环境隔离与一键切换,金丝雀发布侧重于小流量验证与逐步放量,而滚动更新则是逐个实例替换。考虑到 Agent 系统对状态连续性的严格要求,蓝绿部署往往能提供更安全的切换保障,因此成为许多团队的首选。
二、蓝绿部署:为 Agent 系统定制的双环境方案
蓝绿部署的核心是维护两套完全独立的环境(蓝/绿),切换时通过流量网关将全部流量从旧环境转移到新环境。
# docker-compose.blue.yml — 当前生产环境(蓝)
version: '3.8'
services:
agent-service:
image: agent-service:v2.4.0
---
ports: ["8000:8000"]
environment:
- ENV_COLOR=blue
- LLM_MODEL=gpt-4o-2024-08-06
docker-compose.green.yml — 新版本(绿)
version: '3.8'
services:
agent-service:
image: agent-service:v2.5.0
ports: ["8001:8000"]
environment:
- ENV_COLOR=green
- LLM_MODEL=gpt-4o-2024-11-20
流量切换网关(Nginx/Caddy 配置):
```nginx
upstream agent_backend {
# 默认指向蓝环境
server blue:8000 weight=1;
# green 环境 weight=0,不接收流量
server green:8001 weight=0;
}
# 切换脚本
# switch_to_green.sh
sed -i 's/blue:8000 weight=1/blue:8000 weight=0/' nginx.conf
sed -i 's/green:8001 weight=0/green:8001 weight=1/' nginx.conf
nginx -s reload
Agent 系统的特殊处理——优雅排空:
type GracefulDrainer struct {
activeSessions sync.Map
drainTimeout time.Duration // 最长排空时间
}
func (d *GracefulDrainer) StartDrain() {
// 1. 停止接收新请求(网关已切换流量到新环境)
// 2. 等待现有 Agent 任务完成
deadline := time.Now().Add(d.drainTimeout)
for {
count := d.countActive()
if count == 0 {
break
}
if time.Now().After(deadline) {
// 超过排空时间,强制中断并保存状态
d.forceSaveAndExit()
break
}
time.Sleep(5 * time.Second)
}
}
func (d *GracefulDrainer) forceSaveAndExit() {
d.activeSessions.Range(func(key, value interface{}) bool {
session := value.(*AgentSession)
// 保存 Agent 上下文到持久化存储
session.SaveCheckpoint()
// 通知用户:任务将在新版本中继续
session.NotifyUser("系统升级中,你的对话将在新版本中继续")
return true
})
}
三、金丝雀发布:渐进式验证
对于风险较高的升级(如切换底层 LLM 模型、工具调用协议变更),蓝绿部署的一刀切切换风险太大。金丝雀发布通过逐步增加新版流量来验证稳定性。
// 金丝雀流量控制中间件
func CanaryMiddleware(weight int) gin.HandlerFunc {
return func(c *gin.Context) {
userID := c.GetHeader("X-User-ID")
hash := fnv.New32a()
hash.Write([]byte(userID))
// 基于用户 ID 哈希的一致性路由
// 同一用户始终路由到同一版本,避免 Agent 上下文切换
if int(hash.Sum32()%100) < weight {
c.Set("version", "canary")
c.Request.URL.Host = "agent-canary:8001"
} else {
c.Set("version", "stable")
}
c.Next()
}
}
// 金丝雀升级脚本
func progressiveRollout() {
weights := []int{1, 5, 10, 25, 50, 100}
for _, w := range weights {
log.Printf("金丝雀比例: %d%%", w)
setCanaryWeight(w)
// 观察 5 分钟
if err := monitorMetrics(5 * time.Minute); err != nil {
log.Printf("异常检测到,回滚到稳定版: %v", err)
setCanaryWeight(0)
return
}
}
}
四、回滚策略与状态恢复
回滚场景:
- 新模型输出格式不兼容,Agent 工具调用失败率 > 5%
- 新版本内存泄漏导致 OOM
- 新 Prompt 模板导致 Token 消耗翻倍
type RollbackManager struct {
checkpointStore CheckpointStore
}
func (rm *RollbackManager) Rollback(sessionID string, targetVersion string) error {
// 1. 读取最近的 Agent 上下文快照
checkpoint, err := rm.checkpointStore.Load(sessionID)
if err != nil {
return fmt.Errorf("no checkpoint found: %w", err)
}
// 2. 用旧版本 Agent 恢复上下文
oldAgent := NewAgent(targetVersion)
oldAgent.RestoreSession(checkpoint)
// 3. 重新执行从快照点开始的步骤
// 注意:已完成的工具调用如果是幂等的,无需重试
for _, step := range checkpoint.PendingSteps {
if step.IsIdempotent() {
oldAgent.Execute(step)
} else {
// 非幂等步骤需要人工介入或标记为"可能重复"
oldAgent.ExecuteWithWarning(step, "此步骤可能被重复执行")
}
}
return nil
}
五、总结
Agent 系统的零停机升级需要处理模型状态连续性、推理不可中断性、工具调用幂等性三个特殊约束。蓝绿部署适合低风险升级(配置变更、Prompt 优化),通过双环境 + 一键流量切换实现秒级回滚,但需要排空现有 Agent 任务。金丝雀发布适合高风险升级(模型切换、协议变更),通过用户 ID 哈希的一致性路由,逐步增加新流量的同时保持同一用户的上下文连续性。不管哪种策略,Agent 上下文的保存和恢复是降级和回滚的基础。
更多推荐



所有评论(0)