前言
上一篇我把通用后台的"完全体"架构画了出来:Kong、Go-zero、Kafka、MongoDB、ES、Python、Rust、Backstage……一套下来技术栈二十多个组件,看着很爽,部署起来就是另一回事了——光是把这些东西在云上拉起来,每个月账单先把我劝退。
这篇是它的"冷启动版本",也就是真正落地一个 MVP 时我会怎么砍。
砍的原则只有一条:MVP 阶段,每多一个需要单独部署、单独运维、单独花钱的组件,都要被审判一次。能不上的坚决不上,能合并的坚决合并。
最终结论很激进:整个系统只在 Sealos 上跑 3 个应用,外加一个白嫖 Vercel 的管理后台。
技术栈全景(MVP 版)
| 分类 | 完全体方案 | MVP 选型 | 备注 |
|---|---|---|---|
| 部署平台 | K8s 自建 | Sealos | 按量计费,托管中间件 |
| 网关 | Kong | Gin 中间件顶上 | |
| 开发语言 | Go | Go | 不变 |
| Web 框架 | Gin | Gin | 不变 |
| RPC | Go-zero | 单体,函数调用即"RPC" | |
| ORM | GORM | GORM | 不变 |
| 后台管理 | Refine | Refine | 发布到 Vercel |
| 异步任务 | Kafka + Asynq | Asynq | 一个就够 |
| 关系型数据库 | PostgreSQL | PostgreSQL | 不变 |
| 连接池 | PgBouncer | PgBouncer | 跟 PG 打包成一个应用 |
| 缓存 / 锁 / 队列 | Redis | Redis | 一鱼三吃 |
| NoSQL | MongoDB | PG 的 JSONB 顶上 | |
| 全文搜索 | Elasticsearch | PG 全文检索顶上 | |
| 配置中心 | Etcd | 环境变量 + YAML | |
| 可观测性 | OTel+Jaeger+Prometheus+Grafana | Sealos 自带监控 + Zap 日志 | |
| 开发者中心 | Backstage | 一个 README 解决一切 | |
| 多语言扩展 | Python + Rust | 等真有 AI 需求再说 |
3 个 Sealos 应用 + 1 个 Vercel 部署,就这。
整体架构
整张图就这么大。
注意几个关键决策:
- 管理后台不占 Sealos 名额。Refine 本质是个 React 静态站点,扔 Vercel 上免费托管,构建产物走 CDN,它只是通过 REST 调用 Sealos 上那个 Go 应用而已。
- Asynq Worker 不单独部署。它和 Gin API 跑在同一个 Go 二进制里,启动时多开一个 goroutine 消费队列。这样"应用端"始终是 1 个应用。
- PgBouncer 和 PostgreSQL 打包成一个应用。Sealos 的数据库托管模板通常自带连接池,或者用一个 Pod 跑两个容器(PG + PgBouncer sidecar),对外只暴露 PgBouncer 的端口。
为什么是 Sealos
选 Sealos 的核心理由,就俩字:省钱。
更准确地说,是省"心智成本"和"现金成本":
- 按量计费。MVP 阶段没流量,半夜没人访问的时候你不希望还在为闲置的虚拟机付费。Sealos 按实际用量结算,跑得少花得少。
- 托管中间件开箱即用。PostgreSQL、Redis 在 Sealos 上是模板,点几下就起来了,不用自己装、自己调参、自己搞高可用脚本。MVP 阶段我没空当 DBA。
- 本质是 K8s,但不用懂 K8s。这点很重要——等业务长大要回到完全体架构时,底层还是 Kubernetes,迁移路径平滑,不会推倒重来。
一句话:Sealos 让你"先用 PaaS 的姿势开发,将来有 K8s 的退路"。
应用端:Go + Gin + GORM + Asynq 的单体
完全体里那条 Kong → Gin → Go-zero RPC 的链路,在 MVP 里被压扁成一个单体进程。
砍掉 Kong,谁来鉴权限流?
Gin 中间件。完全体里把鉴权、限流、CORS 放网关是为了"关注点分离",但那是服务多了之后的奢侈品。MVP 就一个应用,多写几个中间件,比维护一个 Kong 集群划算太多了。限流直接用 Redis 做计数器(INCR + EXPIRE),几十行代码搞定。
砍掉 Go-zero RPC,业务怎么拆?
不拆。MVP 阶段服务拆分是负优化——你还没搞清楚业务边界在哪,拆出来的服务大概率会被推翻重画。单体里用清晰的 Handler / Service / Repository 分层,把边界划在包(package)层面而不是进程层面。将来要拆,把某个 Service 包提出来套上 Go-zero 就是了,业务代码几乎不动。
Asynq Worker 为什么塞进同一个进程?
为了守住"3 个应用"这条线。生产环境里 Worker 和 API 分开部署是对的(互不影响、独立扩缩容),但 MVP 阶段:
func main() {
// 同一个二进制,两条腿走路
go startAsynqWorker(redisOpt) // 后台消费
startGinServer(addr) // 前台 HTTP
}等哪天异步任务把 CPU 吃满影响到 API 响应了,再把 startAsynqWorker 拎出来单独部署——那时候说明你有这个"幸福的烦恼"了,恭喜。
存储层:一个 PG 顶三个组件
完全体里 PostgreSQL、MongoDB、Elasticsearch 各司其职。MVP 里,PostgreSQL 一个人把这活全干了。
- 替代 MongoDB:检测结果、行为事件这类"Schema 灵活"的数据,丢进 PG 的
JSONB字段。配合 GIN 索引,查询性能在 MVP 的数据量下完全够用,还白送了事务能力。 - 替代 Elasticsearch:PG 自带的
tsvector+GIN全文索引,应付中小数据量的搜索绰绰有余。ES 那套分词、集群、JVM 调优,MVP 阶段碰都别碰。 - PgBouncer 依然保留:这是少数我不肯砍的东西。PG 的连接是重资源,哪怕 MVP,一个写得不好的循环里反复建连,也能把
max_connections打满。PgBouncer 用 Transaction 模式兜底,成本几乎为零。
⚠️ PgBouncer 的 Transaction 模式不支持会话级 SET 语句,用之前确认 GORM 没有依赖会话状态的行为(比如自定义的会话级参数),否则会踩坑。Redis:一鱼三吃
Redis 在 MVP 里身兼三职,这是它性价比最高的用法:
缓存、分布式锁、Asynq 的任务队列,全压在同一个 Redis 上。MVP 阶段数据量小、隔离需求低,没必要分多个实例。
唯一要留个心眼的:Asynq 的队列和你的业务缓存共享内存,如果将来任务积压严重,注意别把内存挤爆导致缓存被驱逐。真到那一步,再拆 Redis 实例不迟。
异步任务:只留 Asynq
完全体里我纠结过 Kafka 和 Asynq 的边界——事件驱动、多消费者、数据管道用 Kafka;延迟、重试、定时用 Asynq。
MVP 阶段这个纠结直接消失:Kafka 不上,全用 Asynq。
Kafka 的价值在于高吞吐、多消费者、数据回放——这些是日活百万级的需求。日活万级以下,那套运维成本(ZooKeeper/KRaft、分区、消费组、Rebalance)纯属自我折磨。
需要"事件驱动"的地方,用 Asynq 任务链模拟即可:任务 A 完成后,在 Worker 里直接 client.Enqueue(taskB)。够用。
管理后台:Refine 白嫖 Vercel
这是省钱的点睛之笔:管理后台一分钱不花,还不占 Sealos 应用名额。
完全体里我设计了独立的 Admin BFF,让运营后台和用户 API 走两条链路,避免运营的慢查询拖垮用户侧。MVP 阶段这个担忧暂时不存在——没那么多并发,也没那么复杂的报表。
所以直接让 Refine 复用应用端那套 REST 接口,通过 RBAC 中间件区分管理员权限即可。Refine 本身是纯前端 React CRUD 框架,vercel deploy 一把梭,免费层的构建额度和 CDN 流量对 MVP 完全够用。
等运营报表真的开始拖慢用户响应了,再在 Go 应用里分出一组 /admin 路由、走只读副本——那是增长期的事。配置与可观测性:能少则少
配置:Etcd 砍掉。静态配置走 YAML,敏感信息(DB 密码、JWT 密钥)走 Sealos 的环境变量 / Secret 注入。功能开关这种"动态配置",MVP 阶段重启一下服务也能接受,没必要为了热更新引入配置中心。
可观测性:OTel + Jaeger + Prometheus + Grafana 这套"三件套"全砍。MVP 阶段:
- 日志:Zap 结构化日志,输出到 stdout,Sealos 控制台直接看。
- 指标:用 Sealos 自带的应用监控面板(CPU / 内存 / 请求),够用。
- 链路追踪:单体应用,一次请求基本不跨进程,日志里带个
request_id串起来就行,不需要 Jaeger。
真正需要分布式追踪,是服务拆成一堆微服务之后的事。MVP 是个单体,追踪个啥。
典型场景走读
场景:用户记录一条任务
整个链路在一个进程内完成,没有跨服务调用,没有网关跳转。延迟低,排查问题就看一份日志。这就是单体在 MVP 阶段的爽点。
它怎么长大?
MVP 不是终点,是起点。这套最简架构留好了回到完全体的路:
| 触发信号 | 加什么 | 从哪长出来 |
|---|---|---|
| 异步任务拖慢 API | Asynq Worker 独立部署 | 把同进程的 goroutine 拎成第 4 个应用 |
| 运营查询拖慢用户侧 | Admin BFF / 只读副本 | Gin 里分出 /admin 路由组 |
| 多消费者 / 数据回放需求 | Kafka | 替换 Asynq 的"伪事件驱动"部分 |
| 全文搜索性能不够 | Elasticsearch | 从 PG 的 tsvector 迁出去 |
| 服务边界清晰且团队变大 | Go-zero RPC 拆服务 | 把 Service 包提出来 |
| 服务数量 > 10 | Kong + Backstage + OTel 三件套 | 该上的都上 |
每一步都是"被业务推着走",而不是"提前设计好"。
总结
MVP 架构的核心思路,和完全体正好相反:
完全体追求"边界清晰",MVP 追求"成本最低"。
具体落到这套方案上:
- 3 个 Sealos 应用(Go App / PG+PgBouncer / Redis)+ 1 个免费 Vercel(Refine),把现金成本压到地板。
- 能合并的全合并:Worker 进主进程、PgBouncer 跟 PG 打包、Redis 一鱼三吃、PG 顶替 Mongo 和 ES。
- 能砍的全砍:Kong、Go-zero、Kafka、Etcd、可观测性三件套、Backstage、多语言扩展——这些都是"长大以后"的事。
- 但留好退路:底层是 Sealos(K8s),分层是
Handler/Service/Repo,将来要回到完全体,是"长出来"而不是"推倒重来"。
完全体那篇讲的是"架构刚好够用、且能随业务生长"。这篇是它的另一半——在业务还没生长之前,先别让架构把你拖死。
先活下来,再谈优雅。