PCon全球产品创新大会最新日程上线,这里直达 了解详情
写点什么

SRE 实战 (01) 初识 + 探索 SRE 如何推进好大夫在线技术债务改造

  • 2021 年 10 月 15 日
  • 本文字数:5396 字

    阅读完需:约 18 分钟

SRE实战(01) 初识+探索SRE如何推进好大夫在线技术债务改造

你是否正面临着产品迭代在不断提速(催进度、要 deadline)的同时,服务产线 BUG/故障也在变多、有大量用户投诉要响应,每天都要花大把时间去处理突发情况、去救火,而无法把主要精力都投入到正常项目中的糟糕的工作状态?


如何保障网站的高可用是行业内的痛点,也是工程师焦虑源头之一。大家都在积极尝试去解决这类问题,好大夫在线参考 Google SRE 思想,结合国内其他公司的经验和我们自身的特点,努力落地 SRE,并取得了一定的进展。


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


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

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

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

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

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

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

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

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

  • ...


本系列文章,将尝试带着这些疑问,结合好大夫在线面临的实际问题,一起来探索 SRE 的落地过程,以及如何用 SRE 来转变大家的工作思路。

SRE 基础认知

SRE 职责

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

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

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

SRE 体系工作流

1、故障预防:

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


2、故障发现:

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


3、故障处理:

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


4、故障复盘:

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

SRE 面临的挑战

如何衡量服务可用性?

第一种从时长考虑:


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


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

第二种从服务质量来看: 请求维度:成功率 = 成功请求数 / 总请求数


这里不能统计单个请求,而是看一段时间的概率分布,比如 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。

五大“黄金指标” 

前面也谈到了选择衡量指标的问题,服务相关的指标很多,比如 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 云平台,辅助开发日常运维。

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

技术债务是如何产生的? 一般技术债务可以分为三类:


  • 不良代码的积累;

  • 业务建模;

  • 业务架构设计。


不良代码积累 

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

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


1、工程师的过度自信。认为以后会有时间解决,以完成当前项目功能为导向。然而大部分情况下,项目结束后就没有下文了,加 TODO 的地方后续也都没人过问。


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


3、错误的理解敏捷开发。只剩下了表面上的“快速”,需求粗糙、变更过快,疲于应付,没有安排后续重构(整理)时间。


程序上的墨菲定律:

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


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


艾森豪威尔矩阵:

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

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


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

业务建模 & 业务架构设计

其实更难解决的技术债务往往是来自于业务建模的不合理和业务架构设计缺陷,主要原因有:


1、未理解 SOLID 设计原则

  • SRP:单一职责原则;

  • OCP:开闭原则;

  • LSP:里氏替换原则;

  • ISP:接口隔离原则;

  • DIP:依赖反转原则。

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


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、稳定依赖:

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


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

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

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


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

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

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

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

如果对这部分感兴趣可以查看我们之前的一篇文章: 线上系统优化秘笈(Ⅰ) -- 慢接口分析。后续,我们还会对具体如何识别和计算技术债务这块做深入探索,敬请期待。

小结

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


参考文献:


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

  • [注 2] MTTR/MTTF/MTBF 图解

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

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

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

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


作者简介:

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

2021 年 10 月 15 日 14:152514

评论 4 条评论

发布
用户头像
sre能决定开发的架构设计?
2021 年 10 月 18 日 08:29
回复
sre和运维、系统架构不能各自为战,需要整体技术架构设计来保障,如果sre只是运维团队自己搞,那基本没戏。
2021 年 10 月 25 日 10:16
回复
SRE是需要和RD和QA配合,但SRE只能提提建议,实际上还是只能在业务架构的基础上做一些小动作,对主体架构并没有什么影响力。
2021 年 10 月 29 日 17:59
回复
取决于公司对整体技术架构的设计,要么靠技术约束,要么靠规则约束,如果sre只是建议站在外边看,挠挠痒,那就属于自high了。技术架构要向矩阵转,纵向靠整体技术约束+业务团队个性化,横向靠提前达成的共识+发现生成技术负债+设计评审,有公共平台发现和扫荡问题。但总之是要看公司是否整体技术体系的把控能力了。
2021 年 11 月 11 日 11:10
回复
没有更多了
发现更多内容

【数据结构与算法 11】常见的7种排序算法

Java 程序员 后端

技术干货 | Flutter在线编程实践总结

有道技术团队

flutter 大前端 #技术干货#

【并发编程系列3】volatile内存屏障及实现原理分析(JMM和MESI)

Java 程序员 后端

【数据库实验】《小型MIS的开发》

Java 程序员 后端

【消息队列最佳实践】消息恰好被消费一次

Java 程序员 后端

音频 AI 算法在 RTC 中的实践

网易云信

人工智能 算法 音视频

【玩转Linux】史上最详细的Linux命令大全和线上问题排查手册

Java 程序员 后端

【备战秋招冲击大厂】Java面试题系列—Java集合

Java 程序员 后端

【备战秋招冲击大厂】Java面试题系列—数据库

Java 程序员 后端

【并发编程】Thread类的详细介绍

Java 程序员 后端

【死磕JVM】什么是JVM调优?

Java 程序员 后端

Java程序媛的秋招历程(附字节,阿里,百度,网易,美团等面经)

Java spring 程序员 面试 大厂

【源码分析设计模式 9】SpringIOC中的模板方法模式

Java 程序员 后端

【Spring Boot 8】Okhttp实现GitHub第三方登录

Java 程序员 后端

【Spring 工厂】工厂设计模式、第一个Spring程序细节分析、整合日志框架

Java 程序员 后端

【大厂技术内幕】字节跳动原来是这么做数据迁移的!

Java 程序员 后端

【增强】(注解)SSM之配置多数据源

Java 程序员 后端

做云原生时代标准化工具,实现高效云上研发工作流

CODING DevOps

云原生 研发管理工具 CODING

【数据结构 Java 版】玩转顺序表

Java 程序员 后端

【SpringBoot搭建个人博客】- 后台登录(四)

Java 程序员 后端

用APaaS平台落地高校闲置资产调剂业务

明道云

【程序人生】为什么Java开发人员在简历上不敢轻易写精通Java(1)

Java 程序员 后端

【Spring Cloud 12】分布式架构下的高可用设计与可伸缩设计

Java 程序员 后端

【Spring 工厂】注入详解 — Set注入(JDK内置类型

Java 程序员 后端

【源码分析设计模式 10】SpringMVC中的适配器模式

Java 程序员 后端

【springcloud】eureka服务治理入门

Java 程序员 后端

外包学生管理系统详细架构设计

stars

架构训练营

【程序人生】为什么Java开发人员在简历上不敢轻易写精通Java

Java 程序员 后端

【springcloud合集】02:微服务架构理论基础

Java 程序员 后端

基于etcd实现大规模服务治理应用实战

百度Geek说

百度 架构 后端 etcd 服务治理

【消息队列最佳实践】消息恰好被消费一次(1)

Java 程序员 后端

SRE实战(01) 初识+探索SRE如何推进好大夫在线技术债务改造-InfoQ