从单体到云原生:一个独立后端的血泪实战
上周五晚上十一点,我戴着 AirPods 听着 Lo-fi beats,在家里的小书房敲代码。突然收到运维告警:生产环境 CPU 飙到 95%!点开日志一看,又是那个“万年不改”的单体服务在搞事情——这已经是我们团队本月第三次线上雪崩了。
作为一家不到 30 人的小厂里唯一负责某条核心业务线的后队开发者(没错,就我一个人扛这条线),我真的有点绷不住了。每天既要对接产品经理天马行空的需求(“能不能加个区块链功能?听起来很酷”),又要应付测试同事凌晨三点发来的 Bug 报告,还得自己部署、监控、回滚……最离谱的是,上周 HR 还悄悄问我:“你最近有更新简历吗?”
于是,我决定不再忍受这个又大又臭的单体应用了。趁着 Q3 没有大促,我咬牙启动了架构演进计划:从单体走向云原生。今天这篇文,就是我这半年踩坑、翻车、再爬起来的实战记录。不讲理论,只说真话。
起点:那个让我又爱又恨的 Django 单体
我们的老系统是用 Python + Django 写的,2018 年上线,至今还在跑。说实话,一开始它真的很香:开发快、部署简单、文档齐全。但随着业务增长,它逐渐变成了“意大利面条式架构”——订单、用户、支付、风控、甚至临时加的“区块链积分兑换”模块(对,产品经理真的让我加了)全塞在一个 repo 里。
典型的症状包括:
- 一次小需求要全量部署,风险极高
- 数据库连接池经常打满,因为所有业务共用同一个 PostgreSQL 实例
- 日志混在一起,排查问题像大海捞针
- 想加个新功能?先祈祷别影响老逻辑
更惨的是,去年双 11 前夕,因为一个风控规则写错了,导致整个下单流程瘫痪。我当时坐在电脑前,手抖得连 kubectl 都打错了(其实那时候我们还没用 k8s)。那一刻我就知道:必须重构。
第一步:微服务拆分 —— 别信教科书,先保命
很多人一说架构演进就直接上 Kubernetes、Service Mesh,但现实是:小厂资源有限,老板只关心“能不能跑、稳不稳、花不花钱”。
所以我定了三个原则:
- 不动数据库(DB 是底线,不敢乱动)
- 渐进式拆分(不能停服)
- 优先拆高风险模块(比如那个“区块链积分”)
实战:把区块链模块拎出来
产品经理非要加的“区块链积分”其实是个伪需求——底层只是调第三方 API 记录交易哈希,并没有真正的链上交互。但它却和其他核心逻辑耦合极深,每次改积分规则都可能炸掉支付流程。
我用 FastAPI 快速写了一个独立服务,只做两件事:
- 接收主站发来的积分事件(通过 RabbitMQ)
- 调用第三方区块链网关,返回 TxID
# blockchain_service/main.py
from fastapi import FastAPI
from celery import Celery
import requests
app = FastAPI()
celery_app = Celery('blockchain_tasks', broker='amqp://guest@rabbitmq//')
@app.post("/record_transaction")
async def record_tx(user_id: str, amount: float):
# 异步处理,避免阻塞主流程
record_blockchain_tx.delay(user_id, amount)
return {"status": "accepted"}
@celery_app.task
def record_blockchain_tx(user_id, amount):
try:
resp = requests.post(
"https://fake-blockchain-api.example.com/tx",
json={"user": user_id, "amount": amount}
)
if resp.status_code != 200:
# 发送告警到企业微信
alert(f"Blockchain API failed: {resp.text}")
except Exception as e:
log_error(e)
关键点:
- 异步解耦:主站只发消息,不等结果
- 失败重试:Celery 自带 retry 机制
- 隔离风险:就算区块链服务挂了,主站照样能下单
上线后,主站的错误率直降 40%。产品经理还夸我“技术前瞻性”(其实是怕背锅)。
第二步:容器化 —— Docker 不是银弹,但真香
光拆服务不够,部署还是靠手动 scp + supervisor,每次发布都像在拆炸弹。
我决定全面 Docker 化。但小厂没 DevOps,只能自己撸 YAML。
# Dockerfile for main app
FROM python:3.9-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
# 关键:不打包虚拟环境,减小镜像体积
EXPOSE 8000
CMD ["gunicorn", "--bind", "0.0.0.0:8000", "myapp.wsgi:application"]
配合 docker-compose 本地调试:
# docker-compose.yml
version: '3'
services:
web:
build: .
ports:
- "8000:8000"
depends_on:
- db
- rabbitmq
db:
image: postgres:13
environment:
POSTGRES_DB: myapp
rabbitmq:
image: rabbitmq:3-management
教训:一开始我把 static 文件也打进镜像,结果每次前端改个 CSS 都要重新 build 整个后端镜像。后来改成 Nginx 独立托管静态资源,清爽多了。
第三步:拥抱云原生 —— 在成本和稳定性之间走钢丝
公司用的是阿里云,预算紧张,所以没法直接上 ACK(阿里云 Kubernetes)。但我发现 Serverless + 容器实例 是个折中方案。
具体做法:
- 核心服务用 ECI(Elastic Container Instance) 部署,按秒计费
- 无状态服务(如区块链积分)用 函数计算 FC
- 数据库继续用 RDS,但加了读写分离
# eci-deployment.yaml (简化版)
apiVersion: apps/v1
kind: Deployment
metadata:
name: main-app
spec:
replicas: 2
template:
spec:
containers:
- name: web
image: registry.cn-hangzhou.aliyuncs.com/myapp/main:latest
ports:
- containerPort: 8000
resources:
limits:
cpu: "1"
memory: "2Gi"
效果:
| 指标 | 单体时代 | 云原生后 |
|---|---|---|
| 部署时间 | 15 分钟 | < 2 分钟 |
| 故障隔离 | 无 | 模块级 |
| 月成本 | ¥8,000 | ¥6,200(省了 22%!) |
| 回滚成功率 | 60% | 98% |
最爽的是,现在我可以半夜在家用手机点一下“重启服务”,不用开电脑。远程办公的快乐,谁懂?
关于 Python 的选择:FastAPI > Flask > Django(在微服务场景)
很多人问:为什么新服务不用 Django?
答:太重了。Django ORM、Admin、Session 这些在单体里是优势,在微服务里全是负担。
我现在主力用 FastAPI,理由很实在:
- 自动生成 OpenAPI 文档,前端对接省事
- Pydantic 模型校验,减少脏数据
- 异步支持好,适合 I/O 密集型任务(比如调区块链 API)
举个接口例子:
from pydantic import BaseModel
class OrderRequest(BaseModel):
user_id: str
items: List[str]
promo_code: Optional[str] = None
@app.post("/orders")
async def create_order(req: OrderRequest):
# 自动校验 req 是否符合模型
# 如果 promo_code 是 "BLOCKCHAIN2023",触发积分服务
if req.promo_code == "BLOCKCHAIN2023":
await send_to_blockchain_service(req.user_id, 10)
return {"order_id": "xxx"}
产品经理看到自动生成的 Swagger 页面,居然主动说:“原来接口是这样用的啊!” —— 这可能是我今年听到最暖心的话。
简历上的“云原生”不是吹的,是熬出来的
说到简历,最近我确实更新了。以前写“熟悉微服务架构”,现在敢写“主导从单体到云原生架构演进,QPS 提升 3 倍,故障恢复时间从 30 分钟降至 2 分钟”。
但只有我知道背后有多难:
- 为了说服老板投钱买 ECI,我做了整整一周的成本对比表
- 为了不让测试同事骂我,我写了详细的迁移 checklist
- 为了监控,我硬着头皮学了 Prometheus + Grafana,现在 dashboard 比我的桌面还干净
最搞笑的是,上周面试一个候选人,他简历写着“精通云原生”,我问他:“如果 Pod 频繁 OOMKilled 怎么办?” 他支支吾吾说“加内存”。我默默关掉了简历——真正的云原生,是在资源受限下还能稳如老狗。
写在最后:小厂程序员的生存之道
我不是大厂架构师,没有百万 QPS 的场景,也没有专职 SRE 团队。我只是一个在家边听音乐边改 Bug 的普通后端,想让系统别在半夜把我叫醒。
从单体到云原生,不是技术炫技,而是用最小成本换取最大稳定性。如果你也在小厂挣扎,我的建议很朴素:
- 先解决最痛的点(比如那个总炸的模块)
- 别追求一步到位,能跑就行,后续再优化
- 文档和监控比代码更重要(否则下次出事还是你背锅)
- 学会用云厂商的托管服务,别重复造轮子
对了,那个“区块链积分”功能,上个月已经下线了——产品经理说“风向变了”。但没关系,拆出来的服务还能复用,代码可维护性也提高了。至少,我的简历又多了一行实战经验。
现在,我要去改另一个需求了:“能不能接 Web3 钱包登录?” …… 我默默打开了 FastAPI 文档,顺手把 Lo-fi 播放列表切到了“Coding in the Rain”。
(完)

评论 0