把 FinOps 拆成两台:分体部署的取舍
把成本分析平台从一台机器拆成两台:DB 主机只跑 TimescaleDB,APP 主机跑 API、Redis、Celery worker 和 beat。这个拆分本身不难,难的是拆分之后你会多出一整类问题——它们都不在"单机部署"的检查清单里。
收益
最直接的是故障隔离。APP 主机跑满 CPU、内存泄漏、或者某个任务把磁盘写满,数据库不会跟着一起挂;反过来,一次误操作的 drop table 也不会直接把 API 进程带走。其次是规格可以分开选——数据库吃 IOPS 和内存,应用吃并发,两者曲线完全不同,合机时只能取一个折中值。
代价
代价集中在三处:
- 迁移不再是单机操作。单机脚本默认数据库和应用在同一个 Compose project 里,拆开后两边的生命周期就解耦了。
- 升级顺序有讲究。数据库迁移应该只由 API 的 entrypoint 执行一次,worker 和 beat 绝对不能再跑一遍,否则并发迁移会打架。正确顺序是:先换 API(让它带迁移),健康检查通过,再换 worker 和 beat。
- 网络与凭据。APP 主机需要连 DB 的 5432,这就要求一条私网链路和一份共享密码;两台机器的时区、时序、证书续期也要各自维护。
升级前必须先想清楚的五件事
- 回滚路径:新版本如果改了数据库 schema,旧镜像未必还能读新 schema。回滚之前先确认兼容性,不兼容就得走"恢复备份",而不是"换回旧镜像"。
- 备份必须真的能恢复:升级前先在 DB 主机上做一次
pg_dump,找不到可备份的容器就让升级直接失败,而不是静默跳过。 - 密钥不能重新生成:Fernet 加密密钥一旦轮换,历史加密的云账号凭据全部读不出来。升级过程中必须原样保留。
- 限流与配额要按新拓扑重算:链路上多一跳、拓扑变了,原来"够用"的阈值可能就不够了。
- 单机脚本要在拓扑上禁用:为单机准备的
deploy.sh直接跑在分体环境上,会试图在 APP 主机上起一个 PostgreSQL,和真正的 DB 主机撞车。
一次真实教训
分体部署完成后,我们发现 APP 主机上的应用把所有请求都记成了同一个客户端 IP。排查下来是:请求经过 nginx 再经过容器端口转发后,TCP 对端的地址永远是 podman 网桥网关,应用拿到的"客户端 IP"其实是它自己。
这在网络慢的时候完全看不出来——请求零星到达,限额永远用不完。某天把链路优化到 100ms 以内,SPA 单页并发十几个 API 请求,限额瞬间被打穿,正常用户开始收到 429。相关细节见 《限流键取错》。
结论是:拓扑拆分做完之后,那些"依赖单台机器假设"的行为(限流键、健康检查来源 IP、迁移执行者)都得重新过一遍,不能只看编排文件能不能跑通。