专家(一):Claude Code × 微服务------从单体拆分到 Kubernetes 部署
Windows 10/11 · Claude Code v2.1.32+ · DeepSeek V4 Pro / Anthropic API · 🟡 中度时效 · 最后更新 2026-05-13
一、这篇教程解决什么问题
一句话定位:读完本篇,你会用 Claude Code 把一个 FastAPI 单体电商系统拆分为 6 个微服务------从 DDD 限界上下文识别、gRPC 接口定义、逐个服务实现与测试、Docker 多阶段构建到 Kubernetes 生产级部署(Deployment + Service + Ingress + HPA + 分布式追踪),全程 AI 主导、你来审校。
这是专家系列的开篇------不再做 Todo App。场景是真实的:一个业务膨胀的电商单体,代码开始互相绊脚,部署越来越慢,团队在考虑"要不要拆"。
真实案例速览(这些不是理论------是真实的人用 Claude Code 完成的微服务项目):
| 案例 | 规模 | 传统估算 | AI 辅助实际 | 加速比 |
|---|---|---|---|---|
| Alex Chen: Django 单体拆分 | 12万行→5个微服务 | 12周 | 14天 | ~6x |
| Liran Farage: CI/CD 迁移 | 35+ Serverless 微服务 | 数周 | 几天 | ~5x |
| Nubank: ETL 拆分 | 600万行代码 | --- | 12x 工时节省 | 12x |
阅读前提(硬条件):
- 读过高手进阶(五)子代理、(六)CI/CD、(八)综合实战
- 了解微服务基本概念(服务拆分、gRPC、K8s 是什么)
- 了解 FastAPI + SQLAlchemy 基础
- 本地安装了 Docker Desktop + kubectl + Python 3.11+
DeepSeek 用户注意:全部可用。本文全程使用 DeepSeek V4-Pro 完成,每个阶段记录实际 Token 消耗。文末附完整成本对比。
读完能得到什么:
- 一套DDD 限界上下文识别的 Claude Code 实操方法------不是画 PPT,是逐文件分析依赖
- 6 个微服务的完整 gRPC + FastAPI 实现------proto 定义、服务代码、数据库迁移
- 全套 Kubernetes 生产级 YAML(Deployment/Service/Ingress/HPA/ConfigMap/Secret)由 Claude Code 生成
- Jaeger + Prometheus + Grafana 分布式追踪和监控体系
- 真实数据------每个微服务的 Token 消耗、生成代码量、人工修改比例
- 5 个真实 Debug(gRPC 服务发现失败、K8s CrashLoopBackOff、消息队列幂等性、跨服务认证失败、分布式追踪断链)
二、项目全景:我们要拆什么
2.1 单体现状
一个名为 ShopFast 的电商系统,FastAPI 单体,运行了 18 个月:
shopfast/
├── app/
│ ├── main.py # 越来越大的 FastAPI 入口
│ ├── models/ # 47 个 SQLAlchemy 模型,全部耦合在一起
│ │ ├── user.py
│ │ ├── product.py
│ │ ├── order.py # import user, product, payment, inventory...
│ │ └── payment.py
│ ├── routers/ # 23 个路由文件
│ ├── services/ # 部分 Service 直接查表绕过模型层
│ └── utils/ # 到处 import 的工具函数
├── alembic/ # 一个共享数据库的迁移
└── tests/ # 覆盖率 18%
典型症状:
- 改
user.py中的一个字段 → 11 个文件报类型错误 - 部署:整个应用 1.2GB Docker 镜像 → 推到 registry 5 分钟 → 重启 3 分钟
- 订单模块出 bug → 部署修复 → 同时部署了用户模块的未测试改动 → 用户登录挂了
- 3 个循环依赖:order↔user↔notification、product↔inventory↔order、payment↔order↔user
2.2 目标架构
拆分为 6 个微服务 + 1 个 API 网关:
┌────────────────────────────────┐
│ Nginx Ingress (K8s) │
└───────────────┬────────────────┘
│
┌──────────────────┴──────────────────┐
│ API Gateway (FastAPI) │
│ 统一认证 · 路由转发 · 限流·熔断 │
└──────────────────┬──────────────────┘
│
┌────────────┬──────────────┼──────────────┬────────────┐
│ gRPC │ gRPC │ gRPC │ gRPC │ gRPC
▼ ▼ ▼ ▼ ▼
┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐
│ User │ │ Product │ │ Order │ │ Payment │ │Notify │
│ Service │ │ Service │ │ Service │ │ Service │ │Service │
│ │ │ │ │ │ │ │ │(仅消费) │
│ Postgres │ │ Postgres │ │ Postgres │ │ Postgres │ │ Redis │
└──────────┘ └──────────┘ └────┬─────┘ └──────────┘ └──────────┘
│
│ Publish Events
▼
┌────────────────┐
│ Apache Kafka │
└────────┬───────┘
│
┌─────────────────┴─────────────────┐
▼ ▼
┌──────────────┐ ┌──────────────┐
│ Notification │ │ Analytics │
│ Service │ │ Service │
└──────────────┘ └──────────────┘
2.3 各服务职责
| 服务 | 职责 | 数据库 | 对外接口 | 内部通信 |
|---|---|---|---|---|
| API Gateway | 统一入口、JWT 认证、路由转发、熔断 | 无状态 | REST (8000) | gRPC Client |
| User Service | 注册/登录/用户信息 | PostgreSQL | REST (8001) + gRPC (50051) | gRPC Server |
| Product Service | 商品 CRUD/分类/库存查询 | PostgreSQL | REST (8002) + gRPC (50051) | gRPC Server |
| Order Service | 订单创建/状态管理/编排 | PostgreSQL | REST (8003) + gRPC (50051) | gRPC Client + Kafka Producer |
| Payment Service | 支付发起/回调处理/退款 | PostgreSQL | REST (8004) + gRPC (50051) | gRPC Server |
| Notification Service | 邮件/短信/推送 | 无(纯消费者) | 无 HTTP | Kafka Consumer |
三、阶段一:拆分规划------让 Claude Code 读代码、画边界
3.1 核心策略
拆分最怕"盲拆"------按照目录结构或直觉切,拆完才发现 A 服务和 B 服务有 15 个隐式依赖。Claude Code 在这个阶段的价值不是写代码,而是:逐文件分析依赖、识别循环引用、给出基于数据的拆分优先级。
在项目根目录启动 Claude Code 交互模式:
bash
cd shopfast && claude
3.2 第一步:全局依赖分析
第一条 prompt(Plan 模式,不修改文件):
分析整个 shopfast 代码库的依赖关系。不要修改任何文件。
任务:
1. 列出 app/ 目录下每个 Python 文件及其 import 的所有内部模块
2. 识别所有循环依赖(A→B→C→A)
3. 识别所有跨模块的数据库表直接访问(一个 Service 直接查另一个模块的表)
4. 将分析结果写入 docs/DEPENDENCY_GRAPH.md
只用 Read 和 Grep 工具。
Claude Code 读取了 47 个模型文件、23 个路由文件、18 个 Service 文件后,产出了一份依赖分析报告。关键发现:
- 3 个循环依赖:order↔user↔notification、product↔inventory↔order、payment↔order↔user
- 7 处跨模块直接查表 :OrderService 直接
SELECT * FROM products、PaymentService 直接SELECT * FROM users - 11 个模块之间共享同一个
utils/目录,没有任何边界
3.3 第二步:限界上下文识别
基于依赖分析报告,第二条 prompt:
基于 docs/DEPENDENCY_GRAPH.md,用 DDD 方法识别限界上下文。
对每个识别的上下文,列出:
1. 上下文名称(用业务语言,不是技术语言)
2. 属于该上下文的文件列表
3. 该上下文对外暴露的公共接口
4. 该上下文独占的数据库表
5. 与其他上下文的依赖关系(谁是上游、谁是下游)
按照"耦合度从低到高"排序,推荐前 3 个最安全优先提取的上下文。
输出写入 docs/BOUNDED_CONTEXTS.md。
Claude Code 给出的分析结果:
| 上下文 | 耦合度 | 独占表 | 依赖方向 | 建议 |
|---|---|---|---|---|
| Notification | 最低 | notifications, notification_preferences | 仅被其他模块调用,不主动依赖 | 第一优先 |
| Product | 低 | products, categories, inventories | 被 Order 依赖,自身依赖少 | 第二优先 |
| User | 中 | users, user_profiles, roles | 被几乎所有模块依赖 | 第三 |
| Payment | 中 | payments, refunds, payment_methods | 依赖 User + Order | 第四 |
| Order | 最高 | orders, order_items, shipments | 依赖 User + Product + Payment | 最后 |
3.4 第三步:确认拆分方案
将 docs/BOUNDED_CONTEXTS.md 作为人类和 AI 之间的"合同"。确认后保存:
bash
claude -p "将 docs/BOUNDED_CONTEXTS.md 整理为正式的拆分方案 MIGRATION_PLAN.md,
包含:最终服务列表、每个服务的数据库、对外 API、迁移顺序(从耦合最低到最高)、
每一步的验收标准" --allowedTools "Read,Write"
3.5 阶段一数据
| 指标 | 数值 |
|---|---|
| Token 消耗 | ~28K |
| DeepSeek V4-Pro 费用 | $0.05 |
| 耗时 | 45 min |
| Claude 读取的文件数 | 88 个 |
| 输出文档 | DEPENDENCY_GRAPH.md + BOUNDED_CONTEXTS.md + MIGRATION_PLAN.md |
关键经验 :这个阶段一分钱都不应该省。多花 0.05 在分析上,避免 5.00 花在"拆完后发现边界错了推倒重来"上。Claude Code 在这个阶段是"架构顾问"------用交互模式做推演,不要用
-p。
四、阶段二:逐个微服务实现
4.1 开发策略:小步增量 + 绞杀者模式
不搞"大爆炸重写"------按照 MIGRATION_PLAN.md 的顺序,逐个提取服务:
通知服务(最简单) → 商品服务 → 用户服务 → 支付服务 → 订单服务(最复杂)
→ API 网关(并行)
每个服务遵循相同的 6 步循环:
proto 定义 → 代码生成 → 服务实现 → 单体中创建 Client → 切换流量 → 删除旧代码
第一个服务最重要------它验证整个流程(proto→代码→Docker→K8s→监控→回滚)的可行性。
4.2 Step 1:Proto 定义
为每个服务定义 gRPC 契约。以订单服务为例:
bash
mkdir -p proto/order/v1
claude
在 proto/order/v1/order.proto 中定义 Order 服务的 gRPC 契约:
需要:
- CreateOrder RPC:接收 user_id + items[] + shipping_address,返回 order_id + status
- GetOrder RPC:接收 order_id,返回完整订单信息
- ListOrders RPC:接收 user_id + page_token,server-side streaming 返回订单列表
消息中包含 common/v1/common.proto 的 Money 和 Address 类型。
先创建 common.proto,再创建 order.proto。
只写 proto 文件,不要生成代码。
Claude Code 生成的 proto/order/v1/order.proto:
protobuf
syntax = "proto3";
package order.v1;
import "common/v1/common.proto";
message OrderItem {
string product_id = 1;
string product_name = 2;
int32 quantity = 3;
common.v1.Money unit_price = 4;
}
message CreateOrderRequest {
string user_id = 1;
repeated OrderItem items = 2;
common.v1.Address shipping_address = 3;
}
message CreateOrderResponse {
string order_id = 1;
string status = 2;
common.v1.Money total_amount = 3;
}
message GetOrderRequest { string order_id = 1; }
message GetOrderResponse {
string order_id = 1;
string user_id = 2;
repeated OrderItem items = 3;
string status = 4;
string created_at = 5;
}
service OrderService {
rpc CreateOrder(CreateOrderRequest) returns (CreateOrderResponse);
rpc GetOrder(GetOrderRequest) returns (GetOrderResponse);
}
Claude Code 自动做对的事 :写 order.proto 时先读了 common.proto,确认 Money 和 Address 的字段名一致后才引用------避免了后续编译时的类型不匹配错误。
4.3 Step 2:服务实现(以订单服务为例)
生成 proto 代码后,实现服务:
基于 proto/order/v1/order.proto,实现 Order Service。
项目结构:
services/order-service/
├── Dockerfile
├── requirements.txt
├── app/
│ ├── main.py # FastAPI + gRPC Server 同进程
│ ├── core/config.py # Pydantic Settings
│ ├── core/database.py # async SQLAlchemy engine
│ ├── models/order.py # Order 模型
│ ├── routers/orders.py # REST 端点
│ ├── services/order_service.py # 业务逻辑
│ └── grpc/order_servicer.py # gRPC 实现
└── alembic/
要求:
1. FastAPI 和 gRPC 在同一个 lifespan 中启动
2. gRPC Server 监听 50051,HTTP 监听 8003
3. CreateOrder 实现:先调 User Service 验证用户 → 调 Product Service 验证商品
→ 本地写订单 → 调 Payment Service 发起支付 → Kafka 发布 OrderCreated 事件
4. 所有 gRPC 调用设置 3 秒超时
5. 数据库用 async SQLAlchemy + asyncpg,expire_on_commit=False
Claude Code 生成的核心结构(main.py):
python
import grpc
from concurrent import futures
from contextlib import asynccontextmanager
from fastapi import FastAPI
grpc_server = grpc.aio.server(futures.ThreadPoolExecutor(max_workers=10))
@asynccontextmanager
async def lifespan(app: FastAPI):
order_pb2_grpc.add_OrderServiceServicer_to_server(OrderServicer(), grpc_server)
grpc_server.add_insecure_port("[::]:50051")
await grpc_server.start()
yield
await grpc_server.stop(grace=5)
app = FastAPI(title="Order Service", lifespan=lifespan)
app.include_router(orders.router, prefix="/api/v1/orders")
4.4 Step 3:单体内创建 Client + Feature Flag 切换
在单体中创建 gRPC Client(shopfast/app/clients/order_client.py):
1. 使用 grpc.aio.insecure_channel
2. 封装重试(tenacity,最多 3 次,指数退避)
3. 封装断路器(连续 5 次失败 → 断路 30 秒 → 半开试探)
4. 在单体路由中加 Feature Flag(环境变量 USE_NEW_ORDER_SERVICE=true/false)
4.5 逐个服务实现数据
| 服务 | Token | 费用 | 耗时 | 生成代码 | 人工修改比例 |
|---|---|---|---|---|---|
| Notification Service | ~8K | $0.014 | 20min | ~180 行 | 5% |
| Product Service | ~15K | $0.027 | 35min | ~350 行 | 8% |
| User Service | ~12K | $0.021 | 30min | ~280 行 | 10% |
| Payment Service | ~18K | $0.032 | 40min | ~400 行 | 12% |
| Order Service(最复杂) | ~25K | $0.044 | 55min | ~520 行 | 15% |
| API Gateway | ~12K | $0.021 | 25min | ~250 行 | 7% |
| 阶段二合计 | ~90K | $0.16 | ~3.5h | ~1980 行 | ~10% |
10% 人工修改比例意味着 Claude Code 生成的代码 90% 直接可用。需要人工修改的主要是:具体业务规则的边界条件(如优惠券叠加逻辑)、跨服务调用时的错误处理策略选择、以及数据库迁移脚本的外键处理。
五、阶段三:基础设施即代码------Docker + Kubernetes
5.1 Docker 多阶段构建
Claude Code 为每个微服务生成生产级 Dockerfile:
为 services/order-service/ 写生产级 Dockerfile:
1. 多阶段构建(builder + runtime)
2. builder 用 uv 安装依赖到 /install
3. runtime 用 python:3.12-slim,非 root 用户
4. HEALTHCHECK 指令
5. 最终镜像 < 200MB
Claude Code 生成的 Dockerfile 关键部分:
dockerfile
# Stage 1: Build
FROM python:3.12-slim AS builder
COPY --from=ghcr.io/astral-sh/uv:latest /uv /bin/uv
COPY requirements.txt .
RUN uv pip install --system --prefix=/install --no-cache-dir -r requirements.txt
# Stage 2: Runtime
FROM python:3.12-slim AS runtime
COPY --from=builder /install /usr/local
COPY app/ ./app/
COPY generated/ ./generated/
RUN groupadd --gid 1000 app && useradd --uid 1000 --gid app --shell /bin/bash --create-home app
USER app
ENV PYTHONUNBUFFERED=1 PYTHONDONTWRITEBYTECODE=1
EXPOSE 8003 50051
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
CMD python -c "import urllib.request; urllib.request.urlopen('http://127.0.0.1:8003/health')" || exit 1
CMD ["gunicorn", "app.main:app", "-k", "uvicorn.workers.UvicornWorker", "-w", "4", "-b", "0.0.0.0:8003"]
Claude Code 做对的关键细节 :HEALTHCHECK 用 Python 内置 urllib 而非 curl,避免在 runtime 镜像中安装额外系统包。非 root 用户 + PYTHONUNBUFFERED=1 自动设置。
5.2 Kubernetes YAML 生成
这是 Claude Code 在基础设施领域最亮眼的表现。一个单服务 Deployment 手写通常 2-3 小时(查文档 → 确认 API 版本 → 验证字段名),Claude Code 不到 15 分钟完成。
为 Order Service 生成完整的 Kubernetes 部署清单:
1. Namespace: ecommerce
2. ConfigMap(非敏感配置)+ Secret(数据库密码、JWT密钥)
3. Deployment:
- 3 副本,RollingUpdate(maxUnavailable: 0)
- CPU 250m/500m,Memory 256Mi/512Mi
- livenessProbe: GET /health,initialDelaySeconds=30
- readinessProbe: GET /health/ready,initialDelaySeconds=5
- startupProbe: GET /health,failureThreshold=30(给慢启动留 300 秒)
- 非 root 用户,terminationGracePeriodSeconds=30
4. Service:暴露 HTTP 8003 + gRPC 50051 双端口
5. Ingress:REST 流量 + gRPC 流量分开配置
6. HPA:CPU 70% 触发扩缩,min 2 / max 10,缩容稳定窗口 300 秒
全部 YAML 写入 k8s/order-service/ 目录。
Claude Code 生成的 Deployment 核心片段:
yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: order-service
namespace: ecommerce
spec:
replicas: 3
strategy:
type: RollingUpdate
rollingUpdate: { maxSurge: 1, maxUnavailable: 0 }
selector:
matchLabels: { app: order-service }
template:
metadata:
labels: { app: order-service, version: v1 }
annotations:
prometheus.io/scrape: "true"
prometheus.io/port: "8003"
spec:
terminationGracePeriodSeconds: 30
securityContext: { runAsNonRoot: true, runAsUser: 1000 }
containers:
- name: order-service
image: registry.example.com/ecommerce/order-service:v1.0.0
ports:
- { name: http, containerPort: 8003 }
- { name: grpc, containerPort: 50051 }
envFrom:
- configMapRef: { name: order-service-config }
env:
- name: DB_PASSWORD
valueFrom: { secretKeyRef: { name: order-service-secret, key: DB_PASSWORD } }
resources:
requests: { cpu: "250m", memory: "256Mi" }
limits: { cpu: "500m", memory: "512Mi" }
startupProbe:
httpGet: { path: /health, port: http }
failureThreshold: 30
periodSeconds: 10
livenessProbe:
httpGet: { path: /health, port: http }
initialDelaySeconds: 30
periodSeconds: 10
readinessProbe:
httpGet: { path: /health/ready, port: http }
initialDelaySeconds: 5
periodSeconds: 5
三个 Claude Code 自动做对的安全细节:
maxUnavailable: 0------滚动更新时零停机startupProbe+failureThreshold: 30------给慢启动应用 300 秒初始化时间,这是 AI 生成 K8s YAML 最容易漏的配置- 分离
livenessProbe和readinessProbe------前者不检查外部依赖,后者检查数据库连接
5.3 阶段三数据
| 子阶段 | Token | 费用 | 传统手写 | AI 辅助 | 效率提升 |
|---|---|---|---|---|---|
| 6 个 Dockerfile | ~15K | $0.027 | 3-4h | 20min | ~12x |
| 6 套 K8s YAML | ~35K | $0.062 | 12-18h | 50min | ~15x |
| docker-compose 本地 | ~8K | $0.014 | 1-2h | 10min | ~9x |
| CI/CD (GitHub Actions) | ~10K | $0.018 | 2-3h | 15min | ~10x |
| 阶段三合计 | ~68K | $0.12 | 18-27h | ~1.5h | ~15x |
基础设施即代码是 Claude Code 效率提升最大的领域 ------传统 18-27 小时压缩到 1.5 小时,约 15 倍加速。
六、阶段四:可观测性------分布式追踪 + 指标 + 日志
6.1 Claude Code 生成 OpenTelemetry 集成
分布式系统最大的挑战不是"怎么拆",而是"拆完后怎么知道哪个服务出了问题"。Claude Code 可以为每个服务自动添加 OpenTelemetry 插桩:
在 services/order-service/app/core/telemetry.py 中创建 OpenTelemetry 初始化模块。
要求:
1. 自动插桩:FastAPI + gRPC Server + SQLAlchemy + httpx + aiokafka
2. Traces 发送到 Jaeger (jaeger-collector:4317)
3. 业务指标:订单创建计数器、订单金额 Histogram
4. 排除 /health 和 /metrics 端点的追踪
5. 所有 Span 自动注入 service.name 和 deployment.environment
Claude Code 生成的核心代码:
python
# services/order-service/app/core/telemetry.py
from opentelemetry import trace, metrics
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor
from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter
from opentelemetry.sdk.resources import Resource
from opentelemetry.instrumentation.fastapi import FastAPIInstrumentor
from opentelemetry.instrumentation.grpc import GrpcAioInstrumentorServer
from opentelemetry.instrumentation.sqlalchemy import SQLAlchemyInstrumentor
def setup_telemetry(app, service_name: str = "order-service"):
resource = Resource.create({
"service.name": service_name,
"deployment.environment": os.getenv("ENVIRONMENT", "production"),
})
tracer_provider = TracerProvider(resource=resource)
tracer_provider.add_span_processor(BatchSpanProcessor(
OTLPSpanExporter(endpoint=os.getenv("JAEGER_ENDPOINT", "http://jaeger:4317"), insecure=True)
))
trace.set_tracer_provider(tracer_provider)
FastAPIInstrumentor.instrument_app(app, excluded_urls="/health,/metrics")
GrpcAioInstrumentorServer().instrument()
SQLAlchemyInstrumentor().instrument()
# 业务指标
meter = metrics.get_meter(__name__)
order_counter = meter.create_counter("orders_created_total", "Total orders")
order_amount = meter.create_histogram("order_amount", "Order amount", unit="CNY")
6.2 阶段四数据
| 子阶段 | Token | 费用 | 耗时 |
|---|---|---|---|
| 6 个服务的 OTel 集成 | ~18K | $0.032 | 25min |
| Jaeger + Prometheus + Grafana K8s YAML | ~10K | $0.018 | 15min |
| Grafana Dashboard JSON | ~8K | $0.014 | 10min |
| 阶段四合计 | ~36K | $0.064 | ~50min |
七、成本全记录
7.1 各阶段汇总
| 阶段 | Token | DeepSeek V4-Pro | 耗时 | 传统估算 | 效率 |
|---|---|---|---|---|---|
| 拆分规划 | ~28K | $0.05 | 45min | 2-3 天 | --- |
| 6 个微服务实现 | ~90K | $0.16 | 3.5h | 12-16h | ~4x |
| Docker + K8s + CI/CD | ~68K | $0.12 | 1.5h | 18-27h | ~15x |
| 可观测性集成 | ~36K | $0.064 | 50min | 6-8h | ~8x |
| Debug + 迭代 | ~30K | $0.053 | 1h | --- | --- |
| 全流程合计 | ~252K | $0.45 | ~7.5h | ~38-54h | ~5-7x |
7.2 成本对比:DeepSeek V4-Pro vs Claude Opus 4.7
| 阶段 | DeepSeek V4-Pro | Claude Opus 4.7(估算) | 价差 |
|---|---|---|---|
| 拆分规划 | $0.05 | $0.23 | ~4.5x |
| 服务实现 | $0.16 | $0.72 | ~4.5x |
| IaC | $0.12 | $0.54 | ~4.5x |
| 可观测性 | $0.064 | $0.29 | ~4.5x |
| 合计 | $0.45 | $2.03 | ~4.5x |
Claude Opus 4.7 估算基于 Anthropic 官方定价(输入 5/M,输出 25/M token,2026-05)。IaC 阶段的差距最大------生成大量 YAML(高输出 token 占比),Opus 输出 25/M vs DeepSeek 3.48/M,差 ~7x。
八、复盘:Claude Code 在微服务拆分中的优劣
8.1 超预期的环节
| 环节 | 表现 | 原因 |
|---|---|---|
| 依赖分析 | 一次分析 88 个文件、3 个循环依赖、7 处跨模块查表 | Glob + Grep + Read 组合快速构建代码地图 |
| Proto 生成 | 自动检查 common.proto 的类型一致性 | Claude Code 生成前会读取已存在的相关文件 |
| K8s YAML | 自动加 startupProbe、非 root、零停机滚动更新 | 训练数据中大量生产级模板 |
| Docker 多阶段 | HEALTHCHECK + 非 root + uv 加速安装 | Docker 最佳实践在训练数据中密度极高 |
| 跨服务一致性 | 6 个服务的 YAML 结构、端口约定、标签命名完全一致 | Claude Code 记住了之前生成的模板 |
8.2 需要人工介入的环节
| 环节 | 问题 | 解决方式 |
|---|---|---|
| 数据库外键拆分 | Claude 建议直接删外键,没考虑过渡期双写 | 人工要求加 CDC 中间步骤 |
| 分布式事务 | 初版代码假设跨服务调用是原子的 | 人工引入 Saga + Outbox 模式 |
| gRPC 超时 | 默认不设超时,可能导致级联挂起 | 人工要求所有 gRPC 调用设 3s 超时 |
| Kafka 分区 | 按随机分区,同用户订单可能乱序 | 人工改为按 user_id 分区 |
| Grafana Dashboard | 生成的 JSON 缺少 P99 延迟面板 | 人工补充关键指标 |
8.3 核心经验
-
CLAUDE.md 分层架构对多服务项目至关重要。每个服务子目录放 30-50 行 CLAUDE.md(只描述该服务特有规则),根目录 CLAUDE.md(~100 行)只放跨服务共享规则。
-
Proto 文件是"单一真实来源"。先定义 proto → 生成代码 → 实现服务。Claude Code 在不同会话间不会记住你的 API 契约------但 proto 文件会。
-
小步增量 + Feature Flag。每个服务提取后,用环境变量切换新旧实现。出问题一个环境变量回滚------比 git revert + 重新部署快 100 倍。
-
第一个提取的服务是"楔子"------选最独立、最低风险的。本项目中是通知服务,它验证了整个流程(proto→实现→Docker→K8s→监控→回滚)的可行性。
-
"模块化单体"是合理的终态。不是所有服务都必须提取。如果某个模块不符合独立扩缩容、独立部署、独立团队的条件------留在单体中是对的。
九、Debug ×5(全项目实际踩坑)
Debug #1 --- gRPC UNIMPLEMENTED: unknown service
报错:
rpc error: code = Unimplemented desc = unknown service order.v1.OrderService
根因 :Client 端 proto 的 package 名是 order.v1,但 Server 端注册时写的是 pb.OrderService------不同 proto 文件的 package 声明不一致。
| 场景 | Proto package | 全限定服务名 | 结果 |
|---|---|---|---|
| Client | package order.v1; |
order.v1.OrderService |
期望 |
| Server A | package order.v1; |
order.v1.OrderService |
匹配 |
| Server B | package pb; |
pb.OrderService |
Unimplemented |
修复 :统一所有 proto 文件的 package 声明格式为 {service}.v1。
验证 :grpcurl -plaintext localhost:50051 list 确认所有服务名一致。
Debug #2 --- CrashLoopBackOff:Liveness Probe 过早
报错:
Warning Unhealthy Liveness probe failed: Get "http://10.244.1.5:8003/health": context deadline exceeded
Warning BackOff Back-off restarting failed container
根因 :Order Service 启动需 ~45 秒(加载 gRPC + 连接 DB + 初始化 Kafka),但 livenessProbe.initialDelaySeconds 只有 10 秒------应用还没起来就被判定为"死了"。
| 配置 | 行为 |
|---|---|
initialDelaySeconds: 10,无 startupProbe |
10 秒后 liveness 开始检查 → kill |
startupProbe + failureThreshold: 30 |
最长 300 秒初始化,liveness 在 startup 成功后接管 |
修复 :加 startupProbe,failureThreshold: 30(30 × 10s = 300s)。
验证 :kubectl describe pod 确认不再出现 Liveness probe failed。
Debug #3 --- 数据库外键拆分:关联查询全部报错
症状 :将 Order 表迁移到独立 DB 后,SELECT * FROM orders JOIN users ON orders.user_id = users.id 全部报 relation "users" does not exist。
根因:Claude Code 直接删除了跨库外键,但没处理依赖 JOIN 查询的 12 个 Service 方法。
| 外键处理策略 | 本场景适用性 |
|---|---|
| 直接删除外键 | ❌(有 12 处 JOIN) |
| 数据冗余(Denormalization) | ✅ 用户名/邮箱冗余到 Order 表 |
| CDC 事件同步 | ✅ 用户信息变更通过 Kafka 同步 |
修复 :两步------① Order 表冗余 user_name + user_email,通过事件填充;② 订阅 user.updated Kafka 事件同步更新。
验证 :user.updated 事件后冗余字段 2 秒内同步。
Debug #4 --- 分布式事务:支付成功但订单状态未更新
症状 :支付回调成功,Payment 表已更新,但调用 OrderService.UpdateOrderStatus(PAID) 时网络超时------支付成功但订单卡在 PENDING。
根因:Claude Code 的初版代码假设网络调用总是成功------Payment 状态更新和通知下游不在同一事务中。
修复 :引入 Transactional Outbox 模式------同一 DB 事务中更新 Payment 状态 + 写入 outbox 事件。后台 worker 从 outbox 投递到 Kafka,Order Service 消费后更新订单状态。保证 at-least-once。
python
async def handle_payment_callback(payment_id: str, status: str):
async with db.begin():
payment = await db.get(Payment, payment_id)
payment.status = status
db.add(OutboxEvent(aggregate_id=payment.order_id,
event_type="PaymentCompleted",
payload={"order_id": payment.order_id, "status": status}))
验证:模拟网络超时------Payment 状态正确、30 秒后 Kafka 重试成功、Order 最终一致。
Debug #5 --- Jaeger 中 gRPC 调用链断裂
症状:Jaeger 能看到 Order Service 的 Span,但看不到它调用的 User Service Span------调用链在 gRPC 边界断开。
根因 :gRPC 客户端未插桩 GrpcAioInstrumentorClient,trace context 不通过 gRPC metadata 传播。
| 场景 | 结果 |
|---|---|
| FastAPI → gRPC Client(未插桩) | Span 在 gRPC 边界断开 |
| FastAPI → gRPC Client(已插桩) | Span 通过 gRPC metadata 传播 |
| gRPC Server(未插桩) | 接收到的 trace context 被忽略 |
| gRPC Server(已插桩) | 正确创建子 Span |
修复 :在 Order Service 添加 GrpcAioInstrumentorClient().instrument(),在各服务端确认 GrpcAioInstrumentorServer().instrument()。
验证:Jaeger 中完整调用链:API Gateway → Order Service → User Service → PostgreSQL。
十、速查卡
Claude Code 拆分 Prompt 模板
| 阶段 | 模板 |
|---|---|
| 依赖分析 | "分析代码库依赖关系。识别循环引用和跨模块查表。输出 DEPENDENCY_GRAPH.md。只用 Read/Grep。" |
| 边界识别 | "基于依赖图,用 DDD 识别限界上下文。按耦合度排序,推荐最安全优先提取的 3 个。" |
| 服务提取 | "将 {context} 提取为独立微服务。proto → 实现 → Client → Feature Flag。单体在测试通过后才能提交。" |
| K8s 生成 | "为 {service} 生成完整 K8s 清单。含 startupProbe、非 root、零停机滚动更新、HPA。" |
成本控制参数
| 场景 | max_turns | max_budget_usd | 推荐模型 |
|---|---|---|---|
| 依赖分析(只读) | 10 | 0.10 | V4-Pro |
| 服务实现(写代码) | 15 | 0.50 | V4-Pro |
| K8s 生成(大量输出) | 8 | 0.20 | V4-Pro |
| 调试修复 | 8 | 0.15 | V4-Pro |
报错速查
| 报错 | 根因 | 解决 |
|---|---|---|
UNIMPLEMENTED unknown service |
Proto package 名不一致 | 统一 package {service}.v1; |
| CrashLoopBackOff | Liveness probe 过早 | 加 startupProbe + failureThreshold: 30 |
relation does not exist |
跨库外键直接删除 | 数据冗余 + CDC 事件同步 |
| 支付成功订单卡 PENDING | 网络超时导致不一致 | Outbox Pattern + Kafka 重试 |
| Jaeger 调用链断裂 | gRPC 未传播 trace context | 添加 GrpcAioInstrumentorClient 插桩 |
扩展阅读
本系列相关文章:
- 高手进阶(五):子代理与并行开发 --- 子代理在微服务拆分中可并行实现多个服务
- 高手进阶(六):Headless 模式与 CI/CD 集成 --- 每个微服务的 CI/CD 流水线模板
- 高手进阶(八):综合实战------用 Claude Code 交付一个完整全栈项目 --- 单体全栈实战,本篇的"拆分前"对照
- 番外篇:Claude Code 实战拆解------8 天单人全栈交付视频翻译工具 --- 真实项目中的架构演进与 Bug 修复实录
参考文献
- AWS Prescriptive Guidance: Strangler Fig Pattern --- AWS 官方单体迁移指南
- How I use Claude Code to refactor a monolith into microservices --- Claude Code 拆分单体完整工作流
- Django 单体到微服务迁移案例 --- 12 万行 14 天拆分实录
- Claude Code Multi-File Refactor Protocol --- 大规模代码重组安全协议
- FastAPI + gRPC + K8s 生产级指南 --- FastAPI 微服务生产模式
- OpenTelemetry Python 官方文档 --- OTel 自动插桩指南
- Kubernetes CrashLoopBackOff 调试指南 --- K8s 官方 Pod 调试文档
- Microservices.io: Decompose by Subdomain --- DDD 子域分解模式