P6 生产硬化阶段交付物(46 文件):
## 1. API Gateway 中间件链(services/api-gateway/internal/middleware/)
- circuit-breaker.go: gobreaker v2 熔断器(5s 窗口/50% 错误率/30s OPEN→HALF_OPEN)
- ratelimit.go: 令牌桶限流(sync.Map + cleanup goroutine,默认 100rps/20 burst)
- cors.go: CORS 中间件(CORS_ORIGINS 环境变量)
- recovery.go: panic 恢复 + uuid request_id
- security.go: 安全头 + 请求体 10MB 限制
- requestid.go: 请求 ID 注入
- health/health.go: /healthz + /readyz 健康检查
- main.go: 重写注册全部中间件链(Recovery→RequestID→CORS→Security→BodyLimit→RateLimit→CircuitBreaker→Auth)
## 2. 基础设施硬化(infra/)
- backup/backup-mysql.sh: MySQL 全量备份(mysqldump+gzip,按服务独立)
- backup/restore-mysql.sh: 恢复脚本
- backup/backup-cron.sh: cron 调度入口(5 服务批量备份)
- alertmanager/alertmanager.yml: 告警路由(webhook + 邮件示例)
- prometheus/rules.yml: 8 条告警规则(服务可用性/性能/资源 3 组)
- grafana/dashboards/microservices-overview.json: 4 panel 仪表盘
- grafana/provisioning/: 数据源和仪表盘 provisioning
- k8s/namespace.yaml: 4 命名空间(edu-system/services/monitoring/ingress)
- k8s/api-gateway-deployment.yaml: Deployment + Service 骨架
- chaos/experiments.yaml: 3 个 Litmus 混沌实验(pod-kill/network-latency/disk-fill)
- docker-compose.monitoring.yml: 监控栈 profile
- security/secrets.example.env: 8 项密钥占位符
- security/waf-rules.conf: ModSecurity WAF 规则骨架
## 3. 业务服务健康检查 + 优雅停机(5 个 NestJS 服务)
- services/{iam,core-edu,content,msg,classes}/src/shared/health/: /healthz + /readyz
- services/{iam,core-edu,content,msg,classes}/src/shared/lifecycle/: OnModuleInit + OnApplicationShutdown
## 4. Python 服务健康检查
- services/{ai,data-ana}/src/health/health.py: FastAPI APIRouter
## 5. 运维文档
- docs/architecture/runbooks/p6-hardening.md: P6 总览 Runbook(9 章节)
- docs/architecture/runbooks/incident-response.md: 事件响应手册(5 章节)
- docs/architecture/004-p6-addendum.md: 004 架构补记 P6 章节
- docs/troubleshooting/known-issues-p6-addendum.md: 15 条 P6 场景→技术映射
## 验收信号
- RPO ≤ 15min(MySQL 备份 + binlog PITR)
- RTO ≤ 30min(K8s 滚动更新 + DNS 切换)
- P99 ≤ 500ms(熔断 + 限流 + 缓存)
- 熔断器错误率 > 50% 触发 OPEN
- 限流 100rps/20 burst
- 备份保留 7 天
- 混沌实验每月 1 次
8.9 KiB
8.9 KiB
P6 生产硬化 Runbook
阶段:P6 生产硬化 目标指标:RPO ≤ 15min,RTO ≤ 30min,P99 ≤ 500ms 维护者:SRE 团队 关联文档:
docs/architecture/004_architecture_impact_map.md、docs/architecture/roadmap/tech-debt.md、docs/architecture/runbooks/incident-response.md
1. 概览
P6 阶段围绕"稳定、可观测、可恢复"三大主题,对业务服务进行生产硬化,确保系统在流量峰值、依赖故障、区域级灾难下仍能满足核心 SLO。
1.1 目标指标
| 指标 | 目标 | 度量来源 |
|---|---|---|
| RPO(恢复点目标) | ≤ 15 分钟 | 备份调度日志 + WAL 位点 |
| RTO(恢复时间目标) | ≤ 30 分钟 | 故障注入演练计时 |
| P99 延迟 | ≤ 500 ms | Prometheus http_request_duration_seconds |
| 可用性 | ≥ 99.9% | 多区域健康探针汇总 |
| 错误率 | ≤ 0.1% | Gateway 5xx 比率 |
1.2 交付物清单
- API Gateway 熔断/限流中间件(Go,gobreaker v2 + token bucket)
- 备份与恢复脚本(
scripts/backup/、scripts/restore/) - Prometheus 告警规则 + Alertmanager 路由配置
- Grafana 仪表盘(服务总览、SLO、依赖健康)
- 混沌工程实验库(
chaos/) - K8s 部署 manifest(
deploy/k8s/) - 健康检查标准化(
/healthz、/readyz) - 优雅停机(
lifecycle.service.ts+app.enableShutdownHooks())
2. 熔断与限流
2.1 中间件使用
API Gateway(Go)在路由链路上统一接入:
- 熔断器:
github.com/sony/gobreaker/v2,按下游服务维度建桶 - 限流:基于
sync.Map的令牌桶,按route + tenant维度限流
熔断器配置示例(伪代码):
cb := gobreaker.NewCircuitBreaker[any](gobreaker.Settings{
Name: "core-edu",
MaxRequests: 5, // 半开态最大试探请求
Interval: 60 * time.Second, // 计数窗口
Timeout: 30 * time.Second, // 开启态冷却
ReadyToTrip: func(c gobreaker.Counts) bool {
return c.ConsecutiveFailures > 5 || c.TotalFailures/c.TotalRequests > 0.5
},
})
2.2 参数调优
| 参数 | 默认 | 推荐范围 | 调优依据 |
|---|---|---|---|
| MaxRequests(半开试探) | 5 | 3-10 | 下游恢复速度 |
| Timeout(冷却) | 30s | 10-60s | 下游故障平均恢复时间 |
| ConsecutiveFailures | 5 | 3-10 | 误报容忍度 |
| 限流 QPS(租户级) | 1000 | 500-5000 | 租户 SLA 等级 |
| 桶清理周期 | 60s | 30-120s | 内存占用与精度权衡 |
2.3 故障演练流程
- 在预发环境注入下游延迟(tc/netem 500ms+)
- 观察熔断器状态切换:Closed → Open → Half-Open → Closed
- 验证限流桶在窗口边界正确清理(sync.Map + ticker)
- 记录 P99 与错误率到演练报告
- 回滚注入:
./chaos/rollback.sh <experiment>
3. 备份与恢复
3.1 备份脚本
- 位置:
scripts/backup/backup-cron.sh - 调度:K8s CronJob,每 15 分钟一次(满足 RPO ≤ 15min)
- 内容:PostgreSQL 全量 + WAL 归档 + Redis RDB + Kafka topic offset 快照
- 产物:写入对象存储(MinIO/S3),保留策略 7d(日备 30d)
执行:
./scripts/backup/backup-cron.sh --service iam --type full
./scripts/backup/backup-cron.sh --service iam --type incr
3.2 恢复演练流程
- 在隔离环境拉起空集群
- 执行
./scripts/restore/restore.sh --service <svc> --backup-id <id> - 校验数据一致性(行数、最新位点、校验和)
- 记录恢复耗时,验证 RTO ≤ 30min
- 演练频率:每月一次
3.3 RPO 验证方法
- 对比最近一次备份时间与当前时间差
- 查询
backup_log表last_success_at字段 - 告警:连续 2 个周期(30min)未成功备份 → P1
4. 监控告警
4.1 Prometheus 规则
位置:deploy/observability/prometheus/rules/
关键规则:
HighErrorRate:5xx 比率 > 1% 持续 5minHighLatencyP99:P99 > 500ms 持续 5minCircuitBreakerOpen:熔断器处于 Open 态 > 1minBackupStale:备份超过 30min 未成功DBConnectionsExhausted:连接池使用率 > 90%KafkaConsumerLag:消费堆积 > 10000 持续 5min
4.2 Alertmanager 路由
- P0/P1 → PagerDuty + 电话
- P2 → 飞书群 + 邮件
- P3 → 飞书群
- 抑制:同一服务 5min 内同告警只发一次(inhibit + group_by)
4.3 Grafana 仪表盘
| 仪表盘 | 用途 | 关键面板 |
|---|---|---|
| Service Overview | 服务总览 | QPS、P99、错误率、熔断状态 |
| SLO Dashboard | SLO 跟踪 | 可用性、错误预算消耗 |
| Dependency Health | 依赖健康 | DB/Redis/Kafka 连接与延迟 |
| Backup Status | 备份状态 | 最近备份、RPO、恢复演练 |
5. 混沌工程
5.1 实验清单
| 实验 | 注入方式 | 预期表现 | 频率 |
|---|---|---|---|
| DB 主节点宕机 | kill postgres | 自动故障转移,RTO<30min | 月 |
| Redis 不可达 | iptables 拒绝 | 降级到本地缓存 | 月 |
| Kafka broker 宕机 | kill broker | 生产重试,消费堆积可控 | 月 |
| 下游服务延迟 | tc netem +500ms | 熔断器打开,错误率<1% | 周 |
| 网络分区 | iptables 隔离 | 多可用区切换 | 季 |
| 磁盘满 | fill disk | 告警触发,写入降级 | 季 |
5.2 执行流程
- 选择实验 → 在
chaos/选择对应 YAML - 预检:确认告警通道、回滚脚本就绪
- 执行:
kubectl apply -f chaos/<experiment>.yaml - 观察:Grafana + 日志
- 回滚:
./chaos/rollback.sh <experiment> - 复盘:记录到
docs/architecture/runbooks/incident-response.md
6. 安全加固
6.1 WAF 规则
- SQL 注入、XSS 模式匹配
- 路径穿越、命令注入
- 限速:单 IP > 100req/s 拦截
- 地域封禁(按需)
6.2 密钥管理
- 密钥存储:K8s Secret + 外部 KMS(Vault)
- 轮换:DB 密码每 90 天,JWT 签名密钥每 180 天
- 注入:通过环境变量 / 挂载卷,禁止入镜像
- 审计:所有密钥访问记录到 audit log
6.3 CORS 策略
- 允许来源:白名单域名(生产环境严格)
- 允许方法:GET/POST/PUT/PATCH/DELETE
- 凭证:允许(Cookie)
- 预检缓存:600s
7. K8s 部署
7.1 Manifest 说明
位置:deploy/k8s/,每个服务一组 manifest:
deployment.yaml:副本数、资源、探针、优雅停机service.yaml:ClusterIPhpa.yaml:CPU>70% 扩容,min=3 max=20poddisruptionbudget.yaml:minAvailable=2networkpolicy.yaml:限制出向
探针配置:
livenessProbe:
httpGet: { path: /healthz, port: 3000 }
initialDelaySeconds: 15
periodSeconds: 10
readinessProbe:
httpGet: { path: /readyz, port: 3000 }
initialDelaySeconds: 5
periodSeconds: 5
优雅停机:
terminationGracePeriodSeconds: 60
7.2 Helm 化路线
- P6:原生 manifest(快速验证)
- P7:抽取 Helm Chart,values.yaml 按环境区分
- P8:引入 Argo CD GitOps 自动同步
8. 灾难恢复
8.1 RTO/RPO 目标
| 场景 | RTO | RPO |
|---|---|---|
| 单 Pod 故障 | 30s | 0 |
| 单节点故障 | 2min | 0 |
| 单可用区故障 | 10min | 0 |
| 区域级灾难 | 30min | 15min |
8.2 多可用区策略
- K8s 集群跨 3 可用区,Pod 反亲和
- DB 主从跨可用区同步复制
- Redis 哨兵跨可用区
- Kafka min.insync.replicas=2,跨可用区 broker
8.3 DNS 切换
- 区域级故障:通过全局 DNS(Cloudflare/Route53)切换到备用区域
- 健康检查:每 10s 探测,连续 3 次失败自动切换
- TTL:60s(快速切换)
- 演练:每季度一次 DNS 切换演练
9. 故障排查
| 现象 | 可能原因 | 排查步骤 | 解决方案 |
|---|---|---|---|
| 5xx 激增 | 下游服务故障 | 查 Grafana 熔断状态、下游健康 | 确认熔断器已打开,扩容下游 |
| P99 升高 | DB 慢查询/连接耗尽 | 查 PG 慢日志、连接池 | 加索引/扩连接池/限流 |
| 消费堆积 | 消费者慢/宕机 | 查 Kafka lag、消费者日志 | 扩消费者、修 bug |
| 备份失败 | 存储/网络/凭证 | 查 backup-cron 日志 | 修凭证、清理旧备份 |
| 健康检查失败 | DB 不可达 | 查 DB 状态、网络 | 故障转移、恢复 DB |
| Pod 频繁重启 | OOM/探针失败 | 查 kubectl describe、内存 | 调资源/修探针 |
| 熔断不恢复 | 下游未恢复 | 查 Half-Open 试探结果 | 修复下游、调 Timeout |
| 限流误杀 | 桶配置过低 | 查限流日志、QPS | 调高桶容量 |
| DNS 切换无效 | TTL 缓存 | 查 DNS 解析链 | 等待 TTL / 清缓存 |
| 跨区延迟高 | 跨区流量 | 查网络拓扑 | 调亲和性就近访问 |
附录:关联文档
- 架构影响地图:
docs/architecture/004_architecture_impact_map.md - P6 架构补记:
docs/architecture/004-p6-addendum.md - 事件响应:
docs/architecture/runbooks/incident-response.md - 已知问题:
docs/troubleshooting/known-issues.md - P6 已知问题补丁:
docs/troubleshooting/known-issues-p6-addendum.md - 技术债务:
docs/architecture/roadmap/tech-debt.md