概述
你公司用了某云厂商的服务,SLA 白纸黑字写着 99.95% 月度可用性。某天凌晨服务挂了 30 分钟,你跑去找云厂商索赔,拿回来的赔偿大概是这么算的:故障时间占月度总分钟数的比例,乘以你当月的服务费。假设你月付 1000 块,30 分钟除以 43200 分钟再乘 1000,赔你 0.7 元。
没看错,七毛钱。你线上炸了半小时,客服电话被打爆,用户退款申请堆了一屏,云厂商赔你七毛。
这不是段子,这是 2025 年某 CSDN 技术博客拆解云厂商 SLA 时用的真实计算逻辑。当然,实际云厂商的赔偿用的是阶梯制(后面会讲),但核心问题不变:SLA 的数字看着唬人,赔偿条款却藏着大量排除项和窄化定义,到了真出事的时候,赔的那点钱远远覆盖不了你的业务损失。
这篇文章不聊怎么"读懂"SLA——那种文章网上一搜一大把。我要聊的是:作为 SRE,你怎么设计自己服务的 SLA,怎么避免云厂商 SLA 的坑,怎么把一个合同条款变成可执行的工程约束。
SLA、SLO、SLI:别再混为一谈
很多团队把这三个词当同义词用。开会时"我们的 SLA 是 99.9%",实际上说的是 SLO。这三个东西的区别不是文字游戏,搞混了会出真问题。
先上一张对比表,后面逐个展开:
| 维度 | SLI(指标) | SLO(目标) | SLA(协议) |
|---|---|---|---|
| 是什么 | 对服务质量的量化测量值 | 基于 SLI 设定的内部目标值 | 对外承诺的合同条款 |
| 面向谁 | 工程团队 | 工程团队 + 产品 | 客户 / 业务方 |
| 违约后果 | 无 | 冻结发布、投入稳定性修复 | 赔偿、合同违约 |
| 严格程度 | 客观事实,无严格/宽松之分 | 必须严于 SLA | 比 SLO 宽松,留缓冲 |
| 典型例子 | P99 延迟 = 120ms | P99 延迟 < 200ms,成功率 > 99.9% | 月度可用性 > 99.5%,否则赔 10% |
SLI:你到底在测什么
SLI 是纯粹的客观数据。它回答的问题是"我们实际观察到了什么"。比如:
- 请求成功率:成功请求数 / 总请求数
- 延迟分位数:P99 响应时间
- 可用性:正常服务时间 / 总时间
SLI 的定义看似简单,实际上坑很深。举个例子,“成功率"怎么定义?HTTP 200 算成功?那 HTTP 200 但返回了错误数据的呢?HTTP 500 但 50ms 后重试成功了的呢?SLI 的定义精度直接决定了后面 SLO 和 SLA 是否有意义。
Google SRE Book 里有个经典观点:SLI 应该尽可能接近用户体验。不是测你的服务器 CPU 利用率,而是测用户发起请求到收到响应的全链路延迟。这个原则在后面设计 SLA 监控时会反复用到。
举个具体的例子。一个支付 API,如果你把 SLI 定义为"API 进程存活的分钟数 / 总分钟数”,那进程活着但返回 500 的 45 分钟就不算故障。但用户在这 45 分钟里确实付不了款。用户不关心你的进程活没活,只关心请求成不成功。所以正确的 SLI 应该是"成功请求数 / 总请求数"——从用户实际发起的请求维度测量,而不是从服务端进程维度测量。
再进一步。如果 API 响应了但延迟从 50ms 飙到 8 秒,用户大概率会超时放弃。这种情况成功率 SLI 可能抓不到(请求最终成功了,只是慢),但用户体验已经受损。所以延迟也应该是一个 SLI 维度。完整一点,一个服务的 SLI 至少覆盖三个维度:可用性(成功率)、延迟(P99/P999 响应时间)、吞吐量(处理能力上限)。三个维度的权重按业务特点分配,不是一刀切。
举两个对比。一个电商首页:延迟的权重应该最高——用户等 3 秒就走了,成功率反而还好(首页本身不太会失败)。一个支付接口:成功率的权重应该最高——慢一点可以接受,但付不了钱是致命的。一个日志写入服务:吞吐量权重最高——单条日志延迟 2 秒无所谓,但每秒写不完 10 万条就是事故。SLI 的权重不是技术决策,是业务决策。产品经理和 SRE 坐在一起,用业务场景倒推 SLI 权重,不是 SRE 自己拍。
SLO:你的内部目标
SLO 是团队给自己设的标尺。比如"30 天内,99.9% 的请求 P99 延迟低于 200ms"。SLO 不对外公布,不涉及赔偿,但它是工程治理的核心工具——错误预算就是从 SLO 算出来的。
关于 SLO 的详细设计,可以参考我之前写的 SLO 设计实战 和 SLI、SLO 与错误预算。这里只强调一个核心原则:
SLO 必须严于 SLA。
这是 Google SRE 的铁律。如果你对外承诺 99.9%,内部 SLO 至少要设到 99.95% 甚至 99.99%。为什么?因为你需要缓冲。故障从发生到被检测、到确认、到恢复,中间有大量时间窗口。如果 SLO 和 SLA 持平,意味着只要 SLI 稍有波动就会触发对外违约,你连反应时间都没有。
举个数字感受一下。SLA 承诺 99.9%,月度允许不可用 43 分钟。如果 SLO 也设 99.9%,你只有 43 分钟的余量。一次 30 分钟的故障就吃掉了 70% 的预算,接下来的 20 多天里只能有 13 分钟的余量。任何一个轻微抖动都可能触发 SLA 违约。但如果 SLO 设 99.95%,你的内部预算只有 21.5 分钟,一次 30 分钟的故障会先触发 SLO 违约(冻结发布),但离 SLA 违约还有 21.5 分钟缓冲。这 21 分钟就是你的应急窗口——足够做一次回滚或切换。
Chris Jones 和 Niall Murphy 在 2016 年 SREcon 上说过一句话:SLA isn’t the right tool for SREs to manage a service。SRE 管理服务靠的是 SLO 和错误预算,SLA 只是法务和商务层面的兜底。这句话当时听着像异端,现在看是共识。
多说一句:在很多公司里,SLA 是商务团队谈的,SLO 是工程团队设的,两者之间没人对接。商务团队为了签单把 SLA 承诺拉高到 99.99%,工程团队根本不知道这个数字意味着什么,还在按 99.9% 的 SLO 做容量规划。等出事了才发现承诺和能力之间差了一个数量级。SLA 和 SLO 必须由同一个机制管理——商务团队承诺之前,必须拿工程团队的 SLO 数据做依据;工程团队改 SLO 时,必须评估对 SLA 承诺的影响。
SLA:合同层面的事
SLA 是服务提供方和客户之间的正式协议。它规定了:承诺什么指标、达不到怎么办(通常是服务额度抵扣)、哪些情况不算违约(排除条款)。
SLA 条款的严格程度通常低于 SLO。这不是偷懒,而是务实的缓冲策略——内部 SLO 是你追求的标尺,外部 SLA 是你兜底的底线。两者之间的差距就是工程团队的应急空间。
关键区别在于后果。SLO 没达到,工程团队停下来修 bug;SLA 没达到,法务团队开始算赔偿。一个是工程动作,一个是合同动作。
Azure 官方文档对这两者的关系有一个很好的总结:SLA 描述了服务提供商承诺的最低保证,SLO 反映了用户实际需要的可靠性。别简单照搬 SLA 的百分比作为你服务的可靠性目标,要考虑你自己的代码、依赖项和用户容忍度。
云厂商的 SLA 赔偿细则:赔的钱能买回损失吗
大部分云厂商的 SLA 赔偿不是发现金,而是"服务额度"——抵扣你未来的账单。这一点首先搞清楚:你拿不到钱,只拿到一张代金券。
阶梯制赔偿:比你想的更鸡肋
主流云厂商一般用阶梯制计算赔偿额度。下面是一个典型结构(具体数值因服务和厂商而异,仅示意):
| 月度可用性 | 赔偿比例 | 说明 |
|---|---|---|
| < 99.9% | 10% | 低于承诺值 0.1% |
| < 99.0% | 25%-30% | 严重低于承诺值 |
| < 95.0% | 50%-100% | 灾难级故障 |
乍一看还合理?仔细想想:假设你月付 5000 块买某个云服务,SLA 承诺 99.9%。某月服务实际可用性是 99.8%,低于 99.9% 但远高于 99.0%,你拿到 10% 赔偿 = 500 块代金券。
但这 99.8% 意味着什么?一个月 43200 分钟里有 86 分钟服务不可用。86 分钟的线上故障,如果是电商,可能是几十万订单流失;如果是金融,可能是百万级交易中断。你拿到 500 块代金券,而且只能在下个月的账单里抵扣。
赔偿公式的猫腻
更隐蔽的问题在计算方式上。有的云厂商不是按"低于承诺值多少"来算,而是按"故障时间占比"来算。这就是开头那个 0.7 元的算法:故障时间 / 月度总分钟 × 月费 = 赔偿额。这种算法对短时高频故障特别不友好——每次挂 5 分钟,挂 10 次,总共 50 分钟故障,但每次都没达到"最小索赔门槛"(后面会讲),可能一分钱都赔不到。
AWS 在其官方博客中也明确提到:SLA 是对客户的承诺,包括未达标时的补救措施。但补救措施是"服务抵扣"不是"损失赔偿"。Microsoft 的可靠性架构指南更直白:Be Redundant,design to overcome failure,别指望 SLA 赔偿覆盖你的业务损失,把故障设计进系统里。
我推荐的做法
别把云厂商的 SLA 当你的业务保险。它连你业务损失的零头都覆盖不了。正确的做法是:
- 明确你的业务对中断的真实容忍度(按分钟算损失,不是按百分比)
- 基于业务容忍度反推你需要的服务可靠性等级
- 如果云厂商的 SLA 低于你的需求,用架构补——多可用区、跨区域容灾、服务降级方案
- 云厂商 SLA 只作为"选型参考"和"出事后的法务兜底",不作为业务可靠性的唯一依赖
补充一个实际数据感受:AWS 2017 年 us-east-1 的大规模故障持续了数小时,影响了 Netflix、Pinterest 等大量客户。根据当时的报道,一些客户选择起诉 AWS 违反 SLA,最终 AWS 同意向受影响客户支付了一定额度赔偿。但这个"一定额度"相比这些客户因服务中断造成的实际损失(用户流失、收入下降、品牌受损),只是杯水车薪。这就是为什么 Microsoft 的可靠性指南说"拥抱故障,设计来克服它"——别指望 SLA 赔偿兜底。
复合 SLA 的数学:依赖链上的故障概率怎么算
如果你的服务依赖三个云服务,每个单独看 SLA 都不错,组合在一起会怎样?这里有个容易踩的数学坑。
串行依赖:乘法法则
假设你的服务架构是:API 网关 → 计算服务 → 数据库,三个服务串行依赖。任何一个挂了,你的服务就挂了。复合 SLA 的计算方式是三个独立 SLA 相乘:
复合SLA = SLA_网关 × SLA_计算 × SLA_数据库
代入实际数字:
| 组件 | 单独 SLA | 年允许停机 |
|---|---|---|
| API 网关 | 99.95% | 4.38 小时 |
| 计算服务 | 99.95% | 4.38 小时 |
| 数据库 | 99.99% | 0.88 小时 |
| 复合 SLA | 99.89% | 9.59 小时 |
三个 99.95% 级别的服务串在一起,复合 SLA 只有 99.89%。如果你对外承诺 99.9%,你会发现你的依赖链本身就达不到。
Microsoft Azure 的云采用框架文档中有一个公式可以估算年故障时间:
Est. Outage = (1 - Composite_SLA) × 8760 hours
99.89% 的复合 SLA 对应每年约 9.59 小时停机。这意味着即使每个组件单独看都"不错",组合起来的可靠性可能比你预期的差很多。
并行冗余:加法法则
如果你做了冗余设计(比如多可用区部署),故障概率的计算方式变了。两个独立的 99.9% 服务并行,只有两个同时挂才算故障:
复合SLA = 1 - (1 - SLA_1) × (1 - SLA_2)
= 1 - 0.001 × 0.001
= 1 - 0.000001
= 99.9999%
这就是为什么多可用区部署能显著提升可靠性。但前提是两个实例真正独立——不共享控制平面、不共享网络入口、不共享认证服务。如果共享了,独立性假设就不成立,复合 SLA 的数学也就不成立。
每个"9"的成本是指数级的
从 99% 到 99.9%,不可用时间从 87.6 小时/年降到 8.76 小时/年——10 倍改善。从 99.9% 到 99.99%,再降到 0.876 小时/年——又是 10 倍。但每一倍改善的工程成本不是线性的。
| 可用性 | 年允许停机 | 月允许停机 | 相对工程成本 | 典型架构 |
|---|---|---|---|---|
| 99% | 87.6 小时 | 438 分钟 | 1× | 单实例 + 自动重启 |
| 99.9% | 8.76 小时 | 43.8 分钟 | 3-5× | 多实例 + 负载均衡 |
| 99.99% | 52.6 分钟 | 4.38 分钟 | 10-20× | 多可用区 + 自动故障转移 |
| 99.999% | 5.26 分钟 | 0.44 分钟 | 50-100× | 多区域 + 同城双活 + 全球流量调度 |
每个"9"往上走,投入的工程资源大约翻 3-5 倍。99.99% 到 99.999% 不是"再努力一下",是从"多可用区"升级到"多区域全球调度",是数量级的架构变化。
做 SLA 决策时先问自己:多一个"9"值不值这个钱?99.99% 意味着一个月只允许停机 4.38 分钟。一次发布回滚可能就超了。你的业务真的需要这么高的可用性吗?大部分业务 99.9% 已经够用了。
实际教训
我见过一个团队,用 AWS 的 API Gateway + Lambda + DynamoDB 搭服务,三个服务的 SLA 分别是 99.95%、99.95%、99.99%。他们算了一下复合 SLA 是 99.89%,觉得"差不多 99.9%,够用了"。结果某次 AWS us-east-1 区域性故障,三个服务同时挂——因为全部署在同一个区域。他们以为的"独立故障"实际上是同一个底层基础设施。
教训:复合 SLA 的数学只在组件真正独立时成立。跨区域部署的成本比单区域高,但换来的是独立性假设的有效性。
真正的冗余:独立性验证清单
做了多可用区或多区域部署,不等于获得了真正的独立性。下面这个清单可以帮你验证:
# 独立性验证清单
independence_checklist:
control_plane:
- question: "各区域的控制平面是否独立?"
risk: "共用控制平面的多区域,控制面故障时全部挂"
check: "确认各区域有独立的 API Server / 控制节点"
data_replication:
- question: "数据复制是同步还是异步?"
risk: "同步复制可能因网络分区导致写入超时"
check: "核心数据异步复制 + 最终一致性,非核心数据可同步"
failover_mechanism:
- question: "故障切换是自动还是手动?"
risk: "手动切换的 RTO 可能超过 SLA 承诺"
check: "DNS 健康检查 + 自动切换,RTO < 5 分钟"
capacity:
- question: "单区域能否承载全部流量?"
risk: "切流后单区域过载导致级联故障"
check: "每个区域容量规划按 150% 峰值流量设计"
shared_dependencies:
- question: "是否有共享的认证、DNS、CDN 等基础服务?"
risk: "共享服务挂了,多区域也白搭"
check: "认证服务多区域部署,DNS 使用多个服务商"
这份清单不能只填一次。每次架构变更后都要重新检查——新增了一个微服务?它依赖的东西是不是引入了新的共享点?复杂数据库加了读写分离?写库和读库是不是在同一个可用区?
SLA 条款里 6 个容易忽略的坑
云厂商的 SLA 文档通常几十页,大多数人只看首页的可用性百分比就签了。真正的坑都在后面。
坑 1:排除条款——这些情况宕机了也不赔
几乎所有 SLA 都有一段排除条款,列出不计算在内的情况:
- 计划内维护:云厂商提前通知的维护窗口不算不可用时间。问题是"提前多久通知"“一个月能做几次"“窗口多长"这些细节,有的 SLA 根本不写。实际操作中,云厂商宣布"今晚维护两小时"然后这两小时就不算 SLA,你无可奈何。
- 不可抗力的宽泛解释:有的 SLA 把"第三方网络供应商问题"也归为不可抗力。骨干网抖动导致用户访问不了,算不算不可抗力?条款模糊就有操作空间。
- 客户侧问题:你误删了实例、配置错了安全组、应用有 bug 导致资源耗尽——这些当然不算云厂商的。但"客户侧"的边界在哪?如果云厂商的某个组件有 bug 导致你被迫做了错误配置,算谁的?
坑 2:“不可用"的定义比你以为的窄
大部分 SLA 对"不可用"的定义是:所有请求持续失败超过 X 分钟。注意三个关键词:所有、持续、失败。
- “所有"意味着部分用户访问不了不算——只有全部用户都不行才算。
- “持续"意味着间歇性故障不算——每次挂 3 分钟恢复 2 分钟再挂 3 分钟,如果单次不连续超过 X 分钟,可能达不到索赔门槛。
- “失败"通常指 HTTP 5xx 或连接超时。你的 API 返回 503 但不是 500?可能不算。你的 API 响应从 50ms 飙到 10 秒但没完全挂?在绝大多数 SLA 里不算"不可用”。
坑 3:最小索赔门槛
很多 SLA 规定单次故障必须超过一定时长才够索赔资格。比如"单次故障持续时间超过 15 分钟才可申请赔偿”。如果故障 14 分钟恢复了,即使影响了可用性百分比,也无法索赔。
这个门槛设计看似合理(避免微小抖动触发大量索赔),但实际效果是:高频短时故障成了 SLA 盲区。每次挂 10 分钟,挂 5 次,总共 50 分钟故障,但一次都没达到 15 分钟门槛,拿不到一分钱赔偿。
坑 4:监控口径偏差
SLA 里写的可用性通常以"云厂商监控系统的数据"为准。问题是你和云厂商的监控口径可能完全不同。
你的监控从用户视角测量——模拟真实用户请求,从多个地域拨测,统计成功率。云厂商的监控从服务端测量——检查服务进程是否存活,端口是否可达。服务进程活着但响应全错的情况,你的监控显示故障,云厂商的监控显示正常。
这个偏差是真实发生过的。某团队用了 AWS API Gateway,某次配置更新导致 30% 的请求返回 503。从 AWS 的监控看,API Gateway 实例是活着的、端口是通的,所以"服务可用”。从团队自己的监控看,30% 的用户请求失败了 45 分钟。索赔?不通过,因为 AWS 的 SLA 定义不认这个。
坑 5:赔偿上限
大部分 SLA 的赔偿有上限——通常不超过当月服务费。你月付 5000 块,最多赔 5000 块(还得是代金券)。你的业务因为故障损失了 50 万?跟 SLA 赔偿没关系。
这个上限意味着云厂商的风险是封顶的,而你的风险是敞口的。理解这一点,你就知道为什么不能依赖 SLA 做业务保险。
坑 6:通知和索赔时效
SLA 通常要求客户在故障发生后一定期限内(比如 30 天或 60 天)提交索赔申请,过期作废。有的还要求你提供"故障证据”——你自己的监控日志、影响范围说明等。
如果故障发生后你的团队都在救火,没人想着在 30 天内整理证据提交索赔,等忙完了回头去找云厂商,可能已经过了时效。
我的建议:把"SLA 索赔"作为故障恢复流程的固定步骤。不是每次故障都索赔——但在事后复盘清单里加一项"本次故障是否影响 SLA?如果是,在 X 天内提交索赔”。用自动化脚本在故障恢复时自动生成索赔材料(时间线、影响范围、监控截图),存在工单系统里,等复盘时一并提交。
另一个常见问题是:云厂商要求你用他们的渠道提交索赔(工单系统、支持案例),不认邮件或口头通知。如果走错了渠道,可能被认定为"未在规定期限内正式提交”。签 SLA 时确认索赔流程的具体步骤和渠道,别到时候才发现走了弯路。
还有一个容易忽略的点:多租户场景下的 SLA 计算。如果你的服务是多租户的(一个实例服务多个客户),某个客户的异常行为(比如无限流的大查询)导致其他客户受影响,这种"部分降级"在 SLA 里怎么算?有的合同规定只有"所有租户同时不可用"才算 SLA 违约,部分租户受影响不算。这和云厂商的"全部请求失败才算不可用"是同一套逻辑,对你不利。多租户服务在设计 SLA 时,应该按租户级别定义——“每个租户的月度可用性不低于 X%",而不是笼统的"服务可用性不低于 X%"。
SRE 视角的内部 SLA 设计:6 个工程决策
前面讲了云厂商 SLA 的坑。现在聊你自己的服务怎么设计 SLA。不是签合同层面的事,是工程层面的事。
决策 1:SLA 数值怎么定——参考用户容忍度,别照搬
最常见的错误是:看了云厂商的 SLA 是 99.9%,自己的 SLA 也写 99.9%。这叫照搬,不叫设计。
SLA 数值应该从业务容忍度反推。问三个问题:
- 用户能容忍多长时间的中断?电商用户可能容忍 5 分钟,金融交易用户可能只容忍 30 秒。
- 中断的直接经济损失是多少?按分钟算损失金额,不是拍脑袋说"大概很多”。
- 中断的间接损失(商誉、用户流失)怎么量化?可以参考历史故障的用户流失数据。
算出来之后,把容忍度反推成 SLA 百分比。比如用户能容忍月度停机 43 分钟(约 0.03% 不可用),SLA 就是 99.9%。但别忘了前面讲的缓冲原则——你的 SLO 要比 SLA 更严。所以 SLO 设 99.95%,SLA 设 99.9%。
# SLA 数值推算工具(示意)
def calculate_sla(tolerable_downtime_minutes_per_month, buffer_ratio=0.5):
"""
根据业务容忍度反推 SLA 数值
Args:
tolerable_downtime_minutes_per_month: 业务可容忍的月度停机分钟数
buffer_ratio: SLO 比 SLA 严格的倍数,0.5 表示 SLO 错误预算是 SLA 的一半
Returns:
(sla_percentage, slo_percentage, monthly_error_budget_minutes)
"""
total_minutes = 30 * 24 * 60 # 43200
sla_downtime = tolerable_downtime_minutes_per_month
sla_percentage = (1 - sla_downtime / total_minutes) * 100
# SLO 比 SLA 严 buffer_ratio 倍的错误预算
slo_downtime = sla_downtime * buffer_ratio
slo_percentage = (1 - slo_downtime / total_minutes) * 100
error_budget = sla_downtime - slo_downtime # SLA 和 SLO 之间的缓冲
return sla_percentage, slo_percentage, error_budget
# 示例:业务容忍月停机 43 分钟
sla, slo, buffer = calculate_sla(43)
print(f"SLA: {sla:.2f}% | SLO: {slo:.2f}% | 缓冲: {buffer:.1f} 分钟")
# 输出: SLA: 99.90% | SLO: 99.95% | 缓冲: 21.5 分钟
决策 2:SLA 违约的"赔偿"机制——内部也要有
外部 SLA 违约赔钱,内部 SLA 违约赔什么?赔工程资源。
具体做法:当 SLO(内部目标)被违反但 SLA(对外承诺)还没被违反时,触发"冻结发布"机制——所有非紧急变更暂停,工程团队集中精力修稳定性问题,直到错误预算恢复。这是我之前在错误预算耗尽还在催发版里详细讲过的机制。
如果 SLA 本身被违反(对外违约),那就不只是冻结发布的问题了——需要触发事后复盘、影响评估、客户沟通流程,甚至可能需要法务介入。
关键点是:SLA 违约不能没有工程后果。如果违约了只是写个报告就过去了,SLA 就只是一行数字,不会产生任何行为改变。
决策 3:SLA 监控指标——从用户视角定义
这是前面"坑 4"的反面。你自己设计 SLA 时,“不可用"的定义必须从用户视角出发,不能只看服务端。
Google SRE 的原则是:SLI 应该尽可能接近用户体验。具体怎么做?
# SLA 监控定义示例(Prometheus + Sloth 风格)
version: "prometheus/v1"
service: "payment-api"
slos:
- name: "payment-api-availability"
objective: 99.9
description: "支付 API 月度可用性,从用户视角测量"
sli:
events:
total: "sum(rate(http_requests_total{job='payment-api'}[5m]))"
errors: "sum(rate(http_requests_total{job='payment-api', status=~'5..'}[5m]))"
# 注意:这里用的是 http_requests_total,而不是 process_up
# process_up 只测进程存活,不测用户请求是否成功
# http_requests_total 测的是用户实际发起了多少请求,其中多少失败了
- name: "payment-api-latency"
objective: 99.0
description: "P99 延迟 < 500ms,覆盖 99% 的请求"
sli:
events:
total: "sum(rate(http_request_duration_seconds_count{job='payment-api'}[5m]))"
errors: "sum(rate(http_request_duration_seconds_bucket{job='payment-api', le='0.5'}[5m]))"
# 延迟 SLI:超过 500ms 的请求算"不达标"
# 这个定义比"服务进程存活"更接近用户体验
# 因为用户不关心你的进程活没活,只关心请求成功不成功、快不快
有两个关键点:
- 用请求成功率,不用进程存活率。进程活着但请求全返回 500,对用户来说就是挂了。
- 从多个地域拨测。某个地域的网络抖动只影响该地域用户,从服务端看可能是"局部问题”,从用户看就是"服务不可用"。
我实测发现,从用户视角定义的 SLI 比从服务端定义的 SLI,故障检出率高出约 30%。因为大量故障是"服务活着但用户体验受损"的灰度故障,服务端监控根本看不到。
决策 4:SLA 评审周期——按业务变化速度定
SLA 不是签了就一劳永逸。服务在变、依赖在变、用户群体在变,SLA 也要定期评审。
评审周期取决于你的业务变化速度:
| 业务类型 | 建议评审周期 | 原因 |
|---|---|---|
| 快速迭代型(SaaS、互联网产品) | 每季度 | 架构和依赖频繁变化,SLA 可能过时 |
| 稳定型(内部系统、传统企业应用) | 每半年 | 变化少,但需要定期校准 |
| 合同约束型(金融、医疗) | 每年 | 合同期内稳定,但需对标行业基准 |
评审时看三个数据:
- SLI 实际表现 vs SLO 目标:如果连续 3 个月 SLI 远超 SLO(比如 SLO 是 99.9% 但实际跑到了 99.99%),说明 SLO 设低了,可以适当提高;如果连续接近违约线,说明 SLO 设高了,要么降目标要么投入资源补能力。
- 错误预算消耗趋势:是匀速消耗还是集中消耗?匀速说明正常波动,集中消耗说明有系统性问题。
- 用户反馈:有没有用户投诉了但 SLI 显示正常的情况?这说明 SLI 定义有盲区。
还有一种评审信号容易被忽略:发布频率和错误预算的关系。如果你发现每次发布后错误预算都会跳一下——不是大跳,是小幅消耗——说明发布质量不稳定。这种小消耗平时不触发告警,但累积起来可能在月底把预算吃光。把每次发布的错误预算变化量记下来,按周聚合,趋势线向下走说明发布质量在改善,横盘或向上走说明需要加测试门禁。
决策 5:SLA 文档化——用代码管理,不是 Word
SLA 文档不应该是某个角落里的 Word 文件,而应该是可执行的代码。用 YAML 或 TOML 定义 SLA,配合工具自动生成监控规则和告警策略。
# sla-definition.yaml — 用代码管理 SLA
apiVersion: sre.example.com/v1
kind: ServiceLevelAgreement
metadata:
name: payment-api-sla
effectiveDate: "2026-09-01"
reviewCycle: quarterly
spec:
service: payment-api
# 对外承诺
externalCommitments:
availability: 99.9
measurementWindow: 30d
creditPolicy:
tiers:
- below: 99.9
creditPercent: 10
- below: 99.0
creditPercent: 30
- below: 95.0
creditPercent: 100
maxCredit: monthlyFee
currency: serviceCredit # 不是现金
exclusions:
- plannedMaintenance # 需提前 48h 通知
- customerMisconfiguration
- forceMajeure
claimDeadline: 30d
measurementSource: customerFacingProbe # 从用户视角测量,不是服务端
# 内部目标(必须严于对外承诺)
internalSLO:
availability: 99.95
latencyP99: 500ms
latencyP99Objective: 99.0
errorBudgetPolicy:
freezeDeployment: true # 错误预算耗尽自动冻结发布
burnRateAlerts:
- threshold: 14.4 # 1h 消耗 1 天预算
severity: critical
- threshold: 3.6 # 6h 消耗 1 天预算
severity: warning
这种做法的好处是:SLA 定义和监控系统是一体的,改了 YAML 就自动生效监控规则。不用人在两个系统之间同步,减少遗漏。
但光有定义文件不够,还需要一套审计流程。每个季度跑一次 SLA 合规审计:检查 SLA 定义文件是否和实际监控系统一致(有没有改了 YAML 但没部署的情况)、排除条款是否被滥用(计划内维护是不是被当成挡箭牌用了太多次)、赔偿记录是否完整(有没有违约了但没走赔偿流程的情况)。审计结果写入季度 SRE 报告,由 SRE 负责人签字确认。这不是走形式——审计的价值在于发现定义和执行之间的裂缝。我见过一个团队 SLA 文件写着"维护窗口需提前 48 小时通知",但实际操作中维护通知经常只提前 4 小时。审计时发现这个裂缝后,要么改流程执行(真的提前 48 小时),要么改 SLA 定义(承认做不到就调整承诺)。两种方向都行,但不能装看不见。
决策 6:SLA 和 SLO 之间的缓冲设计
前面反复提到"SLO 比 SLA 严",但具体严多少?缓冲怎么设?
这里有个实际的工程决策。缓冲太大,工程团队压力过大,错误预算很快耗尽,频繁冻结发布,业务方不满。缓冲太小,SLA 违约风险高,法务和商务团队不满。
我推荐的做法是:SLO 的错误预算是 SLA 错误预算的 50%。也就是说,如果 SLA 允许月度停机 43 分钟(99.9%),SLO 只允许 21.5 分钟(99.95%)。中间的 21.5 分钟就是缓冲——当 SLO 违约时,还有 21.5 分钟的余量在 SLA 违约之前,工程团队有时间恢复。
这个 50% 不是拍脑袋的。Datadog 在其错误预算分析文章中提到,错误预算的定义是 1 减去 SLO 目标值。如果 SLO 是 99.9%,错误预算就是 0.1%。当 SLA 也是 99.9% 时,两者持平没有缓冲。把 SLO 提到 99.95%,错误预算变成 0.05%,正好是 SLA 错误预算的一半。
当然,具体比例要按业务场景调整。对延迟敏感的业务(如实时交易),缓冲可以更大(SLO 错误预算是 SLA 的 30%);对容忍度高的业务(如内部工具),缓冲可以小一些(70%)。
缓冲设太大会怎样?SLO 非常宽松,错误预算充裕,团队永远不触发"冻结发布"机制。结果是 SLO 形同虚设——大家觉得"反正不会超",稳定性投入意愿降低。等真的出了一次大故障,SLI 直接砸穿 SLA 线,中间的缓冲完全没用上。缓冲不是越大越好,关键是有没有在缓冲区里做正确的事:检测到 SLO 违约,立刻投入稳定性工程,而不是觉得"还没违约,先别急"。
缓冲设太小呢?每次微小抖动就触发发布冻结,业务团队怨声载道,最后 SRE 被迫放宽 SLO 标准。这比一开始就设合理还折腾。正确做法是:先设 50%,跑一个季度看数据,再按实际消耗模式调整。别一次定死,SLA 是活的约束,不是刻在石头上的。
最后提一个反直觉的观点:有时候故意让 SLO 违约是正确的决策。如果你的 SLO 设了 99.95%,某个月错误预算用完了,按规则应该冻结发布。但如果当月有个重要的安全补丁要上——它可能带来短期波动但长期提升稳定性——你应该发。SLO 是工具不是枷锁,错误预算耗尽不是"不允许做任何变更",而是"只做降低风险的变更"。安全补丁恰恰是在降低风险。关键是决策过程要透明:为什么在预算耗尽时还做变更、依据是什么、谁来批准。这种决策应该有文档记录,而不是悄悄做了不说。
SLA 违约后的工程响应:从检测到复盘
SLA 违约不只是法务问题,首先是工程问题。从故障发生到事后复盘,整个响应链路如下:
检测(SLI 跌破 SLO 阈值)
↓
确认(排除误报,确认影响范围)
↓
通报(通知相关团队和利益相关方)
↓
恢复(执行 Runbook,优先恢复服务)
↓
稳定(确认根因已修复,无二次故障)
↓
复盘(Postmortem,输出改进项)
↓
SLA 评估(是否触发对外违约,是否需要赔偿)
每一步都应该有对应的自动化工具支撑。检测靠监控告警,确认靠 SOP 检查清单,通报靠 Oncall 通知系统,恢复靠 Runbook 自动化。这些我之前在故障响应框架和Runbook 编写指南里详细讲过。
这里重点讲 SLA 违约特有的部分——SLA 评估。
故障恢复后,需要计算本次故障对 SLA 的影响:
#!/bin/bash
# SLA 影响评估脚本
# 输入:故障开始时间、故障结束时间、服务名
# 输出:本次故障对月度 SLA 的影响
SERVICE=$1
START_TIME=$2 # 格式: "2026-09-15T00:30:00+08:00"
END_TIME=$3 # 格式: "2026-09-15T01:15:00+08:00"
# 计算故障时长(分钟)
DOWNTIME_MINUTES=$(python3 -c "
from datetime import datetime
fmt = '%Y-%m-%dT%H:%M:%S+08:00'
start = datetime.strptime('$START_TIME', fmt)
end = datetime.strptime('$END_TIME', fmt)
print(int((end - start).total_seconds() / 60))
")
# 当前月度总分钟数
TOTAL_MINUTES=$(python3 -c "
import calendar
from datetime import datetime
now = datetime.now()
_, days = calendar.monthrange(now.year, now.month)
print(days * 24 * 60)
")
# 计算月度可用性
AVAILABILITY=$(python3 -c "
downtime = $DOWNTIME_MINUTES
total = $TOTAL_MINUTES
uptime = total - downtime
print(f'{uptime / total * 100:.4f}')
")
echo "服务: $SERVICE"
echo "故障时长: ${DOWNTIME_MINUTES} 分钟"
echo "月度总分钟: ${TOTAL_MINUTES}"
echo "当前可用性: ${AVAILABILITY}%"
echo ""
# 判断 SLA 状态
SLA_TARGET=99.9
python3 -c "
availability = float('$AVAILABILITY')
target = $SLA_TARGET
if availability < target:
print(f'⚠ SLA 违约!当前 {availability}% < 目标 {target}%')
# 计算赔偿比例(示意)
if availability < 95.0:
credit = 100
elif availability < 99.0:
credit = 30
else:
credit = 10
print(f'预估赔偿比例: {credit}%')
else:
buffer = availability - target
print(f'✓ SLA 正常。当前 {availability}%,缓冲 {buffer:.2f}%')
"
这个脚本不只是算数字。它的价值在于:故障刚恢复,团队还在紧张的时候,你能立刻告诉业务方"这次故障是否触发对外违约"“要不要走赔偿流程”。不用等人手动算,不用等法务确认,工程层面直接给出判断。
SLA 仪表盘:让数字变成可决策的信息
SLA 评估脚本解决的是"单次故障影响",但日常运维更需要的是持续可见的 SLA 状态。团队需要随时知道:当前月度 SLA 状态是绿还是红?错误预算还剩多少?燃烧率是否在告警区间?
用 Grafana 搭一个 SLA 状态仪表盘,核心指标有三块:
第一块:当前 SLA 状态。展示当前月的可用性百分比、SLO 目标值、SLA 承诺值,用颜色区分(绿色 = SLO 达标、黄色 = SLO 违约但 SLA 未违约、红色 = SLA 违约)。这块回答"我们现在安全吗"。
第二块:错误预算消耗。展示剩余错误预算(分钟数)、消耗速率(快/慢)、燃烧率。这块回答"我们离违约还有多远"。燃烧率超过 14.4(1 小时消耗 1 天预算)就要告警。
第三块:历史趋势。过去 12 个月的 SLA 达标率、故障次数、MTTR 趋势。这块回答"我们是在变好还是变差"。
# 错误预算燃烧率告警规则(Prometheus)
# 1 小时消耗超过 1 天预算 → 严重
- alert: ErrorBudgetBurnRateCritical
expr: |
(
sum(rate(http_requests_total{job="payment-api", status=~"5.."}[1h]))
/
sum(rate(http_requests_total{job="payment-api"}[1h]))
) > (1 - 0.999) * 14.4
for: 2m
labels:
severity: critical
team: sre
annotations:
summary: "错误预算燃烧率超过 14.4(1h 消耗 1 天预算)"
description: "支付 API 错误率过高,预计 1 小时内消耗 1 天错误预算"
# 6 小时消耗超过 1 天预算 → 警告
- alert: ErrorBudgetBurnRateWarning
expr: |
(
sum(rate(http_requests_total{job="payment-api", status=~"5.."}[6h]))
/
sum(rate(http_requests_total{job="payment-api"}[6h]))
) > (1 - 0.999) * 3.6
for: 5m
labels:
severity: warning
team: sre
annotations:
summary: "错误预算燃烧率超过 3.6(6h 消耗 1 天预算)"
description: "支付 API 错误率持续偏高,请关注趋势"
燃烧率告警有两个阈值:14.4 对应 1 小时窗口消耗 1 天预算,3.6 对应 6 小时窗口消耗 1 天预算。前者是急性故障,需要立即介入;后者是慢性退化,需要持续关注。这套多窗口多燃烧率告警策略来自 Google SRE Workbook,我在之前的错误预算消耗策略里也讲过。
一个生产故事
2025 年底,某 SaaS 公司用 AWS 搭建了支付服务。架构很简单:API Gateway → Lambda → DynamoDB,全部署在 us-east-1。SLA 承诺客户 99.9% 月度可用性。
某天,AWS 做了一次 API Gateway 的配置更新,引入了一个回归 bug。团队的监控显示:23% 的支付请求返回 503,持续了 45 分钟。用户端支付失败率飙升,客服系统被投诉电话打爆。
团队立刻找 AWS 索赔。AWS 的回复是:从 AWS 的监控看,API Gateway 实例正常运行,端口可达,SLA 定义中的"不可用"指的是"所有请求持续失败",而 77% 的请求是成功的,不符合"不可用"定义。索赔被拒。
更糟的是,这 45 分钟的故障让该公司的月度可用性掉到了 99.87%,低于对外承诺的 99.9%。他们需要自己掏钱赔客户,而云厂商一分钱不赔。
这次事件后,团队做了三个改变:
- 重新定义"不可用":把内部 SLA 的"不可用"定义为"错误率超过 5% 且持续超过 2 分钟"。不再是"全部请求失败"才算不可用。
- 跨区域部署:在 us-west-2 部署了热备。虽然成本增加了 40%,但下次 us-east-1 挂了,流量可以自动切换。
- SLO 提到 99.95%:给自己留了 21.5 分钟的缓冲。如果再发生类似故障,在 SLA 违约之前,错误预算就会耗尽并触发发布冻结,团队能更早介入。
后来又加了一条:独立拨测。团队在 AWS 之外(一个 VPS 上)部署了持续拨测脚本,从用户视角测量 API 成功率。不依赖 AWS 的监控数据,也不依赖应用内监控——第三方独立测量。拨测脚本 10 秒一次,记录响应码和延迟。索赔时这份数据比"我们自己的 Grafana 截图"更有说服力,因为它是独立第三方采集的,云厂商更难质疑其公正性。
这个故事的教训是:不要等到 SLA 违约了才发现定义有问题。SLA 的每个条款——什么是"不可用"、怎么测量、什么算"排除"——都应该在设计阶段想清楚,而不是在索赔被拒之后才后知后觉。
总结
SLA 不是一行数字,是一套工程约束。总结几个要点:
SLA、SLO、SLI 不能混。SLI 是测量值,SLO 是内部目标,SLA 是对外合同。SLO 必须比 SLA 严,中间的差就是缓冲。Google SRE 的经验是:SLO 错误预算建议设为 SLA 错误预算的 50%。
云厂商 SLA 的赔偿不值得依赖。阶梯制赔偿看着合理,但"服务额度"不是现金,上限是月费,覆盖不了业务损失。排除条款、窄化定义、最小索赔门槛、监控口径偏差,每一个都能让赔偿变成空头支票。
复合 SLA 的数学比直觉差。串行依赖的 SLA 是相乘的,三个 99.95% 串在一起只有 99.89%。并行冗余的 SLA 是"1 减失败概率乘积",但前提是组件真正独立。跨区域部署的成本不白花,买的是独立性假设的有效性。
内部 SLA 设计的 6 个决策:数值参考用户容忍度不照搬、违约要有工程后果不只是写报告、监控从用户视角定义、评审周期按变化速度定、用代码管理文档不是 Word、SLO 和 SLA 之间留 50% 缓冲。
独立拨测是最后一道防线。别只依赖云厂商的监控数据,也别只依赖应用内监控。在第三方环境部署独立拨测脚本,从用户视角持续测量。索赔时第三方数据最有说服力。拨测不贵——一个 VPS 加一个 shell 脚本就能搞定。
SLA 是活的约束。不是签完合同就完事的东西。每个季度评审一次,按实际数据调整 SLO 和 SLA 之间的缓冲比例。发布频率和错误预算的关系要看趋势线。审计流程要发现定义和执行之间的裂缝。SLA 管理不是一次性的法务动作,是持续的工程实践。
别等出事才看条款。SLA 的每个定义——什么是"不可用"、怎么测量、什么算"排除"——在设计阶段就要想清楚。索赔被拒才后知后觉,已经晚了。
最后提醒一句:SLA 不是一个文档,是一套工程流程。从 SLI 定义、SLO 设定、监控部署、告警策略、故障响应、事后复盘到季度审计,每个环节都得有。缺了任何一环,SLA 就退化为纸面上的数字。纸面上的数字保护不了你的用户,也保护不了你的业务。
参考资料与致谢
本文在撰写过程中参考了以下资料,感谢原作者的贡献:
- 如何阅读服务水平协议(SLA) — Microsoft Azure 官方文档,SLA 与 SLO 的关系定义及 SLA 条款解读方法论
- Improve application reliability with effective SLOs — AWS Cloud Operations Blog,SLI/SLO/SLA 定义与错误预算概念
- Service Levels and Error Budgets — Chris Jones & Niall Murphy (Google),SREcon16 演讲,SLA 不是 SRE 管理服务的正确工具,SLO 和错误预算才是
- Business commitment in cloud management — Microsoft Azure 云采用框架,复合 SLA 计算公式与年故障时间估算
- Failure is Always an Option — Microsoft Learn,SLA 赔偿不等于业务损失覆盖,应从架构设计层面应对故障
- Burn rate is a better error rate — Datadog 技术博客,错误预算的定义、计算方式与燃烧率告警
- 云服务 SLA 大揭秘:99.99% 背后,是承诺还是文字游戏? — CSDN,云厂商 SLA 赔偿计算实例、排除条款拆解与"不可用"定义分析
- SLA 服务级别协议深度解析 — 腾讯云开发者社区,SLA 常见误区、可用性百分比换算与计划内停机处理