AgentFlow 上 K8s:镜像、HPA、SSE 零停机与成本控制笔记
Agent 从单机 Demo 到生产,卡点往往不在 Agent 引擎怎么写,而在部署层。本地 docker compose up 能跑通完整流水线,不代表能扛 99.9% 可用性和峰值 50x 流量。AgentFlow 是 Node.js + TS monorepo,含 apps/api、apps/worker、apps/sandbox 三个进程。这篇笔记记录它搬上 Kubernetes 的四件事:容器镜像、Deployment 与 HPA、SSE 零停机、成本。
一、镜像:小的不只是省存储,是冷启动与攻击面
三个硬约束:
- 小——镜像越大,K8s 调度拉取越慢,HPA 扩容反应越迟;镜像里包的工具(编译器、curl、bash)越多,被 RCE 后攻击者能做的事越多。生产镜像只留运行产物,不要源码、devDependencies、git 历史。
- 构建快、缓存稳——多阶段构建,先复制
package.json,让只改业务代码时能命中依赖层缓存。 - 运行时非 root——配合
securityContext的 baseline 配置,CVE 被突破后的逃逸难度会明显上升。
三个 app 共用同一个 Dockerfile 模板,只有 CMD 不同。典型分两段:
- builder 段:安装全量依赖。
isolated-vm、@temporalio/core-bridge需要build-essential python3。 - runtime 段:只拷运行产物,去掉构建工具,
USER node。
.dockerignore 不能少。
二、Deployment 拓扑:三个进程三种扩缩
API 层:无状态,承接 SSE。经 Service 接 LoadBalancer,HPA 水平扩。
Worker 层:无 Service,不对外暴露。由队列驱动(BullMQ 订阅),扩缩依据队列深度。
Sandbox 层:单独成一个 Tier。原因是它的 NetworkPolicy 和 securityContext 与其他层完全不同,权限最收紧,也最需要隔离。
健康检查必须区分 readiness 与 liveness:
- liveness 探测进程是否挂;
- readiness 探测 Pod 能否接流量。
混用的坑:LLM 响应慢或瞬时过载时,liveness 超时会触发重启,把暂时繁忙的 Pod 杀掉,引发雪崩。
HPA 别只盯 CPU。Agent 的瓶颈往往不是 CPU,而是 LLM 调用并发数——也就是队列深度。只按 CPU 扩的话,CPU 还没到 70%,队列里已经堆了上万个任务。所以用双指标:
- CPU 70%
- 队列深度
scaleDown 必须设 stabilizationWindowSeconds。LLM 流量秒级抖动,没有稳定窗口,集群会在几分钟内反复扩缩,每次都要拉镜像、起进程、跑 readinessProbe,把集群折腾乱。项目里给的是 300 秒,让缩容慢下来。
三、零停机:别把进行中的 SSE 流一刀切断
SSE 是长连接流式响应。滚动更新处理不好,新版本一上线,就把在跑用户的 SSE 全掐断。
K8s 杀 Pod 的顺序:先摘 Endpoint,把流量切走;再发 SIGTERM;等 terminationGracePeriodSeconds 后强杀。少了额外等待,进行中的 SSE 会瞬间断。
做法是 preStop + 应用层 graceful shutdown:
preStop执行sleep 5。- 应用收到 SIGTERM 后不立刻退出:
/health/ready改为返回 503,让 K8s 摘流量、停接新请求。 - 正在进行的 SSE 流跑完,再退出进程。
terminationGracePeriodSeconds设为 60。
数据库 schema 迁移分两步走:
- 先做向后兼容变更(加列不加约束)。
- 老版本全部退场后,再执行破坏性操作。
这样新老版本并存期间可以安全过渡。
四、成本:LLM 平台月 $50K 压到 $5K
四个阶段:
| 阶段 | 月成本 | 手段 |
|---|---|---|
| Phase 1 原型 | $50K | 无缓存 |
| Phase 2 | $20K | prompt caching + 模型分级:高频 prompt 命中前缀缓存;不同任务路由到不同价位/能力档次的模型 |
| Phase 3 | $8K | 语义缓存:命中缓存就不重复调 LLM |
| Phase 4 | $5K | 精细化:自托管 embedding、压掉多余往返 |
Token 成本熔断是硬约束:单任务 input/output 超上限就中断,防止失控循环烧预算。
小结
Agent 服务部署与普通 Web 的最大差异在于:更长时、更有状态——SSE、长任务、工具隔离。镜像攻击面、队列驱动扩缩、长连接优雅退出、Token 成本,这些都比普通 CRUD 敏感。动手前先想清楚三件事:
- 三个进程怎么各扩各的;
- 滚动发布怎么不掐断在跑流;
- Token 预算怎么兜底。
配套的示例 Dockerfile、K8s 清单、Helm chart、migration 在 examples/ 目录。YAML 可直接 kubectl apply,需要替换占位镜像名与 Secret。本文是架构部署记录,不是逐行实测;具体数值请按自己的业务校准。