别再把半夜爬起来当成运维的本事:On-call 文化,到底该怎么建?
别再把半夜爬起来当成运维的本事:On-call 文化,到底该怎么建?
作者:Echo_Wish
做运维的,估计都经历过这种场景。
凌晨两点,手机突然响了。生产环境告警,接口大量超时。
你迷迷糊糊地爬起来,打开电脑,登录服务器,查日志、看监控、翻最近的发布记录。折腾半个小时,终于发现是某个服务的连接池耗尽了。重启服务,告警恢复,顺手在群里发一句:“问题已解决,持续观察。”
然后呢?
第二天正常上班,昨天晚上没睡好的觉得补回来,没补回来的继续扛。周会上,领导可能还会夸一句:“这次响应挺及时,辛苦了。”
听起来挺正能量,对吧?
但我一直觉得,这里面有个很值得琢磨的问题:如果一个运维团队,总是依靠某几个能半夜爬起来、反应特别快的人来保证系统稳定,那究竟是团队能力强,还是系统本身有问题?
我更倾向于后者。
真正成熟的运维团队,不应该把谁能熬夜、谁接电话快、谁能在凌晨三点保持清醒当成核心竞争力。我们需要建立的是一种 On-call 导向文化:让故障有人接、有人处理、有人负责复盘,同时把值班人员的疲劳、告警响应时间 MTTA、故障恢复时间 MTTR 纳入可量化、可改进的管理体系。
注意,重点不是多做几张报表,而是让每一次故障都能推动系统变得更可靠,让每一次值班都不再只是消耗人。
一、On-call 不是轮流接电话,而是整套责任机制
先说一个常见误区。
有些团队觉得,On-call 很简单,不就是排个值班表嘛。
这周你值班,下周我值班。告警来了,谁值班谁处理。处理不了就找领导,领导再找开发。
表面上看,职责分工很清楚,但实际运行起来,经常是另一回事。
-
告警发出来了,却不知道应该找谁。
-
值班人员接到了告警,却没有对应系统的操作权限。
-
故障发生了,运维不知道最近谁发布过代码。
-
值班人员发现是代码问题,开发说这不是自己的模块。
-
故障好不容易恢复了,过两天同样的问题又出现一次。
这些问题,靠增加值班人数解决不了。
On-call 的核心不是安排一个人随时待命,而是建立一套从发现问题、响应问题、解决问题到持续改进的闭环。
一个相对完整的 On-call 流程,至少要明确四件事:
第一,谁负责接收告警?不能只写一个部门名称,最好明确到具体岗位、当班人员和备用联系人。
第二,接到告警后该怎么做?例如先判断影响范围、检查哪些指标、执行哪些安全操作,以及什么时候需要升级处理。
第三,什么情况需要升级?比如超过 10 分钟无人响应,或者影响范围扩大,就自动通知二线负责人。
第四,问题解决后怎么办?要有事件记录、原因分析、改进任务,而不是在群里说一句“已恢复”就算结束。
举个例子,我们可以把简单的告警升级逻辑抽象成下面这样:
Python
執行
def handle_alert(alert, oncall, backup):
notify(oncall, alert)
if not wait_for_ack(timeout_minutes=5):
notify(backup, alert)
if not wait_for_ack(timeout_minutes=10):
escalate_to_incident_commander(alert)
create_incident_record(alert)
这段代码只是示意,实际实现还需要补充事件关联、重复告警去重、通知失败处理、升级状态记录等机制。
但它体现了一个很重要的思路:不能把“我已经发过通知了”当成“有人已经开始处理了”。
告警发出去,只能说明系统完成了通知动作;有人确认接手,才意味着响应真正开始。
这两个时间点,在管理上必须分开统计。
二、MTTA 和 MTTR,别只盯着数字好不好看
既然要把 On-call 做好,就绕不开两个常见指标:MTTA 和 MTTR。
这两个指标听起来有点专业,其实理解起来并不复杂。
MTTA:平均确认时间
Mean Time to Acknowledge
从告警触发,到负责人员确认接手,平均花了多长时间。
例如,告警 02:00 触发,值班人员 02:04 确认,那么这次 MTTA 就是 4 分钟。
MTTR:平均恢复时间
Mean Time to Restore/Recover(常见用法)
从故障开始,到服务恢复正常,平均花了多长时间。
例如,02:00 开始出现服务异常,02:40 恢复,那么这次故障的恢复耗时就是 40 分钟。
这里要注意,MTTR 在不同团队里也可能指 Mean Time to Repair、Resolve 或 Recover。它们的统计终点不一定一样,所以团队首先要统一定义。本文使用的是更贴近生产运维的“平均恢复时间”。
我们假设某团队最近发生了 3 次故障:
| 故障 | 告警时间 | 确认时间 | 恢复时间 | 确认耗时 | 恢复耗时 |
|---|---|---|---|---|---|
| A | 02:00 | 02:04 | 02:40 | 4 分钟 | 40 分钟 |
| B | 10:00 | 10:02 | 10:20 | 2 分钟 | 20 分钟 |
| C | 15:00 | 15:06 | 16:00 | 6 分钟 | 60 分钟 |
那么:
-
MTTA = (4 + 2 + 6) / 3 = 4 分钟。
-
MTTR = (40 + 20 + 60) / 3 = 40 分钟。
从这组数据看,值班人员平均 4 分钟确认,故障平均 40 分钟恢复。
这时候,很多管理者可能会问:“能不能把 MTTA 从 4 分钟降到 2 分钟?把 MTTR 从 40 分钟降到 20 分钟?”
可以讨论,但我想先问一句:你想让数字变好看,还是想让用户少受影响?
这两件事并不总是一回事。
假设一个团队已经做到告警 1 分钟内确认,但每次确认之后,还需要花 30 分钟查日志、20 分钟定位代码问题、10 分钟等待发布审批。
那 MTTA 再降低 30 秒,对整体恢复效率的帮助可能非常有限。
反过来,如果把常见故障的处理手册补齐,把日志和链路追踪打通,把回滚流程自动化,MTTR 就可能得到更明显的改善。
所以我的建议是:
MTTA 主要帮助我们发现响应链路的问题,MTTR 帮助我们发现故障处理和系统恢复的问题。两者要一起看,不能只看其中一个。
还有一点,平均值很容易骗人。
假设 9 次故障都在 10 分钟内恢复,唯独 1 次严重故障持续了 5 个小时,单看平均值可能会掩盖这次长时间故障的风险。
因此,除了平均值,也建议关注 P90/P95 恢复时间、重大事故恢复时间,以及超出目标的事件比例。
指标的意义不是证明团队表现有多好,而是帮助我们找出最需要改进的地方。
三、最容易被忽略的指标:值班人员到底有多累?
如果说 MTTA 和 MTTR 关注的是故障处理效率,那么疲劳指标关注的就是另一件经常被忽视的事:人能不能长期扛得住。
这件事为什么重要?
因为人不是无限运行的服务器。
服务器 CPU 长期 95% 运行,我们会考虑扩容、限流或者优化任务。可换成值班人员,连续几周夜间被告警打断,我们却可能只说一句:“最近项目比较忙,大家辛苦一下。”
这其实很矛盾。
我们知道系统存在资源瓶颈,却常常忽视人的精力也有上限。
更现实的是,疲劳不仅影响员工体验,还可能影响判断质量。凌晨三点处理数据库连接异常,和下午三点处理同样的问题,人的注意力、反应速度以及判断稳定性未必相同。
如果一个团队经常出现夜间重复告警、连续值班、休息时间被打断等情况,就应该把这些问题当成运维风险,而不是简单归结为个人抗压能力不足。
那么,疲劳应该怎么量化?
我认为,没必要一开始就搞出一套特别复杂的健康评分系统。先把几个最有用的数据统计起来。
| 指标 | 统计方式 | 能发现什么 |
|---|---|---|
| 夜间告警次数 | 统计夜间需要人工处理的告警 | 夜间工作负担 |
| 夜间被打断次数 | 记录值班人员实际收到并处理的事件 | 睡眠被打断的频率 |
| 非计划值班时长 | 统计计划外处理生产问题的时间 | 额外工作负担 |
| 连续值班天数 | 统计连续承担值班责任的天数 | 排班是否过于集中 |
| 单人事件占比 | 统计某人承担的事件数和总事件数 | 是否过度依赖个别人 |
需要强调的是,夜间告警次数不等于夜间被打断次数。一条告警可能重复推送十次,也可能被自动化系统合并处理。真正想了解人的负担,就要尽量统计实际需要人工介入的事件,而不是简单数通知消息。
例如,我们可以先从事件记录里统计夜间人工处理次数:
SQL
SELECT
ONCALL_USER,
COUNT(*) AS NIGHT_INCIDENTS,
SUM(
CASE
WHEN UNPLANNED_MINUTES > 0
THEN UNPLANNED_MINUTES
ELSE 0
END
) AS UNPLANNED_MINUTES
FROM ONCALL_INCIDENT
WHERE INCIDENT_TIME >= @START_TIME
AND INCIDENT_TIME < @END_TIME
AND IS_MANUAL_INTERVENTION = 1
AND (
CAST(INCIDENT_TIME AS time) >= '22:00:00'
OR CAST(INCIDENT_TIME AS time) < '08:00:00'
)
GROUP BY ONCALL_USER
ORDER BY NIGHT_INCIDENTS DESC;
这段 SQL 假设事件表记录了值班人员、事件时间、是否人工介入以及计划外处理时长,字段需要根据实际数据库调整。
有了这些数据,我们就可以发现一些问题。
例如,整个团队一个月有 100 次夜间人工处理事件,其中 70 次集中在两个人身上。即使团队整体 MTTA 很漂亮,也值得进一步分析:是不是某两个系统只有这两个人熟悉?是不是排班不均衡?是不是告警升级机制有问题?
但这里还有一个容易踩的坑:不要把疲劳指标变成考核个人的工具。
如果你统计夜间处理时长,然后对夜间处理时间最长的人扣分,会发生什么?
大家可能开始避免接手事件、减少记录,甚至把实际工作时间填得更短。最后,报表确实变漂亮了,但真实问题被藏起来了。
疲劳指标更适合用于排班优化、工作量平衡、自动化建设和风险识别,而不是简单地给值班人员排名。
我们真正应该问的是:“为什么这个人需要处理这么多事件?”
而不是:“为什么这个人处理了这么多事件?”
这两种问法,背后是完全不同的管理思路。
四、别让 MTTA 越低越好,最后把人逼成告警机器人
指标一旦进入考核,就很容易发生一件事:大家开始优化指标,而不是优化问题本身。
比如,团队要求 MTTA 必须小于 2 分钟。
听起来没问题,直到某天值班人员正在处理一个复杂故障,另一个告警又弹出来了。为了不让 MTTA 超标,他先点了确认,再继续处理手里的问题。
从统计上看,这条告警已经在 30 秒内确认。
但实际上呢?
可能根本没人开始排查,真正处理还要等 15 分钟。
这就是典型的指标异化:我们把“确认”当成了“处理”,把“响应速度”误认为了“服务能力”。
因此,在设计 On-call 指标时,我建议明确区分三个时间点:
-
告警触发时间:系统什么时候发现异常。
-
人工确认时间:负责人什么时候明确接手。
-
实际介入时间:什么时候开始有效排查或执行处置。
这三个时间点不一定相同。
当然,不是每个团队都必须一开始就建设完整的事件管理平台。但至少要保证关键时间戳可追溯,并且确认动作不能被当成故障解决的证明。
另外,MTTA 和 MTTR 也不能脱离告警质量单独评价。
假如监控系统每分钟推送一条 CPU 短暂波动告警,值班人员忙着确认这些无关紧要的通知,真正的数据库故障反而被淹没了。
那么问题到底出在哪里?
是值班人员不够积极,还是告警阈值设置不合理?
很显然,后者至少值得优先排查。
所以,除了 MTTA、MTTR 和疲劳指标,我还建议增加一个基础指标:有效告警占比。
Python
執行
def alert_quality(alerts):
total = len(alerts)
if total == 0:
return {
"total": 0,
"actionable": 0,
"actionable_rate": 0
}
actionable = sum(
1 for alert in alerts
if alert["requires_action"]
)
return {
"total": total,
"actionable": actionable,
"actionable_rate": round(
actionable / total * 100, 2
)
}
这里的 requires_action 应该有清晰的判定规则,例如是否需要人工介入、是否对应明确的风险或处置动作,而不是简单地以“最终是否发生故障”作为标准。
有些提前发现的异常,即使最后没有造成事故,也可能是有效告警。
反过来,一条反复触发、无需任何处置的通知,即使被及时确认,也不一定有实际价值。
好的告警不是越多越好,而是该叫人的时候叫人,不该打扰人的时候尽量别打扰。
如果一个团队能通过告警去重、阈值优化、事件聚合和自动恢复,把夜间无效干扰降低一半,那么这可能比要求所有人把 MTTA 再缩短一分钟更有意义。
五、把 On-call 做成闭环:从一次事故里真正赚到经验
还有一个现象,我相信不少团队都见过。
某次生产故障解决之后,大家在群里讨论了半天,最后总结出三条:
-
加强监控。
-
提高警惕。
-
避免再次发生。
这些话当然没错,但问题是,具体怎么做?
没有负责人,没有完成时间,没有验收标准。过了两周,大家已经忘了这件事,直到同样的问题再次出现。
这不叫复盘,更像是给事故写了一份感想。
真正有用的复盘,应该把故障转化成可以执行的改进任务。
例如:
| 问题 | 改进动作 | 验收方式 |
|---|---|---|
| 连接池耗尽才发现异常 | 增加连接池使用率及等待队列告警 | 模拟压力测试,验证告警提前触发 |
| 故障定位耗时过长 | 增加请求链路追踪与关键日志 | 演练中能定位到具体异常调用 |
| 回滚需要手动操作多个步骤 | 编写并验证自动化回滚流程 | 在测试环境完成回滚演练 |
| 夜间重复告警太多 | 增加告警去重、聚合和抑制规则 | 对比优化前后的人工介入事件数 |
这里还有一个我认为特别重要的原则:复盘要关注系统为什么允许问题发生、为什么没有更早发现、为什么不能更快恢复,而不是急着找一个人背锅。
当然,这不意味着任何操作失误都不需要追责。
如果有人明知操作存在重大风险,故意绕过必要的审批或安全措施,当然需要按制度处理。
但绝大多数常规故障,如果最后只得出“操作人员不够细心”,那团队其实没有学到多少东西。
我们更应该追问:
-
为什么这项危险操作没有防护?
-
为什么操作前没有校验?
-
为什么没有经过验证的回滚方案?
-
为什么一个人就能执行影响整个生产环境的操作?
-
为什么系统没有自动发现并限制风险?
这些问题,才能真正推动运维能力提升。
而且,复盘的结果一定要回到指标里验证。
比如,某类故障之前的 MTTR 是 60 分钟,增加自动化恢复流程之后,经过多次可比事件和演练,发现恢复耗时明显下降,那么这项改进就有了实际价值。
如果只完成了文档编写,却没有验证效果,那我们只能说任务做完了,不能说问题解决了。
六、真正落地,别一上来就搞一套复杂的考核体系
如果你现在所在的团队,还没有成熟的 On-call 管理机制,我不建议一上来就引入十几个指标,再规定每个指标必须达到什么标准。
这样做看起来很专业,实际执行起来却容易变成填表工作。
更靠谱的方式,是分阶段推进。
第一阶段:先把账记清楚
建议周期:第 1~2 周
统一事件定义,记录告警时间、确认时间、开始处理时间、恢复时间、人工介入情况和责任班次。先收集基线数据,不急着排名。
第二阶段:找到最影响人的问题
建议周期:第 3~4 周
分析 MTTA、MTTR、夜间人工介入次数、重复告警率和事件分布,先处理最常见、最耗时、最容易造成疲劳的问题。
第三阶段:用工程手段减少重复劳动
建议周期:第 2 个月
优化告警规则,补齐故障手册,建设自动化诊断和恢复流程,明确升级机制,并让常见故障有标准处置路径。
第四阶段:持续复盘,检查改进有没有效果
长期运行
每周关注高频事件,每月检查疲劳趋势、恢复时间分布和改进任务完成情况。对重大故障单独复盘,不要让平均值掩盖个别严重问题。
至于目标值怎么定,我建议先观察团队自身的历史数据,再结合业务影响、服务等级目标和实际人员配置制定。
不要看到别的公司要求 MTTA 小于 5 分钟,就直接照搬。
一个面向内部测试环境的服务,和一个面向大量客户的核心交易系统,对响应时限的要求本来就不应该一样。
同样,夜间每月发生两次人工介入,和每晚都需要值班人员处理十几次事件,也完全是两种不同的情况。
指标要服务于业务风险,而不是为了让管理制度看起来先进。
七、最后说点实在的:别把人的牺牲,当成系统稳定的证明
我一直觉得,运维这个行业有一个挺容易被忽视的问题。
我们太习惯于表扬那些能扛事的人了。
生产出了问题,有人第一时间爬起来处理,我们说他责任心强;系统连续出故障,有人连续几晚不睡,我们说他经验丰富;一个人熟悉所有系统,任何问题都能找到他,我们说他是团队里的核心骨干。
这些评价并不是没有道理。
但如果换一个角度看,一个人必须随时在线,才能让生产环境稳定;一个团队必须靠几个人长期透支,才能维持正常运转,这真的是值得骄傲的事情吗?
我觉得未必。
真正优秀的运维,不是让人永远顶在系统前面,而是让系统逐渐不需要人永远顶在前面。
On-call 文化建设也是一样。
MTTA 告诉我们,问题发生后,责任链路能不能快速接上。
MTTR 告诉我们,故障发生后,系统能不能尽快恢复。
疲劳指标则提醒我们,在追求稳定和效率的过程中,有没有把成本全部转嫁给值班人员。
这三类指标放在一起,才更接近一套健康的运维管理体系。
如果 MTTA 很低,但 MTTR 长期居高不下,应该检查定位、处置和恢复能力。
如果 MTTR 不错,但夜间人工介入越来越频繁,就应该检查告警质量、自动化能力和排班机制。
如果所有数字都很好看,值班人员却越来越疲惫,那也要警惕:是不是统计口径掩盖了真实问题?
最后,我想把一个观点留给正在做运维管理、平台建设或者 SRE 实践的朋友:
别只统计故障处理得有多快,也要统计团队为了处理这些故障,付出了多少额外成本。
因为系统稳定的最终目的,不是让监控大盘永远绿色,也不是让 MTTA 和 MTTR 永远下降。
而是让业务可以放心运行,让用户少受影响,让团队有能力持续改进,也让那些负责保障系统稳定的人,能够在下班之后安心睡个好觉。
如果哪一天,一个团队不再需要依靠某个人凌晨三点爬起来救火,生产系统依然能够及时发现异常、自动完成部分恢复,并在必要时准确通知真正需要介入的人。
那么,我觉得这才是 On-call 导向文化真正建立起来的样子。
不是没人负责,而是责任更加清晰。
不是不再救火,而是每次救火之后,都努力让下一次火更少一点。
- 点赞
- 收藏
- 关注作者
评论(0)