# 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 <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）

执行：

```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 <svc> --backup-id <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/<experiment>.yaml`
4. 观察：Grafana + 日志
5. 回滚：`./chaos/rollback.sh <experiment>`
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`
