小微企业 SRE 稳定性建设(一):痛点、隐患与上线验收怎么做
作者:xxx
日期:2026-06
标签:SRE、稳定性、小微企业、上线验收、压力测试
专栏:小微企业 SRE 建设 · 第 1 篇
写在前面
最近接到几支 初创 / 小微团队 的业务上线咨询:功能在 Pre 已跑通,负责人问「能不能这周切生产」。往下聊几句,常见问题总是同一类------主从上了但没练过切换、消息链路能通但没测过积压恢复、备份 cron 有但从没 restore 过;研发手里有生产 root,SRE 或兼岗同学 不知道昨天谁改了什么。
这篇把咨询里反复出现的判断,整理成 一篇可给团队对齐的开篇 :前面短讲 为什么要做、不做会怎样 ;后面长讲 上线前必须闭环的验收与测试 (不限于功能点一点,还包括接口、压测、服务器端与故障演练)。细节清单会在专栏后续篇目展开,这里先把 关口 说全。
一、什么样的团队算「小微」
不按注册资本,按 怎么运维:
研发若干人,业务要赶上线;不一定没有 SRE ------常见是 后端兼岗、外部顾问、或实习生顶 SRE 岗位 ,专职且资深的人少,值班和切换往往 只有一个人会 。系统多在云上或几台 Docker 主机,链路通常是 写入(设备/MQ/任务)+ 读取(App/API)+ 数据库/缓存,规模一上来,故障会直接伤口碑和收入。
二、三个痛点
第一,人力与岗位:有人名目不等于有人能扛。
兼岗和实习生可以把监控接上、把 compose 拉起来,但 主备切换、备份 restore、Broker 积压 若只有一人会、且半年没练,等于没有替补。更麻烦的是 没有 Go/No-Go:测过 Pre 不等于 Pre 与 Prod 同构,首批真实流量上来才暴雷。
第二,架构与数据:看起来生产级,实际上不可验证。
主从、代理、Redis、消息队列、定时 Job 叠得很快,数据以哪张表为准、Job 挂了有没有告警、切换后写入怎么恢复 说不清。备份任务在,完整 restore 没做过 ;试点十台正常,上百台同时写入时连接池、延迟、积压一起爆------ 容量只按 demo 验过。
第三,安全与审计:小团队、弱意识、高危操作缺痕。
规模不大,安全容易被当成「以后补」。常见情况:研发直接用 root 或共享账号改生产配置 ,不经过 SRE;数据库、Redis 端口对公网或弱口令;生产变更 无审计、无变更单、无回滚记录 。出问题时无法回答「谁、何时、改了什么」,也无法区分是代码问题还是 人肉误操作 。这类风险不一定会立刻宕机,但 一次误删、一次错误配置 的代价往往不低于一次硬件故障。
三、稳定性要解决的:防火,不是救火
稳定性建设 主要不是 教你在 P0 夜里怎么敲命令更快------那是救火。
主要是防火 :在上线前用 验收、压测、演练、签字,把「会着火」的地方先关掉------数据 silent 丢、假高可用、积压不可见、备份不能 restore、生产裸 root、变更不可追溯。
对负责人可以这么理解:晚几天上线 ,买的是 可恢复、可审计、规模上来不爆 ;带着 P0 隐患上线 ,买的往往是 停发版一两周 + 客户信任 。成本上,一次 Pre 演练的时间 通常远小于 一次真实 P0;钱应花在 restore、Pre 同构、最小告警和演练上,而不是先堆十套组件名字(这点不展开,专栏后面单写成本治理)。
不做或只做表面的隐患 ,浓缩成一句:架构图越复杂,团队越需要可验证的关口;否则复杂度只会放大事故,不会自动变成高可用。
四、标准上线流程:每一步要做什么(干货)
下面是一套 小微团队可执行、且必须闭环 的上线流程。参考近期 双链路 IoT 项目 (设备 MQTT 写入 + App HTTPS 读取 + MySQL/Redis/代理)的 Pre 验收实践,换栈也适用,只需替换具体组件名。
原则三条:
- 写压、故障注入只在 Pre;生产做只读巡检。
- P0 任一项不过,不建议上线(可书面承担风险,但要有签字)。
- 不止功能测试 :必须包含 接口测试、压力测试、服务器端巡检、故障演练。
#mermaid-svg-YeK7mNAjZ7f08foW{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-YeK7mNAjZ7f08foW .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-YeK7mNAjZ7f08foW .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-YeK7mNAjZ7f08foW .error-icon{fill:#552222;}#mermaid-svg-YeK7mNAjZ7f08foW .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-YeK7mNAjZ7f08foW .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-YeK7mNAjZ7f08foW .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-YeK7mNAjZ7f08foW .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-YeK7mNAjZ7f08foW .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-YeK7mNAjZ7f08foW .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-YeK7mNAjZ7f08foW .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-YeK7mNAjZ7f08foW .marker{fill:#333333;stroke:#333333;}#mermaid-svg-YeK7mNAjZ7f08foW .marker.cross{stroke:#333333;}#mermaid-svg-YeK7mNAjZ7f08foW svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-YeK7mNAjZ7f08foW p{margin:0;}#mermaid-svg-YeK7mNAjZ7f08foW .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-YeK7mNAjZ7f08foW .cluster-label text{fill:#333;}#mermaid-svg-YeK7mNAjZ7f08foW .cluster-label span{color:#333;}#mermaid-svg-YeK7mNAjZ7f08foW .cluster-label span p{background-color:transparent;}#mermaid-svg-YeK7mNAjZ7f08foW .label text,#mermaid-svg-YeK7mNAjZ7f08foW span{fill:#333;color:#333;}#mermaid-svg-YeK7mNAjZ7f08foW .node rect,#mermaid-svg-YeK7mNAjZ7f08foW .node circle,#mermaid-svg-YeK7mNAjZ7f08foW .node ellipse,#mermaid-svg-YeK7mNAjZ7f08foW .node polygon,#mermaid-svg-YeK7mNAjZ7f08foW .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-YeK7mNAjZ7f08foW .rough-node .label text,#mermaid-svg-YeK7mNAjZ7f08foW .node .label text,#mermaid-svg-YeK7mNAjZ7f08foW .image-shape .label,#mermaid-svg-YeK7mNAjZ7f08foW .icon-shape .label{text-anchor:middle;}#mermaid-svg-YeK7mNAjZ7f08foW .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-YeK7mNAjZ7f08foW .rough-node .label,#mermaid-svg-YeK7mNAjZ7f08foW .node .label,#mermaid-svg-YeK7mNAjZ7f08foW .image-shape .label,#mermaid-svg-YeK7mNAjZ7f08foW .icon-shape .label{text-align:center;}#mermaid-svg-YeK7mNAjZ7f08foW .node.clickable{cursor:pointer;}#mermaid-svg-YeK7mNAjZ7f08foW .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-YeK7mNAjZ7f08foW .arrowheadPath{fill:#333333;}#mermaid-svg-YeK7mNAjZ7f08foW .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-YeK7mNAjZ7f08foW .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-YeK7mNAjZ7f08foW .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-YeK7mNAjZ7f08foW .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-YeK7mNAjZ7f08foW .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-YeK7mNAjZ7f08foW .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-YeK7mNAjZ7f08foW .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-YeK7mNAjZ7f08foW .cluster text{fill:#333;}#mermaid-svg-YeK7mNAjZ7f08foW .cluster span{color:#333;}#mermaid-svg-YeK7mNAjZ7f08foW div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-YeK7mNAjZ7f08foW .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-YeK7mNAjZ7f08foW rect.text{fill:none;stroke-width:0;}#mermaid-svg-YeK7mNAjZ7f08foW .icon-shape,#mermaid-svg-YeK7mNAjZ7f08foW .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-YeK7mNAjZ7f08foW .icon-shape p,#mermaid-svg-YeK7mNAjZ7f08foW .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-YeK7mNAjZ7f08foW .icon-shape .label rect,#mermaid-svg-YeK7mNAjZ7f08foW .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-YeK7mNAjZ7f08foW .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-YeK7mNAjZ7f08foW .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-YeK7mNAjZ7f08foW :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 1.变更与架构
2.业务与接口测试
3.服务器端巡检
4.压力测试
5.故障演练闭环
6.安全与审计
7.发布回滚与观察
8.复盘沉淀
步骤 1 · 变更界定与架构对齐
要做什么
- 一页变更说明:版本、影响 写入链 / 读取链、是否动数据层或消息链路、回滚方式一句话。
- 架构简图:标出单点、外部依赖(推送、第三方 API、LLM 等)。
- Pre 与 Prod Diff :镜像版本、代理规则、Broker ACL、连接池、环境变量------无 Diff 或 Diff 已签字。
交付物
变更单 + 双链路图 + Pre/Prod 对照表。
步骤 2 · 业务测试与接口测试(不只是「点一点功能」)
要做什么
业务场景(手工或自动化)
- 主路径全流程:例如 登录 → 绑定资源 → 查当前状态 → 查历史/曲线(按你的产品改)。
- 权限:未登录 / 越权访问他人资源 → 应 401/403。
- 异常输入:非法 payload、缺字段 → 进 dead_letter 或明确 4xx,不能 silent 成功。
接口测试(建议独立做一层)
- 核心 API 契约:状态码、必要字段、错误体格式。
- 鉴权:Token 过期、登出后旧 Token 不可用。
- 读写分离场景:缓存命中与 缓存 miss 回源 各测一条(若架构存在 Redis + DB)。
- 若有 OpenAPI/Swagger,至少跑一轮 回归集合 ;没有则维护 10~20 条 Postman/脚本用例。
交付物
用例列表 + 执行结果;P0 接口 100% 通过 方可进入步骤 3。
步骤 3 · 服务器端巡检(生产只读 + Pre 可写)
针对 数据层、中间件、进程,不是看「容器 Up」就完事。
基础资源
- 磁盘、内存、inode;Docker/进程 全部 Up;证书未过期。
数据库与代理(若有主从 + ProxySQL 等)
- 复制延迟 ≤10s(持续观测),复制线程正常。
- 读写分离实测:只读查询落从库、写入落主库(连发多次验证,非一次侥幸)。
- 连接池:峰值占用 <80%,无持续 ConnERR。
缓存与数据一致性
- 写入一条业务数据后:缓存当前值 vs DB 原始表/结果表 可核对(允许架构约定的延迟窗口,如异步 Job 30~90s,但 口径须书面定义)。
- 异步管道:未处理积压 在阈值内(如 raw 表 pending 数量、最老 age)。
消息链路(若有 MQTT/Kafka 等)
- Broker TLS、ACL:设备 A 不能 pub 设备 B 的 topic。
- 端到端:模拟或真机 一条消息进 Broker → 消费者 → DB/缓存有记录。
备份
- 最近全备 ≤24h ;binlog 已开启且有落盘备份策略。
- Pre 上完整 restore 过一次,抽验核心业务表有数据。
交付物
巡检记录表;任一项 P0 不合格则停止后续压测与上线。
步骤 4 · 压力测试(按首批上线规模)
在 与 Prod 同构的 Pre 上,按 计划首批设备数/用户数/并发 执行,不是 demo 级点测。
写入侧 / ingest 压测(有设备或消息上报时必做)
- 模拟 N 台、按真实间隔(如 60s)持续 ≥30~60min。
- 看:ingest 成功率 ≥99% 、Consumer 无 OOM、DB 主库 CPU/连接不顶满、积压可控(Job 或队列深度在阈值内)。
- 主从:
Seconds_Behind稳态 ≤30s ,压测结束 ≤10s。
读取侧 / API 压测(必做)
- 核心读接口(如「当前状态」):并发 20~30,持续 10~15min。
- 指标:成功率 ≥99.5% ,P95 延迟在团队书面目标内(如 Redis 命中 <800ms ),5xx <0.5%。
- 若有 历史/聚合查询 类接口,单独 较低并发 测一轮(往往走 DB,延迟标准可放宽但需有数)。
混合压测(建议做)
- 写入 + 读取同时进行 30min,观察是否互相拖垮(很多小微事故出在这里)。
Redis miss / 回源专项(有 cache-aside 时建议做)
- 批量失效缓存 Key 后打读 API,验证 回源正确、Key 回填、从库/代理不被打满。
交付物
压测报告:规模假设、峰值 CPU/内存/积压、是否建议 首批规模 ≤ 极限 ×0.7。
步骤 5 · 故障演练闭环(Pre 必做,不是「看过文档」)
以下 至少各做 1 次 并留时间戳记录;典型 IoT 项目里,这几项直接对应「会不会 silent 丢数、会不会假高可用」。
F1 · 数据库主备切换
- Pre 持续写入时 停 Master → 执行与生产一致的 promote/切流脚本。
- 记录:切换耗时、业务恢复耗时 ;切换前已提交数据 在新 Master 可查 ;失败写入 dead_letter / 未 ack / 可重试,不能无声消失。
- F8 补充 :切换完成后 重启旧 Master ,确认 无双主、代理不写旧节点。
F2 · 代理层切流
- 配合 F1 或单独 OFFLINE 写节点:写组指向正确、ConnERR 5min 内恢复。
F3 / F4 · 单点写入失败
- 仅 MySQL 写失败 5min:缓存不能长期单独「假正常」。
- 仅 Redis 失败 5min :DB 仍写入;恢复后缓存 可回填。
F6 · 消息积压恢复
- 停消费者 10min ,上报/生产不停 → 堆积可观测 → 恢复后 ≤30min 消化到稳态;飞书/告警能触发。
F9 · 重复消息 / 幂等
- 同一条 QoS1 或同 payload 连发 20 次 → DB 无重复脏行(去重/幂等生效)。
交付物
演练记录:T0/T1/T2、失败条数、是否需重启应用、Go/No-Go 签字。
步骤 6 · 安全与审计验收
网络与暴露面
- MySQL、Redis、Broker 管理口等 不对 0.0.0.0/0 开放 ;仅 443 / MQTTS 等必要端口公网。
- TLS 证书有效;Broker topic ACL 抽测通过。
账号与操作
- 禁止 日常开发用 root 直连生产改配置;生产变更走 指定账号 + 变更单。
- 抽检:密钥 不进 Git ;生产与 Pre 账号权限分离。
- 审计 :至少能回答「近 7 天谁登录过生产、谁执行过高危变更」------云审计、堡垒机、或 sudo 日志 择一必有,不能全无。
交付物
安全底线勾选表 + 审计方式说明;生产 root 直连视为 P0 不合格(除非隔离网段且书面例外)。
步骤 7 · 发布、回滚与观察期
发布
- Runbook:发布步骤、负责人、回滚触发条件(5xx 超阈、ingest 跌、手动 No-Go)。
- 保留 上一版镜像/配置 ;Pre 练过一次回滚。
观察期(上线后 24~72h)
- 加强看:错误率、积压、主从延迟、磁盘;冻结非紧急变更。
- 首批设备/用户 灰度,有人对指标负责。
交付物
发布记录 + 观察期 checklist。
步骤 8 · 复盘与沉淀
- 凡 P0/P1:轻量复盘(时间线 + 根因 + 行动项)。
- 故障案例、演练记录、Runbook 更新 入库可检索------给下一任兼岗 SRE 或实习生,而不是只留在当事人脑子里。
最小闭环(人力极紧时也不能砍的)
若只能做「最少的一组」,至少:步骤 2 主路径 + 步骤 3 P0 巡检 + 步骤 4 一段压测 + 步骤 5 主备或积压任选一 + 步骤 6 公网与 root + 步骤 7 回滚与观察。
功能测通 alone 不算完成上线验收。
五、结语
小微团队做稳定性,关键是 上线关口 :在 Pre 把 接口、压测、服务器端、故障演练、安全审计 跑成闭环,而不是架构图上多几个组件。兼岗 SRE 和实习生可以执行清单,负责人只需要认 P0 与签字。
一句话给业务方:
我们不是为了完美 SRE,是为了上线前证明:规模上来扛得住、数据库挂了能切、消息堵了能退、备份能救、生产没人乱 root------否则就晚几天上。