← 返回文章列表

把 FinOps 拆成两台:分体部署的取舍

2026-10-24 · 部署 · FinOps

把成本分析平台从一台机器拆成两台:DB 主机只跑 TimescaleDB,APP 主机跑 API、Redis、Celery worker 和 beat。这个拆分本身不难,难的是拆分之后你会多出一整类问题——它们都不在"单机部署"的检查清单里。

收益

最直接的是故障隔离。APP 主机跑满 CPU、内存泄漏、或者某个任务把磁盘写满,数据库不会跟着一起挂;反过来,一次误操作的 drop table 也不会直接把 API 进程带走。其次是规格可以分开选——数据库吃 IOPS 和内存,应用吃并发,两者曲线完全不同,合机时只能取一个折中值。

代价

代价集中在三处:

升级前必须先想清楚的五件事

  1. 回滚路径:新版本如果改了数据库 schema,旧镜像未必还能读新 schema。回滚之前先确认兼容性,不兼容就得走"恢复备份",而不是"换回旧镜像"。
  2. 备份必须真的能恢复:升级前先在 DB 主机上做一次 pg_dump,找不到可备份的容器就让升级直接失败,而不是静默跳过。
  3. 密钥不能重新生成:Fernet 加密密钥一旦轮换,历史加密的云账号凭据全部读不出来。升级过程中必须原样保留。
  4. 限流与配额要按新拓扑重算:链路上多一跳、拓扑变了,原来"够用"的阈值可能就不够了。
  5. 单机脚本要在拓扑上禁用:为单机准备的 deploy.sh 直接跑在分体环境上,会试图在 APP 主机上起一个 PostgreSQL,和真正的 DB 主机撞车。

一次真实教训

分体部署完成后,我们发现 APP 主机上的应用把所有请求都记成了同一个客户端 IP。排查下来是:请求经过 nginx 再经过容器端口转发后,TCP 对端的地址永远是 podman 网桥网关,应用拿到的"客户端 IP"其实是它自己。

这在网络慢的时候完全看不出来——请求零星到达,限额永远用不完。某天把链路优化到 100ms 以内,SPA 单页并发十几个 API 请求,限额瞬间被打穿,正常用户开始收到 429。相关细节见 《限流键取错》。

结论是:拓扑拆分做完之后,那些"依赖单台机器假设"的行为(限流键、健康检查来源 IP、迁移执行者)都得重新过一遍,不能只看编排文件能不能跑通。