你是否正面临着产品迭代在不断提速(催进度、要 deadline)的同时,服务产线 BUG/故障也在变多、有大量用户投诉要响应,每天都要花大把时间去处理突发情况、去救火,而无法把主要精力都投入到正常项目中的糟糕的工作状态?
如何保障网站的高可用是行业内的痛点,也是工程师焦虑源头之一。大家都在积极尝试去解决这类问题,好大夫在线参考 Google SRE 思想,结合国内其他公司的经验和我们自身的特点,努力落地 SRE,并取得了一定的进展。
接下来我们带着问题一起探索 SRE:
本系列文章,将尝试带着这些疑问,结合好大夫在线面临的实际问题,一起来探索 SRE 的落地过程,以及如何用 SRE 来转变大家的工作思路。
SRE 是一个岗位吗?是救火专员吗?需要成为全栈工程师,还是只用盯着监控面板的值班人员?
SRE 的职责主要是保障网站的可用性,是一种认知共识,有一套相关方法论,是一种系统化的思维方式。从故障预防,到故障处理,再到故障复盘,形成一个闭环。[注 1]
从图中可以看出,SRE 涵盖不同的阶段,细分的职责也不相同。涉及日志收集,分析,风险识别,熔断限流,告警,通知。同时每个部分又都是相关依赖的,涉及不同的部门和岗位。是一个整体系统,需要各个方向联动保障。我们可以提供一些抓手,让整个体系运转起来,从而保障了整体的可用性。
1、故障预防:
随着微服务的推进,业务模块被划分到不同的独立的服务中。链路请求越来越复杂,依赖的中间件也越来越多。这给服务治理带来了不少的挑战,同时对工程师编程能力要求也越来越高。一方面需要加强工程师面向失败编程的意识,一方面增强框架的治理能力。比如关心 RPC 请求异常的 Code 码,失败重试,允许数据中间态的存在,考虑分布式事务一致性,合理的熔断限流策略。
2、故障发现:
包括框架通用埋点日志(RPC 链路/中间件依赖),业务核心链路埋点日志(订单状态流转事件)。日志收集到 CLickhouse,通过不同的分析规则生成相应的指标,然后基于 Prometheus 触发告警,通知到相应的业务方开发。
3、故障处理:
SRE 职能成员(业务开发或系统架构组)收到告警,配合 Grafana 看板快速定位问题,部分操作可以基于治理平台完成。我们采用预先配置截图的交互方式,将常见的排查问题思路固化到看板截图上,方便后期其他 SRE 当值人员故障处理。
4、故障复盘:
及时复盘将排障经验固化到看板中,方便下次告警的时候配合看板截图快速定位问题。针对常见的处理措施,需要集成到治理平台上。
第一种从时长考虑:
MTTR、MTTF、MTBF 是体现系统可靠性的重要指标 [注 2]
很明显:MTBF= MTTF+ MTTR
衡量稳定性:AO = MTBF / (MTBF + MTTF)
第二种从服务质量来看: 请求维度:成功率 = 成功请求数 / 总请求数
这里不能统计单个请求,而是看一段时间的概率分布,比如 5xx 占比,计算一段时间的 5xx 占比达到 5%,持续 10min。这块一般用几个 9 来衡量,比如 3 个 9,4 个 9。[注 3]
设置稳定性目标一般需要考虑:成本,业务容忍度,当前业务的现状。大部分时候 4 个 9 目标比 3 个 9 目标投入成本要高很多。有些底层的服务,稳定性要求就比一般配套服务的高。比如 doctor 医生服务的稳定性就需要 99.99,它一旦有问题很可能就是波及全站的范围。由于很多历史原因,“大泥球”的服务积累的技术债务会很多。这时候针对这个服务定一个合理的指标,比定一个标准的指标要好很多。
选择稳定性目标涉及到了测量方法和判断方法的问题,包含三个要素:
这里的衡量指标就是 SLI,衡量的目标就是 SLO,如果针对服务质量的还有一个 SLA。
接下来的问题就是:如何选择合适的 SLI,设置合理的 SLO。
前面也谈到了选择衡量指标的问题,服务相关的指标很多,比如 cpu 负载高了要不要关系,线程数高了要不要关心,QPS 量上涨了要不要关心等等。指标不能多,要设置的合理,比如 cpu 负载高,服务依然能提供稳定的服务,那可以认为服务依然正常。
参照 SRE,Google 运维解密和赵诚老师的课程,一般参考以下五大“黄金指标”,这些指标常用来制定扩缩容,熔断限流,服务降级的策略:
1、容量
主要有服务 QPS/QPM;核心链路 QPS/QPM;单机 QPS;服务最低存活实例数,资源利用率如 CPU 负载。
2、可用性
主要是指核心链路是否异常;每分钟服务 sentry 数;服务端 6xx/5xx/429/430(限流)占比。
3、时延
由于存在长尾效应,平均耗时不一定能反映当前的现状,一般用耗时分布的 95 线/99 线(T95/99)。
4、错误率
主要针对用户侧链路,入口网关流量,如 nginx/kong 请求 Request 的 5xx/4xx 占比。
5、人工介入次数
主要是指程序的鲁棒性;支持自动故障转移;支持幂等;支持失败重试(注意防止雪崩);减少人工干预的次数。
下面给出一些示例:
选择好了指标,接下来就是要采集数据提炼出想要的指标,绘制监控面板,设置告警规则,触发告警了。这部分组件近几年在快速发展,周边的生态也非常的丰富。我们也经历了从人工 -> 工具化 -> 系统化 -> 平台化进化过程。
SRE 生态及工具集
SRE 工具集主要从数据源采集,分析生成监控指标,判定这些指标阈值触发告警后,及时响应。主要包括以下 6 个部分:
1、数据采集
java 体系的性能消耗比较大,我们选择的是 Fluent-bit 和 gohangout,将收集的数据发布到 Kafka。
2、数据分析
主要有流式分析和离线分析。我们也是两者相结合的,链路分析基于我们自研的一套 TracerLog。我们会分析链路的依赖拓扑关系,找出循环调用,慢接口,慢 SQL,双向依赖等常见的风险点。
3、数据存储
监控体系用的 Prometheus,由于 Prometheus 原生不支持分布式存储,我们采用 Clickhouse 做远程存储。针对存储时间长的会采用稀疏存储模式,大量采用物化视图聚合数据。
4.监控画像
日志查询主要基于 ELK 体系,Grafana 用于分析后的指标聚合展示,慢慢也衍生出了公司的看板文化。
5.告警通知
基于 AlertManager 的 hock 模式,我们研发了电话/短信/企业微信通知,让整个处理流程移动化。
6.告警响应
为了让日常处理平台化,解放运维成本,我们推出了 PaaS 云平台,辅助开发日常运维。
技术债务是如何产生的? 一般技术债务可以分为三类:
这类技术债务很容易想到。随着服务的不断迭代,服务越来越复杂,需求的变更,烂代码的引入,建模的不合理,不可避免的就带来一些技术债务。技术债务增加,服务稳定性越差,越容易写出烂代码,越容易积累更多的技术债务,服务稳定性也就越来越差,从而形成了一个正反馈回路。
出现这种现象的主要原因有:
1、工程师的过度自信。认为以后会有时间解决,以完成当前项目功能为导向。然而大部分情况下,项目结束后就没有下文了,加 TODO 的地方后续也都没人过问。
2、工程师过度依赖搜索引擎。有些工程师秉承够用就行,面向“搜索引擎编程”,遇到问题先 Google,ctrl+c & ctrl+v 让编程变成了体力劳动。表面上看貌似没啥问题,但不确定性往往就隐藏在这里面。先不说这些第三方的代码质量本身就参差不齐,更可能是破坏最小依赖原则,往往只需要一个组件的某个功能,却引入了整个组件,而这个组件又依赖其他组件。更糟糕的是后续这些依赖没人记得当初是怎么引进来的,不敢去修改,又没人负责升级迭代,从而变成了不定时炸弹(祖传代码)。
3、错误的理解敏捷开发。只剩下了表面上的“快速”,需求粗糙、变更过快,疲于应付,没有安排后续重构(整理)时间。
程序上的墨菲定律:
程序的规模会一直不断地增长下去,直到解决线上问题将有限编码时间填满为止。
直到这个庞然大物不停的出问题,直到解决一个难题却引入另外一个的难题,才意识到重要的难题一直没时间去做,从而把工程师带入泥潭。
艾森豪威尔矩阵:
我有两种难题:紧急的和重要的,而紧急的难题永远是不重要的,重要的难题永远是不紧急。-- 艾森豪威尔 [注 4]
业务部门与研发人员经常犯的共同错误就是将第三优先级的事情提到第一优先级去做。换句话说,他们没有把真正紧急并且重要的功能和紧急但是不重要的功能分开。这个错误导致了重要的事被忽略了,重要的系统架构问题让位给了不重要的系统行为功能。
成效的软件研发团队会迎难而上,毫不掩饰地与所有其他的系统相关方进行平等的争吵。请记住,作为一名软件开发人员,你也是相关者之一。软件系统的可维护性需要由你来保护,这是你角色的一部分,也是你职责中不可缺少的一部分。公司雇你的很大一部分原因就是需要有人来做这件事。
其实更难解决的技术债务往往是来自于业务建模的不合理和业务架构设计缺陷,主要原因有:
1、未理解 SOLID 设计原则
这块就不展开了,下次有机会再讨论。
2、过早的引入不需要的设计
虽然 SOA 微服务架构是当下主流,新项目一开始就拆分成不同的服务,大家为了适应这个庞杂的服务调用流程不得不额外付出巨大人力成本。好的系统架构是支持渐进式的,沉淀出特定领域逻辑,等到合适的时候再拆分,或者随业务的发展变化还得支持聚合。对采用什么数据库,什么框架不要提前限定死,应该留有更多的余地。
3、边界划分不合理
软件架构设计本身就是一门划分边界的艺术。边界的作用是将软件分割成各种元素,以便约束边界两侧之间的依赖关系。关于边界划分可以参考[注 5]
4、依赖不合理
高层策略依赖低层组件等,这块可以采用 DIP 设计原则解决。微服务间调用依赖不合理,耦合度高。
我们的尝试:技术债务识别及优化追踪
基于链路分析找出潜在的风险:
1、慢接口:
慢,会严重影响整个服务的吞吐量,最终反映到用户体验上,造成客户流失。这里不采用平均耗时是因为接口延迟满足长尾效应,延迟越大危害越大,所以优先优化延迟高的接口。一般采用接口的延迟百分位第 99 位(P99)来衡量。目前我们希望优化后端服务 P99<100ms,前端服务 P99<600ms。
2、异常接口:
接口错误率,实时反映服务可用性。一般用作熔断降级的指示灯。根据业务容忍度可用性可以设置成 3 个 9 或 4 个 9。
3、慢 sql:
随着业务的迭代,未经优化的 SQL 可能会产生雪崩。2019 年初,由于搜索引擎蜘蛛的抓取大分页数据导致数据库雪崩,影响全站的故障。目前我们会分析慢 SQL,收集 SQL 指纹,给出相应的优化建议。
4、稳定依赖:
依赖关系必须要指向更稳定的方向。根据最小依赖原则,能不依赖就不依赖,能少依赖就少依赖,高层策略不能依赖低层组件。当然完全没依赖那就变成孤岛了没有意义。当下微服务依然是主流, 各服务对依赖的稳定性要求也不一样,基础服务要求比前台服务高。我们可以参考一个指标:
常见的服务依赖风险点有:
如果对这部分感兴趣可以查看我们之前的一篇文章: 线上系统优化秘笈(Ⅰ) -- 慢接口分析。后续,我们还会对具体如何识别和计算技术债务这块做深入探索,敬请期待。
这次主要分享 SRE 基础认知,以及 SRE 如何以技术债务作为切入点尝试推进系统健康度建设。技术债务一直都是工程师背负的包袱,而这部分对工程师的要求非常高,需要具备长期抗衡意识,这部分的经验积累才是成就架构梦的原始基石,唯有坚持方能蜕变。关于这部分的经典书籍还是非常多的,《代码整洁之道》,《架构整洁之道》,《重构:改善既有代码的设计》也是工程师必备圣经。如果对 SRE 感兴趣可以阅读《SRE Google 运维解密》,《SRE 生存指南》,极客时间赵诚老师的课程《SRE 实战手册》。
参考文献:
作者简介:
领取专属 10元无门槛券
私享最新 技术干货