自从推进了SRE,长期欠下的技术债终于有救了!

方勇 2021-11-14 09:58:00
作者介绍

方勇,好大夫基础架构部高级工程师,专注于SRE,微服务、中间件的稳定性和可用性建设,整体负责好大夫服务治理云平台的设计和搭建。

 

前言

 

你是否面临过服务产线 BUG 多?处理大量用户投诉?产品迭代加快疲于应付?每日大部分时间救火应急,面临成长危机?如何保障网站的高可用是行业内的痛点,也是工程师焦虑源头之一。很多公司都在积极尝试去解决这些问题,我们参考 Google SRE 思想,结合国内其他公司的经验,努力推进 SRE,并取得了一定的成果。本文主要分成两部分:SRE 的基础认知和 SRE 如何推进技术债务改造的。

 

接下来我们带着问题一起探索 SRE:

 

  • 如何去衡量服务的稳定性?

  • 服务接口平均耗时 30 多 ms,为何单机 QPS 提升不上去呢?

  • 容量规划扩缩容,熔断限流,主要参考的指标是什么?

  • 频繁处理用户投诉意见建议,先于用户提前发现问题?

  • 随着时间推移服务可用性逐渐下滑,产线 BUG 频出,如何监控服务可用性?

  • 技术债务是如何产生的,如何一步步让工程师陷入绝望的困境?

  • 重要不紧急的技术债务,何去何从,SRE 是如何推进技术债务改进?

  • 是否能提前识别潜在风险,提前解决,让服务保持健康?

  • ...

 

一、SRE 基础认知

 

SRE是一个岗位吗?是救火专员吗?需要成为全栈工程师,还是只用盯着监控面板的值班人员?

 

 
1、SRE 职责

 

主要是保障网站的可用性,是一种认知共识,有一套相关方法论,是一种系统化的思维方式。从故障预防,到故障处理,再到故障复盘,形成一个闭环。[1]

 

图片

 

从图中可以看出,SRE 涵盖不同的阶段,细分的职责也不相同。涉及日志收集,分析,风险识别,熔断限流,告警,通知。同时每个部分又都是相关依赖的,涉及不同的部门和岗位。是一个整体系统,需要各个方向联动保障。我们可以提供一些抓手,让整个体系运转起来,从而保障了整体的可用性。

 

二、SRE 体系工作流

 

 
1、故障预防

 

随着微服务的推进,业务模块被划分到不同的独立的服务中。链路请求越来越复杂,依赖的中间件也越来越多。这给服务治理带来了不少的挑战,同时对工程师编程能力要求也越来越高。一方面需要加强工程师面向失败编程的意识,一方面增强框架的治理能力。比如关心 RPC 请求异常的 Code 码,失败重试,允许数据中间态的存在,考虑分布式事务一致性,合理的熔断限流策略。

 

 
2、故障发现

 

包括框架通用埋点日志(RPC 链路/中间件依赖),业务核心链路埋点日志(订单状态流转事件)。日志收集到 CLickhouse,通过不同的分析规则生成相应的指标,然后基于 Prometheus 触发告警,通知到相应的业务方开发。

 

 
3、故障处理

 

SRE 职能成员(业务开发或系统架构组)收到告警,配合 Grafana 看板快速定位问题,部分操作可以基于治理平台完成。我们采用预先配置截图的交互方式,将常见的排查问题思路固化到看板截图上,方便后期其他 SRE 当值人员故障处理。

 

 
4、故障复盘

 

及时复盘将排障经验固化到看板中,方便下次告警的时候配合看板截图快速定位问题。针对常见的处理措施,需要集成到治理平台上。

 

三、SRE 面临的挑战

 

 
1、如何衡量服务可用性?

 

1)第一种从时长考虑:

 

衡量稳定性: AO=MTBF/(MTBF+MTTF)

 

MTTR、MTTF、MTBF 是体现系统可靠性的重要指标 [ 2]

 

  • MTTF (Mean Time To Failure,平均无故障时间),指系统无故障运行的平均时间,取所有从系统开始正常运行到发生故障之间的时间段的平均值。MTTF =∑T1/ N

 

  • MTTR (Mean Time To Repair,平均修复时间),指系统从发生故障到维修结束之间的时间段的平均值。MTTR =∑(T2+T3)/ N

 

  • MTBF (Mean Time Between Failure,平均失效间隔),指系统两次故障发生时间之间的时间段的平均值。MTBF =∑(T2+T3+T1)/ N

 

很明显:MTBF= MTTF+ MTTR

 

图片

 

 

2)第二种从服务质量来看: 

 

请求维度:成功率=成功请求数/总请求数

 

这里不能统计单个请求,而是看一段时间的概率分布,比如 5xx 占比,计算一段时间的 5xx 占比达到 5%,持续 10min。这块一般用几个 9 来衡量,比如 3 个 9,4 个 9。[3]

 

图片

 

设置稳定性目标一般需要考虑:成本,业务容忍度,当前业务的现状。大部分时候 4 个 9 目标比 3 个 9 目标投入成本要高很多。有些底层的服务,稳定性要求就比一般配套服务的高。比如 doctor 医生服务的稳定性就需要 99.99,它一旦有问题很可能就是波及全站的范围。由于很多历史原因,“大泥球”的服务积累的技术债务会很多。这时候针对这个服务定一个合理的指标,比定一个标准的指标要好很多。

 

选择稳定性目标涉及到了测量方法和判断方法的问题,包含三个要素:

 

  • 衡量指标,比如 5xx 比例;

 

  • 衡量目标,总访问量(QPM)code=200 占比小于 95%;

 

  • 影响时长,QPM 其实就是一个聚合指标,也就是说不能简单的计算单次。这在设置告警规则的时候尤为重要,比如持续 5 分钟,服务 sentry 报错大于 10。

 

这里的衡量指标就是 SLI,衡量的目标就是 SLO,如果针对服务质量的还有一个 SLA。

 

接下来的问题就是:如何选择合适的 SLI,设置合理的 SLO。

 

3)五大 “黄金指标”

 

五大“黄金指标” 前面也谈到了选择衡量指标的问题,服务相关的指标很多,比如 cpu 负载高了要不要关系,线程数高了要不要关心,QPS 量上涨了要不要关心等等。指标不能多,要设置的合理,比如 cpu 负载高,服务依然能提供稳定的服务,那可以认为服务依然正常。参照 SRE,Google 运维解密和赵诚老师的课程,一般参考以下五大“黄金指标”,这些指标常用来制定扩缩容,熔断限流,服务降级的策略

 

①容量

 

主要有服务 QPS/QPM;核心链路 QPS/QPM;单机 QPS;服务最低存活实例数,资源利用率如 CPU 负载。

 

②可用性

 

主要是指核心链路是否异常;每分钟服务 sentry 数;服务端 6xx/5xx/429/430(限流)占比。

 

③时延

 

由于存在长尾效应,平均耗时不一定能反映当前的现状,一般用耗时分布的 95 线/99 线(T95/99)。

 

④错误率

 

主要针对用户侧链路,入口网关流量,如 nginx/kong 请求 Request 的 5xx/4xx 占比。

 

⑤人工介入次数

 

主要是指程序的鲁棒性;支持自动故障转移;支持幂等;支持失败重试(注意防止雪崩);减少人工干预的次数。

 

下面给出一些示例:

 

图片

 

 
2、如果及时发现问题?

 

选择好了指标,接下来就是要采集数据提炼出想要的指标,绘制监控面板,设置告警规则,触发告警了。这部分组件近几年在快速发展,周边的生态也非常的丰富。我们也经历了从人工 -> 工具化 -> 系统化 -> 平台化进化过程。这部分可以参考之前的文章 微服务治理平台化探索

 

1)工具集

 

图片

 

SRE 工具集主要从数据源采集,分析生成监控指标,判定这些指标阈值触发告警后,及时响应。主要包括以下 6 个部分

 

①数据采集

 

java 体系的性能消耗比较大,我们选择的是 Fluent-bit 和 gohangout,将收集的数据发布到 Kafka。

 

②数据分析

 

主要有流式分析和离线分析。我们也是两者相结合的,链路分析基于我们自研的一套 TracerLog。我们会分析链路的依赖拓扑关系,找出循环调用,慢接口,慢 SQL,双向依赖等常见的风险点

 

③数据存储

 

监控体系用的 Prometheus,由于 Prometheus 原生不支持分布式存储,我们采用 Clickhouse 做远程存储。针对存储时间长的会采用稀疏存储模式,大量采用物化视图聚合数据。

 

④监控画像

 

日志查询主要基于 ELK 体系,Grafana 用于分析后的指标聚合展示,慢慢也衍生出了公司的看板文化。

 

⑤告警通知

 

基于 AlertManager 的 hock 模式,我们研发了电话/短信/企业微信通知,让整个处理流程移动化。

 

⑥告警响应

 

为了让日常处理平台化,解放运维成本,我们推出了 PaaS 云平台,辅助开发日常运维。

 

四、SRE 的切入点推进技术债务改造

 

 
1、技术债务是如何产生的?

 

一般技术债务可以分为三类:

 

  • 不良代码的积累

  • 业务建模

  • 业务架构设计

 

 
2、不良代码积累

 

这类技术债务很容易想到。随着服务的不断迭代,服务越来越复杂,需求的变更,烂代码的引入,建模的不合理,不可避免的就带来一些技术债务。技术债务增加,服务稳定性越差,越容易写出烂代码,越容易积累更多的技术债务,服务稳定性也就越来越差,从而形成了一个正反馈回路。

 

图片

 

出现这种现象的主要原因有:

 

1)工程师的过度自信。

 

认为以后会有时间解决,当前以完成功能开发为导向。然而大部分情况下,是没有下文了。常常加 TODO 的地方后续都找不到负责人了。 

 

2)工程师过度依赖搜索引擎。

 

有些工程师秉承够用就行,面向“搜索引擎编程”,遇到问题先 Google,ctrl+c & ctrl+v 让编程退变成了体力劳动。表面上看貌似没啥问题,但不确定性往往就影藏在这里面。先不说这些第三方的代码质量本身就参差不齐,更可能是破坏最小依赖原则,往往只需要一个组件的某个功能,却引入了整个组件,而这个组件又依赖其他组件。更糟糕的是后续这些依赖没人记得当初是怎么引进来的,不敢去修改,又没人负责升级迭代,从而变成了不定时炸弹。 

 

3)工程师文化,错误的理解敏捷开发--只剩快速迭代。

 

需求变更过快,疲于应付,没时间重构,甚至整个公司的 KPI 导向近期效益,工程师文化也一直强调单位人效,注重新功能的研发。

 

 
3、业务建模 & 业务架构设计

 

其实更难解决的技术债务往往是来自于业务建模的不合理和业务架构设计缺陷。

 

主要原因有:

 

  • 未理解 SOLID 设计原则

  • SRP:单一职责原则;

  • OCP:开闭原则;

  • LSP:里氏替换原则;

  • ISP:接口隔离原则;

  • DIP:依赖反转原则。

 

这块就不展开了,下次有机会再讨论。

 

1)过早的引入不需要的设计

 

虽然 SOA 微服务架构是当下主流,新项目一开始就拆分成不同的服务,大家为了适应这个庞杂的服务调用流程不得不额外付出巨大人力成本。好的系统架构是支持渐进式的,沉淀出特定领域逻辑,等到合适的时候再拆分,或者随业务的发展变化还得支持聚合。对采用什么数据库,什么框架不要提前限定死,应该留有更多的余地。

 

2)边界划分不合理 

 

软件架构设计本身就是一门划分边界的艺术。边界的作用是将软件分割成各种元素,以便约束边界两侧之间的依赖关系。关于边界划分可以参考[ 4]

 

3)依赖不合理

 

高层策略依赖低层组件等,这块可以采用 DIP 设计原则解决。微服务间调用依赖不合理,耦合度高。后面会谈到具体分析策略。

 

 
4、我们的反思:做好与技术债务长期抗争的准备

 

程序的墨菲定律:

 

程序的规模会一直不断地增长下去,直到解决线上问题将有限编码时间填满为止。

 

直到这个庞然大物在产线不停的出问题,直到解决一个问题却引入另外一个的问题,才意识到重要的难题一直没时间去做,从而把工程师带入泥潭。

 

艾森豪威尔矩阵:

 

我有两种难题:紧急的和重要的,而紧急的难题永远是不重要的,重要的难题永远是不紧急。-- 艾森豪威尔 [5]

 

图片

 

业务部门与研发人员经常犯的共同错误就是将第三优先级的事情提到第一优先级去做。换句话说,他们没有把真正紧急并且重要的功能和紧急但是不重要的功能分开。这个错误导致了重要的事被忽略了,重要的系统架构问题让位给了不重要的系统行为功能。

 

成效的软件研发团队会迎难而上,毫不掩饰地与所有其他的系统相关方进行平等的争吵。请记住,作为一名软件开发人员,你也是相关者之一。软件系统的可维护性需要由你来保护,这是你角色的一部分,也是你职责中不可缺少的一部分。公司雇你的很大一部分原因就是需要有人来做这件事。

 

 
5、成事在人

 

这里总结一下左耳耗子(陈皓老师)的观点。在国内很多工程师定位自己为“码农”,还有一部分随着业务快速拓展过早的进入了技术管理岗位。国内的基础技术还在发展中,技术能力不足,更多的技术人员被产品,运营驱动,很多公司也强调“狼性文化”驱使员工完成更多的工作。

 

但这不会常态,根据亚马逊,Facebook 这样的公司最终都会选择去发展自己的核心技术,提高自己的技术领导力。而反观雅虎,百度将自己定位为广告公司就在走下坡路。同样 Google 当年举全公司之力发展社交是一个失败的案例,还好拉里·佩奇看到苗头不对重新掌权,于是才有了无人车和 AlphaGo 这样惊世之作。

 

尊重技术和不尊重技术在初期可能不明显,而从长期开看,差距会越来越明显。

 

技术人员要有技术领导力:

 

  • 尊重技术,追求核心基础技术

  • 追逐自动化的高效工具和技术,同时避免无效率的组织架构和管理。

  • 解放生产力,追逐人效的提高。

  • 开发抽象和高质量的可以重用的技术组件。

  • 坚持高于社会主流的技术标准和要求。[6]

 

合格的工程师需要培养技术领导力,得有自己的追求。吴军老师在《浪潮之巅》谈到工程师文化把工程师分为了五个等级。工程师与工程师之间是有差别的,我们得承认。分界线也是特别的明显,每一级都差一个数量级,[7]

 

图片

 

 
6、我们的尝试:技术债务识别及优化追踪

 

图片

 

基于链路分析找出潜在的风险。

 

1)慢接口:

 

慢,会严重影响整个服务的吞吐量,最终反映到用户体验上,造成客户流失。这里不采用平均耗时是因为接口延迟满足长尾效应,延迟越大危害越大,所以优先优化延迟高的接口。一般采用接口的延迟百分位第 99 位(P99)来衡量。目前我们希望优化后端服务 P99<100ms,前端服务 P99<600ms。

 

2)异常接口:

 

接口错误率,实时反映服务可用性。一般用作熔断降级的指示灯。根据业务容忍度可用性可以设置成 3 个 9 或 4 个 9。

 

3)慢 SQL:

 

随着业务的迭代,未经优化的 SQL 可能会产生雪崩。2019 年初,由于搜索引擎蜘蛛的抓取大分页数据导致数据库雪崩,影响全站的故障。目前我们会分析慢 SQL,收集 SQL 指纹,给出相应的优化建议。

 

4)稳定依赖:[8]

 

依赖关系必须要指向更稳定的方向。根据最小依赖原则,能不依赖就不依赖,能少依赖就少依赖,高层策略不能依赖低层组件。当然完全没依赖那就变成孤岛了没有意义。当下微服务依然是主流, 各服务对依赖的稳定性要求也不一样,基础服务要求比前台服务高。我们可以参考一个指标:

 

  • Fan-in:入向依赖,这个指标指代了组件外部类依赖于组件内部类的数量。

  • Fan-out:出向依赖,这个指标指代了组件内部类依赖于组件外部类的数量。

  • I:不稳定性,I=Fan-out/(Fan-in+Fan-out)。该指标的范围是[0,1],I=0 意味着组件是最稳定的,I=1 意味着组件是最不稳定的。

 

常见的服务依赖风险点有:

 

  • 多次依赖,同一条链路下游服务多次调用上游服务不同的接口。

  • 循环依赖,同一条链路下游服务多次调用上游服务同一个接口。

  • 双向依赖:同一条链路两个服务互为上下游,相互耦合调用。

 

小结

 

这次主要分享 SRE 基础认知,以及 SRE 如何以技术债务作为切入点尝试推进系统健康度建设。技术债务一直都是工程师背负的包袱,而这部分对工程师的要求非常高,需要具备长期抗衡意识,这部分的经验积累才是成就架构梦的原始基石,唯有坚持方能蜕变。关于这部分的经典书籍还是非常多的,《代码整洁之道》、《架构整洁之道》、《重构:改善既有代码的设计》也是工程师必备圣经。如果对 SRE 感兴趣可以阅读《SRE Google 运维解密》、《SRE 生存指南》等。

 

>>>>

参考资料

 

  • [1] 赵诚《SRE 实战手册》第 01|SRE 迷思

  • [2] MTTR/MTTF/MTBF 图解

  • [3] 赵诚《SRE 实战手册》第 02|系统可用性

  • [4] 《架构整洁之道》第 17 章 划分边界

  • [5] 《架构整洁之道》第 2 章 艾森豪威尔矩

  • [6] 左耳朵耗子(陈皓)《左耳听风》05 何为技术领导力

  • [7] 吴军《浪潮之巅》第 22 章生产关系的革命-工程师文化

  • [8] 《架构整洁之道》第 14 章 稳定依赖原则

 
作者丨方勇
来源丨公众号:HaoDF技术团队(ID:haodf_tech)
dbaplus社群欢迎广大技术人员投稿,投稿邮箱:editor@dbaplus.cn
最新评论
访客 2023年08月20日

230721

访客 2023年08月16日

1、导入Mongo Monitor监控工具表结构(mongo_monitor…

访客 2023年08月04日

上面提到: 在问题描述的架构图中我们可以看到,Click…

访客 2023年07月19日

PMM不香吗?

访客 2023年06月20日

如今看都很棒

活动预告