# 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` 维度限流 熔断器配置示例(伪代码): ```go 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 故障演练流程 1. 在预发环境注入下游延迟(tc/netem 500ms+) 2. 观察熔断器状态切换:Closed → Open → Half-Open → Closed 3. 验证限流桶在窗口边界正确清理(sync.Map + ticker) 4. 记录 P99 与错误率到演练报告 5. 回滚注入:`./chaos/rollback.sh ` ## 3. 备份与恢复 ### 3.1 备份脚本 - 位置:`scripts/backup/backup-cron.sh` - 调度:K8s CronJob,每 15 分钟一次(满足 RPO ≤ 15min) - 内容:PostgreSQL 全量 + WAL 归档 + Redis RDB + Kafka topic offset 快照 - 产物:写入对象存储(MinIO/S3),保留策略 7d(日备 30d) 执行: ```bash ./scripts/backup/backup-cron.sh --service iam --type full ./scripts/backup/backup-cron.sh --service iam --type incr ``` ### 3.2 恢复演练流程 1. 在隔离环境拉起空集群 2. 执行 `./scripts/restore/restore.sh --service --backup-id ` 3. 校验数据一致性(行数、最新位点、校验和) 4. 记录恢复耗时,验证 RTO ≤ 30min 5. 演练频率:每月一次 ### 3.3 RPO 验证方法 - 对比最近一次备份时间与当前时间差 - 查询 `backup_log` 表 `last_success_at` 字段 - 告警:连续 2 个周期(30min)未成功备份 → P1 ## 4. 监控告警 ### 4.1 Prometheus 规则 位置:`deploy/observability/prometheus/rules/` 关键规则: - `HighErrorRate`:5xx 比率 > 1% 持续 5min - `HighLatencyP99`:P99 > 500ms 持续 5min - `CircuitBreakerOpen`:熔断器处于 Open 态 > 1min - `BackupStale`:备份超过 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 执行流程 1. 选择实验 → 在 `chaos/` 选择对应 YAML 2. 预检:确认告警通道、回滚脚本就绪 3. 执行:`kubectl apply -f chaos/.yaml` 4. 观察:Grafana + 日志 5. 回滚:`./chaos/rollback.sh ` 6. 复盘:记录到 `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`:ClusterIP - `hpa.yaml`:CPU>70% 扩容,min=3 max=20 - `poddisruptionbudget.yaml`:minAvailable=2 - `networkpolicy.yaml`:限制出向 探针配置: ```yaml livenessProbe: httpGet: { path: /healthz, port: 3000 } initialDelaySeconds: 15 periodSeconds: 10 readinessProbe: httpGet: { path: /readyz, port: 3000 } initialDelaySeconds: 5 periodSeconds: 5 ``` 优雅停机: ```yaml 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`