Files
zhuangang/topics/devops/k8s/scenarios/agent-rollout.md
T

4.2 KiB
Raw Blame History

id, title, type, domain, stack, tags, level, prerequisites, est_hours, related, week
id title type domain stack tags level prerequisites est_hours related week
devops.k8s.scenario.agent-rollout AI Agent 服务上 K8s:发布时流式回答不中断、慢启动、密钥管理和按负载扩容 scenario devops
k8s
docker
nginx
cicd
滚动更新
优雅退出
SSE
探针
HPA
Secret
回滚
intermediate
devops.k8s.core-objects
devops.docker.image-dockerfile
2.5
devops.k8s.interview
devops.cicd.release-rollback
14

AI Agent 服务上 K8s:发布时流式回答不中断、慢启动、密钥管理和按负载扩容

业务背景与问题

Agent API 从虚拟机迁移到公司的 K8s 集群后:

  1. 每次发布,正在进行中的流式回答被切断,用户看到“网络错误”;
  2. 新版本启动需要预热(加载提示词模板、建立连接池),偶尔被重启循环;
  3. 有人把模型 API Key 直接写在 Deployment 的环境变量里提交到了仓库;
  4. 高峰期响应变慢,但 CPU 使用率并不高,HPA 没有扩容。

约束

  • 集群由平台团队维护,应用团队只能修改自己的清单。
  • 回答可能持续 60 秒以上。

方案设计

  1. 优雅退出:
    • 应用收到 SIGTERM 后:就绪检查立即返回失败、不再接受新请求、等待进行中的流结束(最长 80 秒)后退出;
    • preStop 短暂等待,给 Ingress / Service 摘除端点的时间;
    • terminationGracePeriodSeconds: 90 大于最长回答时间;
    • 滚动更新 maxUnavailable: 0。
  2. 慢启动:startupProbe 给足启动时间;就绪探针在预热完成后才返回成功。
  3. 密钥:轮换已泄露的 Key;清单中只引用 Secret;Secret 由密钥管理系统同步(或平台提供的方案);CI 中加密钥扫描。
  4. 扩容指标:AI 服务主要在等待模型返回,CPU 低但并发连接数高。改用“每个 Pod 的进行中请求数”作为自定义指标(需要平台支持指标适配器),或者先按经验提高副本下限。
  5. 发布与回滚:镜像固定版本;CI 中先 kubeconform + 规范测试;发布后观察错误率和首 token 延迟,异常时 kubectl rollout undo。

关键代码

FastAPI 应用的优雅退出(示意):

import asyncio, signal

active_streams = 0
shutting_down = False

@app.get("/readyz")
async def readyz():
    if shutting_down:
        raise HTTPException(503)                     # 让 K8s 尽快摘除这个 Pod
    return {"ok": True}

async def drain(timeout: float = 80):
    global shutting_down
    shutting_down = True
    loop = asyncio.get_running_loop()
    deadline = loop.time() + timeout
    while active_streams > 0 and loop.time() < deadline:
        await asyncio.sleep(0.5)                     # 等待进行中的流式回答结束

(Uvicorn 自身也会在收到 SIGTERM 后停止接收新连接并等待请求完成,具体参数如 --timeout-graceful-shutdown 以官方文档为准。)

完整清单:examples/agent-api。SSE 在 Nginx 层的配置见 AI 网关场景。

取舍

选择 优点 缺点
优雅退出 + 长宽限期(本方案) 用户无感知 发布时间变长
前端断线自动续传(从断点继续) 对任何中断都有效 后端要保存生成进度,复杂
发布时间放在低峰期 简单 治标不治本

易错点

  • 宽限期比最长回答时间短,SIGKILL 依然切断。
  • 应用作为 shell 脚本的子进程启动,收不到 SIGTERM(使用 exec 形式的启动命令)。
  • 就绪探针在退出阶段仍然返回成功,新请求继续进来。
  • 以为 Secret 是加密的。

面试怎么讲

“迁移到 K8s 后发布会切断流式回答。我做了优雅退出:收到 SIGTERM 后就绪探针立即失败,停止接新请求,等进行中的流结束;preStop 留出摘除端点的时间,宽限期设为 90 秒,大于最长回答;滚动更新不减少可用副本。慢启动用 startupProbe。泄露的 Key 立即轮换,清单只引用 Secret,CI 加密钥扫描。扩容方面,AI 服务 CPU 不高但并发高,改用进行中请求数作为指标。”