本文中,我们使用 DDD 领域建模的方法,设计了一个合理且复杂的微服务叫车系统,并在该系统之上,利用OpenTelemetry进行了可观测性构造实践,最后采用“强化混沌工程”的方法论,借助故障注入手段,实现了用于验证可观测性价值的最佳实践,并将游戏冲关的玩法融入软件开发的生命周期中,提升应用的排障能力,以此降低系统的MTTR。
一、一个类 Uber 叫车试点应用
为了达到应用可观测性构造的效果,需要设计一个足够复杂的试点应用,这里我选择了这个类 Uber App 的设计和开发。
1.1 领域建模
首先,通过领域驱动设计DDD的事件风暴,集众人之力完成该试点应用的领域模型设计:
随后观察领域中重复出现的名词,找出限界上下文:
这样,类 Uber 叫车试点应用的领域模型建模完毕。
1.2 微服务架构设计
接着,我们继续设计试点应用微服务化的系统架构:
类 Uber 叫车试点应用有五个微服务,其中四个是由限界上下文衍生出来的,包括:
- User 乘客
- Match 叫车匹配
- Trip 载客旅程
- Payment 支付金流
另外加开了broker-service,作为Websocket的统一出口。
微服务之间的通信机制,除了最直接的RESTFul API,即上下游调用关系之外,也使用了RabbitMQ作为消息队列将领域事件有效广播到各服务去。
到了这边,类 Uber 叫车试点应用的设计也已趋于完整,接下来就是实现。
1.3 Java Spring Boot 实现
使用 Java Spring Boot,遵循 Clean Architecture,最后花了一个月的时间将后端完整开发出来,共计大约 7000 行代码。
本文并不会深入展开 Clean Architecture 的开发过程,有兴趣的朋友可参考:https://github.com/Johnny8508...
二、OpenTelemetry 构造可观测性
2.1 可观测性概念的回顾
平常在开发环境中,遇到Bug的时候为求方便,都会开启调试器,借助中断来确认应用的行为是否符合预期,但是,一旦应用部署到各环境中,便不能再使用调试器来观察应用的行为了。
所谓的可观测性,指的是应用在被部署到某个线上环境后,其本身还能透过“某种机制”来让开发人员,便捷地观测应用本身在线上“各个时间点”的行为。
其中,Trace记录着每个功能完整的执行过程。由于执行过程会充满着「上下游的调用关系」,结合起来就形成了一棵树。
那么,在技术上到底该如何呈现?
手动分析:
Grafana 呈现:
2.2 OpenTelemetry 的新尝试
OpenTelemetry的发明并非要解决新的问题,而是一个在可观测性三大支柱的需求下,实现单一标准的框架。
我们决定要尝试OpenTelemetry这一项CNCF近期推出的技术框架。
我们先来看一下在以前,我们都是如何做到分布式链路追踪的?
OpenTracing + Jaeger
以OpenTelemetry的设计观点来看,这种“大同小异”便有了可以实施统一标准的可行性。
在应用代码中,使用OpenTelemetry SDK来进行数据采集,下一步Exporter会将采集到的数据如:log/trace/metrics,用配置好的格式传递到Agent去,Agent只是作为数据传递的中介,最后会再提供给Collector。
为了要在应用中做分布式链路追踪,以往我们都必须直接依赖厂商链路追踪产品的SDK来去埋点,但如此一来,未来如果决定要更换厂商时,则应用会有很大一部分需要重写。
OpenTelemetry扮演的就正是那一层抽象的标准接口,如果应用依赖的是OpenTelemetry的API,那未来要更换厂商时,就只需要改变OpenTelemetry的配置就行,任何一行代码都不需要重写。
2.3 Java Agent 的自动采集
首先要先到Github下载OpenTelemetry最新释出的Java Agent JAR。
https://github.com/open-telem...
接下来只需要将这个JAR,在Dockerfile中将其COPY到镜像中,然后在CMD中有关Java的执行指令中,添加javaagent参数,将其指向JAR的位置,一并执行就行。
此外,还需要把追踪系统一并部署起来,例如:Jaeger 或 Grafana Tempo。
基本范例可参考:https://github.com/Johnny8508...
一行代码都不用写,就能够产生出以下的 Trace 了:
以Java Agent方式执行的OpenTelemetry,开发人员不再需要亲自埋点,不需要对链路追踪系统有任何的认知,更不需要为此写任何一行代码。
OpenTelemetry会寻找所有被标注上 @RestController 的物件,并在其每个RESTFul方法调用之前,主动进行数据采集,帮忙埋下适当的 Trace。
2.4 定制化采集
在体验到自动采集的强大之后,大家心中浮现的第一个问题肯定是如果我要定制化采集的话,要怎么办?
- 第一种方法:由环境变量去设置 include / exclude 的类别或方法。
- 第二种方法:使用 @WithSpan 来增加想要被放进 Trace 的方法。
以类 Uber 试点应用为例,有两个 POJO 物件:StartDriving 和 FindCurrentTrip ,也想要被加入到 Trace 之中。
由于这两个物件只是单纯的 POJO,因此在预设的情况并不会被OpenTelemetry捕获,为了将其加入到 Trace 之中,只需要其方法上加上 @WithSpan,就能够得到以下的结果:
我们借助强大的OpenTelemetry in Java,很方便地实现了类 Uber 试点应用的可观测性构造。但是,以 SRE 的观点来看:
一直以来,SRE 都希望能够最小化可观测性构造实践,以减少其所带来的额外维护成本。
因此,我们需要一种新的最佳实践,来证明可观测性构造的价值。
三、推动可观测性的最佳实践
经过了思考与设计,最后我参考了混沌工程的精神,开发出一个方法,可以告诉我“目前的可观测性实践是否仍有缺陷”?我把这个方法称之为“强化混沌工程”。
3.1 叫车流量的自动化模拟
但是,目前仅有一个类 Uber 的叫车系统,还需要模拟乘客和司机的行为,来自动化地产生合理的叫车流量。
如上所附的动画所示,在经过数日的开发后,终于做出了一个简单的自动化叫车流量产生系统。动画中呈现的是五个司机和一个乘客的情况模拟。
序号 | 描述 |
---|---|
1 | 乘客进行叫车匹配 |
2 | 系统完成匹配,并且匹配到了某一司机 |
3 | 该名司机开始此次载客服务,并且朝着乘客的上车地点开车,移动的过程中不断地向伺服器更新自身座标 |
4 | 乘客进行叫车匹配 |
5 | 系统完成匹配,并且匹配到了某一司机 |
6 | 该名司机开始此次服务,并朝着乘客的上车地点开车,移动的过程中不断地向服务端更新自身座标 |
7 | 乘客不断接收到司机最新的座标 |
8 | 司机抵达乘客的上车地点,确认乘客上车后,司机将状态调整成“已上车” |
9 | 司机开车前往目的地,移动的过程中不断地向服务端更新自身座标 |
10 | 司机抵达乘客欲前往的目的地,结束了服务 |
11 | 叫车流程结束,乘客将自己的座标更新到了随机的位置并开始了下一次的叫车匹配 |
看似简单的叫车流程,其背后的工作其实是必须由五个微服务以及 RabbitMQ 来协作完成的,现在我们已经能够创造大量且合理的叫车流量,总算可以来开始实践“强化混沌工程”了。
3.2 强化混沌工程
从 SRE 的视角上,可观测性构造的价值在于,我们要花多少时间才能够察觉并修复好生产系统发现的问题,即 MTTR。
现在已经有了“自动化叫车流量生成器”,也有评判可观测性构造的标准MTTR,那剩下我最缺的就是“故障”了。
微服务等分布式系统在开发和运维上带来更高的门槛和复杂度,因此混沌工程便也开始被不断提倡。讲白一点,混沌工程就是“有目的地对待测系统搞破坏来提早揭露系统的问题”。
为了揭露系统的问题,我们需要先对待测系统定义其稳态。以类 Uber 叫车试点应用来说,稳态便是“能够完整且顺畅地执行每一个叫车流程”。
借鉴了混沌工程的思维,将其运用到可观测性场景中的话,则是“要有目的地在系统中搞破坏来提早揭露可观测性构造的缺陷”。
对此,由于使用场景较为特殊,是否有现存的第三方工具,能够满足类 Uber 叫车试点应用定制化的混沌场景,我并没有太大的自信,因此我决定自己实作一个能够实现混沌工程的技术架构,以下图所示:
新开了一个服务在图正中央,称之为Chaos-Server。
而在应用各个服务中都会执行一个Chaos Client ,即Chaos Agent。
Chaos-Server和各个Chaos-Client互相沟通,传递指令,来实现整个混沌工程的流程。
我们只需要在Chaos的操作页上对Chaos-Server下达命令就好。
3.3 冲关游戏的基本玩法
我们把混沌工程的流程设计成了一款冲关游戏,下面是基本的玩法:
序号 | 步骤描述 |
---|---|
1 | 部署好Chaos-Server和Chaos-Client,启动自动化叫车流量生成器。 |
2 | 浏览Chaos操作页 /api/chaos ,这个页面可以用来对Chaos-Server下达指令。 |
3 | 开始一个新的关卡:每一个关卡都可以由一串随意的字串来产生。 |
4 | 调用 API,如/api/chaos/fun/56a8d709-9c22-489e-b44e-6d86f81796b2,其中的随意字串就是新关卡的唯一标识。 |
5 | 一群特定未知的Chaos就被埋好在应用的各个服务中了。 |
6 | 接下来继续在Chaos操作页上进行,操作页上会显示所有候选的Chaos名单,并且还会显示在这些Chaos中,有几个真的被激活了。 |
7 | 冲关开始后,自动化叫车流量生成器便会进入到“不稳定”的状态,由于Chaos的缘故,叫车流程将会受到影响而被阻断。因此我们可以开始进行排障了。 |
8 | 在排障过程中,利用可观测性构造去观察应用行为,看看能否在最短的时间内找出问题来。 |
9 | 一旦找到任何潜在的问题,对照Chaos操作页上的名字,选择最可疑的那一个将其杀掉。 |
10 | 由于Chaos的名字直接以破坏的内容进行命名,因此我们能根据名字来去进行揣测。 |
11 | 如果杀掉成功,则Chaos的数量会少一个。反之,则会显示讯息:”You are mis-killing trip.SaveTripDelay, he is not the mole!”。 |
12 | 反反覆覆地游玩,直到将所有Chaos赶尽杀绝为止。 |
13 | 如果最后发现无法通关,则代表可观测性不够完整,此时应该要记下笔记,对原先的稳态假说进行修正,并再次进行同一个关卡。 |
3.4 体验一次真正的冲关游戏
首先到Grafana的仪表盘上,可以看到三个基本面板:上方为 Metrics,包括:左上为即时的叫车匹配数量,右上为应用中的错误数量;而下方则显示即时的 Logs。
开始一个新的关卡 56a8d709-9c22-489e-b44e-6d86f81796b2:
发现叫车流程整个卡住了,看来,Chaos是真的开始在搞破坏了…。
一段时间之后,便在Slack频道上收到了一个错误告警,告诉我们是时候去排障了。
同时也在面板上发现上方叫车匹配的数量急剧下滑,而下方也开始产生出了错误日志:
搜索带 “ERROR” 的日志,使用Loki的查询语法:
点击其中一个错误日志,并且从日志的 traceID 栏,直接开启右半部 Jaeger 的 trace 页面。
直接从 trace 上观察,可以看到整个微服务的上半部是顺利的,但到下方调用 /api/drivers/{driverId} 这个 API 时发生了错误。
同时,观察这个 trace 的执行时间,竟然高达了12秒!发现瓶颈为 /api/users/{userId} 这个API。
点击 span 可以看到更多详细信息,而从 DriverController.setDriverStatus 这个方法的 span 中可以看见 exception.message 明目张胆地告诉你,它就是Chaos!
回到操作页面上,把对应到的两个Chaos杀掉:
- user.SetDriverStatusAPIBlocked
- user.FindUserDelay
杀掉完两个坏蛋之后,从页面上可以看到接下来只剩下2个Chaos。
从这之后回去看叫车流程,会发现错误似乎已经消失,但是整个叫车流程还是卡到不行。因此接下来必须要来做性能调优了。
这一步,到 Jaeger 这个 data source 上去操作,搜寻在 match service 中近期的 traces,并且使用执行时间来从大到小排序。从搜寻结果上来看,会发现一个明显的效能瓶颈,竟然有一堆 trace 的总执行时间都高达六秒!点击左侧的 traceId,来导览到 trace 页面。
注意到 FindAvailableDrivers 有性能瓶颈,执行时间高达6秒,这绝对是Chaos搞的鬼。到Chaos操作页上将其杀掉!
接下来我们就只剩下最后一个Chaos了。此时看叫车流程,性能感觉好了一些,但是整个进展还是很缓慢,重复使用相同的手法来排序 traces ,但这一次我们针对 user service。从这一次的搜寻结果中,我们便注意到了有许多的 traces,竟然都花了长达3秒的时间。
发现性能瓶颈可能在 UpdateLatestLocation ,乘客或司机在旅程行驶中,会不断地调用它来更新自身的座标。如果Chaos对这个调用添加了延迟,也难怪叫车流程会如此缓慢了。
最后,我们就不必留情地把最后一个Chaos给杀掉。
游戏结束后,回去看叫车流程,会发现整个叫车流程的顺畅度又回来了!
四、结束语
本文中,我们使用 DDD 领域建模的方法,设计了一个合理且复杂的微服务叫车系统,并在该系统之上,利用OpenTelemetry进行了可观测性构造实践,最后采用“强化混沌工程”的方法论,借助提早揭露实践瑕疵的故障注入手段,实现了用于验证可观测性构造价值的最佳实践方式,以游戏冲关的玩法融入软件开发的生命周期中,提升我们对应用的排障能力,以此降低可观测性构造的MTTR。
来源:混沌工程实践
作者:水球潘/JohnnyPan
声明:文章获得作者授权在IDCF社区公众号(devopshub)转发。优质内容共享给思否平台的技术同伴,如原作者有其他考虑请联系小编删除,致谢。
IDCF社区共创读书会 首期汇报,每周四晚8点,冬哥有话说免费直播,关注公众号回复“共读”获取直播地址
- 8月19日,共读《思考,快与慢》
- 8月26日,共读《DevOps实践指南》
- 9月2日,共读《敏捷无敌之DevOps时代》
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。