小微企业 SRE 稳定性建设(二):上线前 P0 验收项目
写在前面
上一篇讲了小微团队上线前为什么要做 验收、压测、演练、签字 。这一篇只聚焦一个问题:上线前哪些项目属于 P0,任何一项不过都不建议上线?
这里的 P0 不是完整 SRE 体系,也不是大厂标准化平台。它更像一个 上线前一次性关口:用有限时间证明核心链路能跑、数据不会 silent 丢、故障能恢复、生产风险可控。Grafana 全量大盘、ES 日志、蓝绿发布、长期容量治理都重要,但不是这篇的重点。
小微团队最容易踩坑的地方,是把「服务能启动」「功能点过了」当成「可以上线」。真正的 P0 验收要回答的是:
- 真实用户或设备进来后,核心链路是否稳定?
- 数据写入失败时,是否能被发现、重试或补偿?
- 数据库、缓存、消息队列、代理层任一关键点异常后,业务是否能恢复?
- 备份到底能不能 restore?
- 生产环境是否存在一眼能看出的高危暴露和 root 误操作风险?
一、P0 验收的边界
先把边界说清楚,避免团队一开会就跑偏。
P0 是上线门槛,不是长期建设计划。
P0 项目必须满足三个特征:阻断核心业务、可能导致数据丢失或不可恢复、上线后出问题会演变成 P0/P1 事故。
P0 要验现状,不要临场重构。
上线前一周发现 ProxySQL 单点、消息队列没集群、日志没有 ES,不代表必须马上全改。此时更现实的做法是:判断它是否挡上线;若挡,就修最小闭环;若不挡,就签已知风险并列入后续整改。
P0 不通过的结论要明确。
不要写「基本可用」「后面观察」。P0 只有三种结论:
- 通过:证据完整,可以进入发布。
- 不通过:阻断上线,修复后复检。
- 有条件通过:必须写清楚风险、临时措施、责任人与签字;但涉及数据丢失、备份不可恢复、生产高危暴露时,不建议放进有条件通过。
二、P0 最小验收集
下面这张表适合在上线评审会上先过一遍。它不是所有检查项,但能快速判断团队有没有遗漏大的风险面。
| # | 领域 | 必验问题 | 合格标准 |
|---|---|---|---|
| 1 | 变更与环境 | Pre 和 Prod 是否一致?本次上线影响哪些链路? | 镜像、配置、数据库连接、代理规则、消息 ACL 差异已记录;无未知 Diff |
| 2 | 核心业务链路 | 主流程是否走通?鉴权和越权是否测过? | P0 接口 100% 通过;登录、写入、读取、权限、异常输入均有记录 |
| 3 | 单点链路与熔断 | 核心业务路径上有哪些单点?下游慢或挂时是否有熔断/降级? | 单点链路已梳理;无熔断时风险、影响面和人工处置已同步确认 |
| 4 | 弱网与网络抖动 | 网络不稳定时,交互、重试、数据接收是否异常? | 弱网、断连、超时、重复提交场景通过;失败可感知且无重复脏数据 |
| 5 | 数据写入链路 | 写入失败会不会 silent 丢? | 已提交数据可查;失败数据可重试、可追溯或进 dead letter |
| 6 | 数据库与读写分离 | 主从、代理、连接池是否真实可用? | 复制正常;读写路由正确;连接池峰值不超过阈值;应用重启后连接可释放和重建 |
| 7 | 缓存一致性 | Redis/缓存和数据库口径是否一致? | 当前值、原始表、结果表或回源表的口径已书面确认 |
| 8 | 消息与异步任务 | 消费积压、Job 停止是否可见可恢复? | 积压有指标和告警;恢复后能在目标时间内回到稳态 |
| 9 | 备份与恢复 | 备份是否真的能恢复? | 最近全备成功;Pre 或临时实例 restore 过一次;核心表抽验正常 |
| 10 | 压测 | 首批规模是否扛得住? | 按计划设备数/用户数压测通过;错误率、P95、资源、积压在阈值内 |
| 11 | 故障演练 | 关键组件故障后是否能恢复? | 至少做过主备切换、积压恢复、缓存/数据库异常中的核心场景 |
| 12 | 安全与审计 | 生产是否有明显高危暴露? | DB/Redis 不对公网;密钥不进 Git;生产 root 操作有管控和审计 |
| 13 | 发布回滚 | 发版失败是否能退? | 上一版镜像/配置可用;回滚步骤、触发条件、负责人明确 |
| 14 | 签字结论 | 谁对 Go/No-Go 负责? | 研发、运维/SRE、产品或负责人签字;已知风险单独列出 |
这 14 项里,单点链路、数据写入、备份恢复、故障演练、安全暴露 是最不能含糊的。功能错了还能发补丁;数据丢了、备份恢复不了、核心单点无预案、生产被公网扫穿,代价完全不一样。
三、P0-01:变更与环境一致性
很多小团队的事故,不是代码 bug,而是 Pre 和 Prod 不一致。
常见情况包括:
- Pre 连的是单库,Prod 走主从和代理。
- Pre 没有缓存,Prod 有 Redis。
- Pre 的消息 Topic ACL 宽松,Prod 上设备权限不同。
- Pre 的环境变量、连接池、超时时间和生产不一致。
- Pre 用空库或小数据,Prod 有历史数据和慢查询。
上线前至少要有一张对照表:
| 检查项 | Pre | Prod | 结论 |
|---|---|---|---|
| 镜像版本 / Tag | |||
| 配置文件 / 环境变量 | |||
| 数据库连接方式 | |||
| 缓存地址与策略 | |||
| 消息队列 / Broker ACL | |||
| 代理层规则 | |||
| 外部依赖 | |||
| 定时任务 / Job |
合格标准不是「完全一样」,而是 差异可解释、可签字。例如 Pre 用脱敏库、设备数更少,这是合理差异;但 Pre 不走 ProxySQL,Prod 走 ProxySQL,这就必须单独补读写分离和切换验证。
四、P0-02:核心业务与接口验收
业务验收不能只靠产品点几下页面。P0 要覆盖 主路径、单点链路、熔断降级、权限、异常输入、关键接口契约。
最小主路径示例:
- 用户登录成功,拿到 Token。
- 创建、绑定或选择核心业务对象。
- 触发一次写入,例如设备上报、订单提交、表单保存、任务创建。
- 读取当前状态或最新结果。
- 查询历史记录、详情页或列表页。
- 退出登录后旧 Token 不可继续访问。
权限和异常输入至少要测:
- 未登录访问核心接口,应返回 401。
- 用户 A 访问用户 B 的资源,应返回 403 或 404。
- 缺字段、非法枚举、超长 payload,应返回明确 4xx 或进入错误处理链路。
- 重复提交、重复消息、重复回调,应具备幂等或去重策略。
核心业务还要单独做一张 单点链路梳理表。重点不是画复杂架构图,而是把「某个点挂了以后,业务会不会整体不可用」说清楚。
| 链路节点 | 是否单点 | 影响范围 | 是否有熔断/降级 | 无熔断时的风险确认 |
|---|---|---|---|---|
| App / Web 入口 | ||||
| API 网关 / Nginx / Caddy | ||||
| Backend 核心服务 | ||||
| 数据库代理 / 连接池 | ||||
| Redis / 缓存 | ||||
| 消息队列 / Broker | ||||
| 第三方 API / 支付 / 短信 / AI 服务 |
熔断机制不一定要求上线前全部补齐,但必须同步确认风险。例如第三方 API 慢 30 秒时,核心写入是否被拖死;Redis 不可用时,是降级读 DB、返回明确错误,还是线程池被打满;消息队列不可用时,入口是拒绝、排队还是继续返回成功。
没有熔断或降级时,评审记录里至少要写清:
- 哪条核心链路存在单点。
- 单点故障会影响哪些用户或数据。
- 是否会造成数据丢失、重复提交或长时间阻塞。
- 临时人工处置方式是什么。
- 谁接受这个风险,什么时候补齐。
弱网环境也要纳入核心业务验收。尤其是 App、设备上报、移动端提交、WebSocket/MQTT 长连接、文件上传、支付回调这类场景,网络抖动比服务宕机更常见。
弱网测试建议至少覆盖:
| 场景 | 验收重点 | 合格标准 |
|---|---|---|
| 高延迟 | 请求超时、按钮重复点击、前端等待状态 | 不重复提交;超时提示明确 |
| 丢包 / 抖动 | API 调用、设备上报、长连接心跳 | 自动重试或明确失败;服务端无脏数据 |
| 短暂断网后恢复 | App 重新提交、设备重连、消息补发 | 数据最终一致;重复消息可幂等 |
| 慢速网络 | 登录、当前值、提交、上传等主路径 | 不出现无限 loading;失败可恢复 |
| 服务端半成功 | 客户端超时但服务端已写入 | 再次提交不会生成重复业务数据 |
合格标准:
- P0 接口 100% 通过。
- 核心 API 的状态码、返回字段、错误体格式有记录。
- 异常输入不能 silent 成功。
- 鉴权和越权必须有测试证据。
- 核心业务单点链路已梳理;无熔断/降级的风险已由负责人确认。
- 弱网、断连、超时、重复提交场景有测试记录。
五、P0-03:数据写入链路
小微系统最危险的不是「接口报错」,而是 接口成功了,数据没落下,或者只落了一半。
以常见链路为例:
text
请求 / 设备 / 任务
↓
API / Consumer
↓
缓存 Redis
↓
数据库 MySQL
↓
异步 Job / 聚合表 / 结果表
P0 要验的不是链路图,而是每一步能不能核对:
| 检查项 | 验收方式 | 合格标准 |
|---|---|---|
| 写入入口 | 发起一条真实或模拟写入 | API/Consumer 有成功或失败日志 |
| 原始数据 | 查原始表、消息表或落盘记录 | 同一批次数据可查 |
| 缓存当前值 | 查 Redis 或等价缓存 | 与本次写入口径一致 |
| 结果表 / 聚合表 | 等待约定延迟后查询 | 在约定窗口内生成 |
| 失败处理 | 模拟 DB 或下游短暂失败 | 失败可感知、可重试、可追溯 |
这里最重要的是 ack 或返回成功的时机。如果系统在「只写了缓存、还没写数据库」时就告诉设备或用户成功,那么数据库失败时就可能出现 silent 丢数。
上线前必须让研发书面确认:
- 成功响应是在写入哪一步之后返回?
- 如果数据库写失败,数据进入哪里?
- 如果消息重复投递,如何避免重复脏写?
- 如果异步 Job 停止,积压在哪里看?
- 如果缓存和数据库不一致,以哪个为准?
六、P0-04:数据库、主从与代理层
只要生产用了 MySQL 主从、读写分离、ProxySQL、数据库代理或云数据库只读实例,就不能只检查「能连上」。
最小检查项:
| 检查项 | 合格标准 |
|---|---|
| 主从复制 | IO/SQL 线程正常,复制错误为空 |
| 复制延迟 | 稳态延迟在目标内,例如 ≤10s;压测后能回落 |
| 读写路由 | 写请求落主库,只读查询按设计落从库 |
| 从库只读 | 从库不能被业务误写 |
| 连接池 | 压测峰值连接使用率 <80%,无持续 ConnERR |
| 应用重启后的连接回收 | 手动重启 Backend / API / Consumer 后,旧连接能释放,新连接能重建 |
| 慢 SQL / 锁 | 核心接口无明显全表扫,无持续锁等待 |
| 切换流程 | 主备切换 Runbook 有最近演练记录 |
连接池要单独做 应用侧手动重启恢复测试。很多线上问题不是数据库挂了,而是应用重启、滚动发布或异常退出后,旧连接没有及时释放,新连接又打满代理或数据库。
建议在 Pre 执行:
text
1. 记录重启前:应用连接池活跃连接数、ProxySQL/数据库 ConnUsed、Threads_connected。
2. 手动重启一个 Backend / API / Consumer 实例。
3. 观察旧连接是否在合理时间内释放。
4. 观察新实例启动后是否能自动建立连接。
5. 连续调用核心读写接口,确认无持续 5xx、无连接泄漏。
6. 重复 2~3 次,模拟发布或异常恢复。
合格标准:
- 旧应用实例退出后,数据库或代理层旧连接能释放,不长期残留。
- 新应用实例无需人工改配置即可自动重连。
- 重启后连接数回到稳定区间,不持续上涨。
- ProxySQL 或数据库无持续 ConnERR、Too many connections、连接池 exhausted。
- 核心读写接口在恢复窗口后正常。
主备切换建议至少在 Pre 做一次:
text
1. 持续发起写入和读取。
2. 停掉当前 Master 或模拟不可用。
3. 执行与生产一致的 promote / 切流步骤。
4. 记录切换完成时间、业务恢复时间。
5. 核对切换前已提交数据是否在新 Master 可查。
6. 确认旧 Master 恢复后不会重新成为写节点。
合格标准可以根据团队规模写成:
- 切换和写恢复 ≤ 书面 RTO,例如 5 分钟。
- 已 commit 数据不丢。
- 失败写入有明确窗口和补偿方式。
- 切换后无双主、无写旧库。
如果团队没有自动切换,也不是绝对不能上线;但 人工切换步骤必须演练过。最怕的是「文档里说可以切」,真实故障时没人知道先 promote 还是先改代理。
七、P0-05:缓存一致性与回源
Redis、Memcached、本地缓存都一样:上线前要确认它是 缓存,不是团队误以为的事实数据库。
必须确认四件事:
| 检查项 | 合格标准 |
|---|---|
| Key 设计 | 核心 Key 命名、TTL、数据结构明确 |
| 命中路径 | API 命中缓存时,返回值与业务口径一致 |
| Miss 回源 | 删除测试 Key 后,API 能从数据库或权威源回源 |
| 回填策略 | 回源后缓存能重新生成 |
| 失败策略 | Redis 失败时,业务是降级、报错还是读 DB,有明确行为 |
缓存和数据库允许存在短暂差异,但差异必须是 书面定义的窗口,例如:
text
当前值优先读 Redis;
Redis miss 回源查 sensor_data 最新行;
异步 Job 允许 Redis 领先结果表 30~90s;
超过 90s 视为异常,需要告警或人工排查。
没有这个口径,线上一旦用户问「为什么页面数据和后台不一致」,研发、测试、运维会互相解释,最后谁也说不清。
八、P0-06:消息链路与异步任务
有 MQTT、Kafka、RabbitMQ、RocketMQ、Redis Stream、定时 Job、异步消费,就要验 积压、恢复、重复、告警。
P0 不要求你把消息系统建设得很完美,但至少要证明:
- 消息进来了能被消费。
- 消费者停一段时间后,积压能被看见。
- 消费者恢复后,积压能下降。
- 非法消息不会 silent drop。
- 重复消息不会写出重复脏数据。
- Job 停止后有人知道。
建议 Pre 演练一个最小场景:
text
1. 持续生产消息或模拟设备上报。
2. 停消费者 5~10 分钟。
3. 观察队列深度、未处理行数或最老消息 age 上升。
4. 启动消费者。
5. 观察积压持续下降,并在目标时间内回到稳态。
6. 检查是否触发告警。
合格标准示例:
- 积压指标可见。
- 告警 5 分钟内可达。
- 恢复后 30 分钟内回到稳态,或按业务规模写明目标。
- 无 OOM、无重复脏写、无不可解释丢消息。
九、P0-07:备份与恢复
备份最常见的假象是:每天都有文件,但从来没人 restore。
P0 只认恢复结果,不只认备份任务。
| 检查项 | 合格标准 |
|---|---|
| 全备任务 | 最近一次成功 ≤24h |
| 备份保留 | 至少保留 7 天,生产建议更长 |
| Binlog / 增量 | 如要求点到点恢复,binlog 必须开启并有保留 |
| Restore 演练 | Pre 或临时实例完整恢复过一次 |
| 核心表抽验 | 用户、订单、设备、配置、业务主表等数据量级正常 |
| 恢复耗时 | 记录 RTO |
| 数据丢失窗口 | 记录 RPO |
小微团队可以不一开始就做复杂容灾,但不能没有这句话:
text
最近一次全备可恢复;
恢复后核心业务表可查;
本次系统可接受的 RPO/RTO 已由负责人确认。
如果 restore 没做过,建议直接判 P0 不通过。因为真正事故发生时,备份文件损坏、缺库、缺权限、字符集不对、恢复脚本跑不通,都不是临时能稳住的事。
十、P0-08:压测与容量
P0 压测不是为了找极限,而是证明 首批上线规模不会把系统打爆。
压测参数要从业务规模倒推:
| 参数 | 示例 |
|---|---|
| 首批设备数 / 用户数 | 50 台设备、200 个用户 |
| 单设备上报间隔 | 60s |
| App 峰值并发 | 20~30 |
| 核心接口 | 当前值、详情页、历史曲线、提交接口 |
| 压测环境 | 与生产同构的 Pre |
建议至少做三类:
写入压测 :模拟真实写入频率,持续 30~60 分钟。
关注成功率、DB CPU、连接数、主从延迟、消息积压、异步 Job 是否堆积。
读取压测 :核心读接口并发 10~30,持续 10~15 分钟。
关注成功率、P95、5xx、缓存命中和回源压力。
混合压测 :写入和读取同时进行。
很多小团队单测写入没问题,单测读取也没问题,但混在一起时 DB、连接池、缓存回源一起抖。
合格标准建议写成:
- 核心写入成功率 ≥99%。
- 核心读接口成功率 ≥99.5%。
- 5xx <0.5%。
- P95 在业务可接受目标内。
- CPU、内存、连接池不持续触顶。
- 积压在压测结束后可回落。
- 生产首批规模建议不超过 Pre 验证稳定规模的 70%。
十一、P0-09:安全与审计底线
这部分不追求安全体系完整,但要挡住低级高危风险。
上线前必查:
| 检查项 | 合格标准 |
|---|---|
| 数据库公网暴露 | MySQL、PostgreSQL、Redis 不对 0.0.0.0/0 开放 |
| 管理后台 | Broker Dashboard、Admin 后台、数据库管理工具不裸露公网 |
| 密钥 | DB 密码、JWT Secret、第三方 Key 不进 Git、不写在公开文档 |
| 生产账号 | 日常研发不共用 root 直连生产 |
| 审计 | 至少能查近 7 天谁登录过生产、谁做过高危变更 |
| TLS | 对外 HTTPS / MQTTS 证书有效 |
| 权限隔离 | Pre 和 Prod 账号、密钥、数据库权限区分 |
如果发现数据库或 Redis 直接暴露公网,这不是 P1/P2 优化项,而是 P0。不要把它写成「上线后加固」,因为它可能比业务 bug 更快造成事故。
十二、P0-10:发布、回滚与观察期
最后一个 P0 是发版本身。
上线前必须准备:
- 发布步骤:谁执行、在哪台机器、执行哪些命令。
- 回滚步骤:回哪个镜像、回哪个配置、数据库是否需要回滚。
- 回滚触发条件:5xx、错误率、积压、延迟、关键接口失败达到什么阈值必须回滚。
- 观察指标:接口错误率、响应时间、DB 连接、主从延迟、消息积压、磁盘、告警。
- 观察期负责人:上线后 24~72 小时谁盯。
回滚要避免一句空话:
text
出问题就回滚。
应该写成:
text
若上线后 10 分钟内核心接口 5xx > 1%,或写入成功率 < 99%,或消息积压持续增长 15 分钟不下降,则由值班负责人执行回滚到 image:xxx,配置回滚到 commit:xxx。
小微团队不一定有成熟发布平台,但至少要有可执行的 Runbook。
十三、上线评审模板
下面这张表可以直接复制成上线评审记录。
| 项目 | 结论 | 证据 / 链接 | 负责人 |
|---|---|---|---|
| Pre / Prod Diff 已确认 | □ 通过 □ 不通过 | ||
| P0 业务接口 100% 通过 | □ 通过 □ 不通过 | ||
| 核心单点链路和熔断风险已确认 | □ 通过 □ 不通过 | ||
| 弱网、断连、重复提交测试通过 | □ 通过 □ 不通过 | ||
| 数据写入链路可核对 | □ 通过 □ 不通过 | ||
| DB 主从 / 代理 / 连接池通过 | □ 通过 □ 不通过 □ 不适用 | ||
| 应用重启后的连接释放和重建通过 | □ 通过 □ 不通过 □ 不适用 | ||
| 缓存一致性和回源通过 | □ 通过 □ 不通过 □ 不适用 | ||
| 消息积压恢复通过 | □ 通过 □ 不通过 □ 不适用 | ||
| 备份 restore 通过 | □ 通过 □ 不通过 | ||
| 首批规模压测通过 | □ 通过 □ 不通过 | ||
| 关键故障演练通过 | □ 通过 □ 不通过 | ||
| 安全底线通过 | □ 通过 □ 不通过 | ||
| 发布回滚 Runbook 完成 | □ 通过 □ 不通过 | ||
| 观察期负责人明确 | □ 通过 □ 不通过 |
最终结论:
| 结论 | 说明 |
|---|---|
| □ 同意上线 | P0 全部通过 |
| □ 延期上线 | 存在 P0 不通过项 |
| □ 有条件上线 | 风险、临时措施、责任人与复检时间已签字 |
签字:
| 角色 | 姓名 | 日期 |
|---|---|---|
| 研发负责人 | ||
| 运维 / SRE | ||
| 产品 / 业务负责人 |
十四、哪些不属于本次 P0
为了防止 P0 被无限扩大,也要明确哪些可以作为 P1/P2 后续补齐:
- ES / Loki 集中日志平台。
- Grafana 全量业务大盘。
- 24 小时以上 soak 长稳压测。
- 完整蓝绿 / 金丝雀发布平台。
- 多机房容灾。
- 全链路压测平台。
- 自动化故障注入平台。
- 完整安全基线和等保体系。
这些都重要,但对 10~50 人的小微团队来说,第一次上线前更应先把 数据可恢复、故障不 silent、备份能 restore、生产不裸奔 做扎实。
十五、结语
P0 验收的价值,不是让小微团队一夜之间变成成熟 SRE 组织,而是建立一个朴素但有效的上线关口:
功能可用只是起点;数据不丢、故障可恢复、备份能救、风险有人签字,才是可以上线的最低标准。
下一篇可以继续往数据层展开:主从、代理、缓存、异步表之间,如何定义权威数据源、延迟窗口和一致性验收标准。