现在是一个云原生时代,任何一个玩技术的都或多或少跟云计算、容器、Kubernetes、云原生应用有着不同的渊源密切。
这就导致了现在公司对应用的技术的选型以及对应用的监控、管理发生了很大的变化。
特别是对 20 年以前,或者说 20 年以来应用系统的变迁也是非常明显的,这中间有很多个分水岭。
最初我们开始学习开发的时候可能都是从主机模式着手,随后学习基于 C/S 架构的开发模式,接着是从 J2EE 的流行到现在的微服务和基于容器的服务,以及目前比较热门的基于流程编排的开发架构。但我们发现,虽然开发、迭代、交付的效率得到了很大的提升,但是系统或者应用变得稍微复杂了一些。
那么在今天这样一个云原生时代呢,我们应该以什么样的方式对云原生应用进行监控和管理呢?
这就可能要涉及到另外一个话题,也就是本篇文章着重讲解的话题——《可观测性》。我将试图在本文中,帮助大家梳理清楚什么是可观测性。如果你觉得这一点很重要或者认可对云原生应用进行监控、管理的理念,那么我将阐述我对如何针对你的应用去建设这样一个可观测性的系统或者平台的一些实践经验。
首先需要说明的是:可观察性是一套理念系统,没有对技术的具体要求。其重点是团队要融入可观察性的理念,特别是要求研发写出的应用是可观察的。将可观察性包含在你的需求之中,它是与扩展性,可用性同等重要的非业务性需求。
Apple 的工程师 Cindy Sridharan 的博文《监控与观察》(Monitoring and Oberservability)首次将 Oberservability
一词带入开发者的视野。
然而,在谷歌,Google 著名的 SRE 体系在以上 Cindy Sridharan 提出可观测性之前就已经奠定了可观察性的理论基础,也就是说在微服务、可观测性等概念或者出现以前,前辈们将这套理论称为监控,其中 Google SRE 特别强调白盒监控的重要性,而将当时技术圈常用的黑盒监控放在了相对次要的位置。而白盒监控正是应和了可观察性中“主动”的概念。
这里我引用一下 Baron SchSchwarz 大咖的一个定义:“监控告诉我们系统的哪些部分是工作的。可观测性告诉我们那里为什么不工作了。”
上面这句话即定义了可观测性,也告诉了我们可观测性和传统的监控的区别。从引用来看,可观测性似乎更有助于我们诊断系统健康与否。如果我们发现监控系统告诉我们被监控对象的监控状态全都是“绿色”,一切万事大吉,一切都天下太平的话,那么可能监控本身也没有什么价值,存在的意义也不大。
到这里,监控和可观测性之间细微的区别就已经很清楚了。
正因为 SRE 在整个云原生运动中的突出地位,越来越多的团队意识到,应该从系统建设之初去主动的规划监控指标。特别在一些强调自己云原生特性的项目,如 Linkerd,traefik 等,会主动设计可观测系统内部状况的入口。这些入口包括但不限于,Promethues 的 endpoint、Zipkin 协议的支持等。
可观测性的立场是站在被观测对象(也就是你目前运行的云原生应用)之上,他的出发点是被监控的对象。
本文也将再次回顾一下如何基于被观测对象的角度来审视如何构建一个可观测性平台。我将采取四步法的方式来循序渐进的构建你目前已存在的或者未来想要构建成的可观测性。我们更加着重关注的是在每一步中可能存在的难点与挑战。
那么今天通过本文,我期望你能够有一个全面的认识和更加全面的感知。尤其是对你应用系统新的监控思维、理念。
一般对公司运维团队来说,我们比较痛苦的是当监控系统显示状态为“绿色”的情况下,我们的客服系统却被打爆了。业务部门的同事也来向你投诉,业务系统已经不能正常工作了。这是比较尴尬的窘境。
另外一种情况就是你自己也已经发现监控系统已经爆红了,不是红就是黄色警告状态。当你看到这样一个悲观的场面时,监控系统也没办法告诉你到底是哪里不工作了,以及为何不工作了。
为了解决这样的窘境呢,我们希望能有一种新的思维,站在应用系统本身出发去探讨另外一个概念或者特性——应用系统的可观测性。尤其是最前面提到的在当今云原生时代下的应用系统的可观测性。
我们来回顾一下提供可观测性的大背景,正如前面提到的云原生应用,云原生应用目前是大行其道,它不仅是一个简单的名次,其内容也是丰富多彩。其内容主要涉及到以下三大点,可以牵强/不负责任的认为,没有和以下三点强相关的话就不叫做“云原生应用”:
首先是应用架构发生了变化:
- 从单体应用向微服务过渡
- 应用架构过渡为松耦合系统
- 应用版本迭代更快、周期更短
基础设施层发生了变化:
- 容器化、应用自身快、轻、微
- Kubernetes 成为运行容器的默认平台
- Iaas、Paas 和 Caas 平台底层来承载 Kubernetes 平台
软件生命周期:
- 服务通过 DevOps 流水线持续部署
- 服务变更低成本和低风险
- 呈现高频率和全自动变更
我们来详细的看一下,下图算不算是一个云原生应用:
该互联网应用实现了多区部署、负载均衡、多应用副本、自动扩容、数据库读写分离副本。
其实判断一个应用符不符合云原生还得看他是否符合 12 要素(12-Factor),这 12 要素 其实蛮重要的,特别是在微服务年代又被再次热议。上面这张图本身是一个传统架构通过虚拟机部署的应用。他也使用到了云计算或者说云计算当中很多关键的服务,比如基于地域的 DNS、基于 Cloud 提供的负载均衡,数据库也是使用 RDBS 在区域内实现高可用、读写分离等机制。这样来看的话这也可以认为是符合了 12 因素的云原生应用。只不过它不是一个容器化的云原生应用,但它也在 Cloud 运行。当然,容器化之后的云原生应用或许更加优雅、美丽动人。
不过话又说回来,一旦你践行了服务拆分进行微服务之后,或许面临着如下这种场景:
规模化的微服务对我们分布式系统的要求提升:
- 需要在分布式系统下确保我们的服务发现/注册中心的可用性;
- 必须面对容器化环境给我们系统带来的挑战;
- 多个服务之间的依赖关系变得复杂无比,需要通过一定 DevOps 手段来对系统进行治理。
同时也对我们如何对其监控带来了挑战:
- 微服务的规模和动态性使得数据收集的成本大幅度提高,例如 CPU 、内存和网络传输的开销。
- 大量的监控数据对后台数据处理分析的产生影响,服务体量非常大的情况下,出现了问题之后,如何快速定位到发生问题的根本原因上。
- 对于可视化和关联分析的要求方面,传统 APM 缺少好的手段。
另外从应用的生命周期来看,应用在生成环境的部署只是一个开始。
对于开发人员来讲,应用上线并不能够万事大吉、高高挂起。这也许是后面痛苦的开始,即使你的系统架构设计得再好,也没有人能够完全的保证自己的系统不会出现宕机的情况。我们不光要运维好系统,还得做好时时刻刻恢复系统的准备。
然而在如今云原生时代,或者说在分布式系统时代,系统故障点可能出现在任何地方,任何地方都有出现故障的隐患。这也许会让你开始觉得分布式系统或许开始变得不那么美好。
那么我们有没有更好的方式来及时发现这些隐患呢?
我们要重新开始思考以前的监控的做法以及尝试新的手段。上面这张图的左边想突出的是传统的监控妄图通过显微镜一样,无限放大、放大查看很细节。然而,在规模化微服务之后,你可能连宏观的关联关系都还没有发现,更别说是放大地去查看。
由于如今采用容器化之后,可能基础设施都已经不受我们太强的控制。所以,现在更多的是希望在这样一个云原生系统下的遥测。
前面花了大量篇幅来说明监控到可观测性的演变,以及两者的区别。一图胜千言:
正如上面卫星遥测一样,我们希望通过寻找系统的一些比较饱满的信号量,便于我们对系统的了解。
传统的运维可能只能给我们带来最顶层的“告警”和“概况”。那么当应用系统宕机或者一些别的原因,运维需要更深层次的错误信息排错的时候,可能会将研发人员纳入该过程去剖析。比如应用运行时的 profile(Profiling 技术是一种在应用运行时收集程序相关信息的动态分析手段,常用的 JVM Profiler 可以从多个方面对程序进行动态分析,如 CPU、Memory、Thread、Classes、GC 等),甚至是需要研发人员去分析服务于服务之间的关联关系。
从上图来看,可观测性包含了传统监控的范畴。总的来看这一套“信号量”显得有点复杂,我们尝试将其精简一下:
我们把它精简成为三根支柱,也可以认为可观测性是由日志、指标和追踪三根支柱去构建的
。 一般社区在交流的时候也会选用如下这张图去讲解:
- Lgging,展现的是应用运行而产生的事件或者程序在执行的过程中间产生的一些日志,可以详细解释系统的运行状态,但是存储和查询需要消耗大量的资源。所以往往使用过滤器减少数据量。
- Metrics,是一种聚合数值,存储空间很小,可以观察系统的状态和趋势,但对于问题定位缺乏细节展示。这个时候使用等高线指标等多维数据结构来增强对于细节的表现力。例如统计一个服务的 TBS 的正确率、成功率、流量等,这是常见的针对单个指标或者某一个数据库的。
- Tracing,面向的是请求,可以轻松分析出请求中异常点,但与 logging 有相同的问题就是资源消耗较大。通常也需要通过采样的方式减少数据量。比如一次请求的范围,也就是从浏览器或者手机端发起的任何一次调用,一个流程化的东西,我们需要轨迹去追踪。
上面通过大篇幅的对云原生时代大背景以及可观测性的结构之后,且在你认可上面观念的情况下,应该如何针对我们目前存在的系统或者正在开发运维的系统进行可观测性的建设呢?
为了大家更好的理解,我这里采用了直白的四步法,供你们参考,同时你也可以结合你们目前本身的情况进行对比:
可观测性建设四步法
0 健康检查
健康检查属于第一步,在这一步,我们需要通过一些手段能够知道我们的系统是否处于运行状态、是否能够正常处理工作以及是否能够处理更多的工作?抽象地认为是一个服务运行状态的红绿灯系统,通过红绿灯信号量,能够显而易见的知道当前应用运行状态。
总的来讲,有三种方式来实现这样的“红绿灯系统”:
- 1、通过广播的方式,将自身健康状态信息发送到指定地方,以此来更新自己以及邻居节点的状态。
- 2、 通过注册表的方式,将自己的服务 IP、Port 写入到比如 Eureka、Etcd、Zookeeper 等服务清单中。
- 3、 通过接口暴露自己的健康状态,比如在应用中实现类似
/health
指定的接口,并返回统一的格式。这种方式可能是更为普遍的一种实现方式。一般可以通过集成 Prometheus 或者在 Java Spring Boot 中 通过 Actuator 实现。之后需要采用工具或者自己实现相关服务来轮训查询健康状态。
例如:在 Java 服务中通过 Actuator 暴露 /health
接口的返回信息:
{ "description":"User Service Server", "healthy":"true", "denpendencies":[ { "name":"mysql", "healthy":"true" } ] }
你也许会认为应用健康检查属于运维人员去关心的,但其实作为应用开发人员就应该去考虑为你的应用添加此特性或者此功能,该特性与你的应用处理的业务流程的重要性应该被同等对待。
上面只是提供了三种让你可以参考的方式,但是该步骤完成之前,你可能需要去考虑如何定义健康——即何谓健康?以及需要注意健康检查的程度,避免过度检查造成 DDOS 给应用带来影响。
1 指标
紧接着第二步则是指标监控,首先来回顾一下何谓指标?
指标是在许多个连续的时间周期里度量的 KPI 数值。
比如我们常常谈到的一个应用在过去十分钟、半小时内的 CPU、内存占用率以及平均占用率等等。
同时呢,一般情况下会将指标进行分类:
- 系统指标:CPU 使用率、磁盘使用率以及网络宽带情况等等。
- 应用指标:出错率、SLA(服务等级协议)、APDEX(服务满意度)、平均延时等等。
- 业务指标:用户会话、订单数量和营业额等等。
对于上面系统、应用这两种指标来讲,一般能够通过一些开箱即用的工具来实现。比如 Zabbix、Prometheus、Elastic MetrciBeat 等等。
需要强调的是业务指标,像前面提到的健康检查一样,需要开发者考虑并将其与业务处理流程同等对待。
这里列举了一个最常用的指标采集模式:
左边的监控对象包括了很多基础设施,比如服务器、应用框架、中间件和业务服务。其中每个监控对象都有不同的采集方式,如果说在如今云原生环境下的话,可能 Prometheus 这种自暴露的方式被广泛地使用。
但是,也不要对指标过度地依赖,比如我们在做告警分析的时候,要注意并不是每个指标都值得告警,真正需要告警的是针对影响用户端征兆的告警;实时查询意味着鉴于存储成本,数据无法长期保留;传统的静态阈值管理需要根据业务高峰期动态地调整,以此达到可能性预测;数据的聚合、统计、分析和展示需要耗费一定的开发人力成本以及计算需要消耗大量时间和存储成本。
2 日志
恭喜你,成功进阶到日志阶段。日志记录了每件事情,并能够提供发生过什么事情的“证据”。这里拿 Nginx 日志来看:
192.168.1.22 - - [1/May/2020:10:41:46 +0000] 56422 "GET / HTTP/1.1" 200 396 "-" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36"
其中记录了远端 IP,发生请求的时间以及相应状态和数据大小。当然,这是你的系统中一种日志,你还有系统中不同服务的后端用于排错的 Debug、错误日志等等。
因此,得有一定的手段来集中管理大量日志数据,这里提一下三个前提条件:
- 可集中化:传统的可能是运维通过 SSH 连到机器上手动过滤、搜索关键日志。想象一下,当你们系统出现问题之后,你的老板站在你身后看着你打开了四五个,甚至更多的窗口滚动着不同的日志。
- 可全文检索:在上面日志集中化之后,我们需要根据不同维度、关键词进行检索,从大量日志中检索自己期待的日志数据。
- 可关联:这一步可能是在检索之后提高排错或者说日志检索更为重要的一步,在多个索引之间可以通过时间节点、业务系统调用逻辑等角度进行横向关联分析与比对。
这里额外地针对细化日志关联做一点补充,特别是在如今微服务系统盛行的今天,通过事件将日志进行关联分析形成信息流是尤为重要的,以 Dapper 论文中的调用图为例:
这个路径由用户的 X 请求发起,穿过由一系列服务组成的系统。
想象一下,当用户的请求出现问题之后,你会从哪里开始排查问题呢?Dapper 论文中提到的解决方法是:当请求发起时,创建全局的 ID,并将其随着后续请求的调用传递下去,同时被调用的服务需要将该 ID 和你的事件绑定在一起。
简单来讲,目前普遍的方法就是在日志中集成 TraceId 形成日志信息流。在上面集中化的日志系统中进行查询时,能够通过该 TraceId 进行查询,将 A、B、C、D、E 不同服务对该次请求的处理流搜索出来。
3 应用链路追踪
试想一下,服务的健康检查全都是绿色,指标也是绿色的,日志系统中也没有报错。但是总会有一些用户抱怨,某个操作响应很慢或者点击出现 500 错误等等。公司业务部门找到了运维团队,客服电话也被轰炸。如何破解此囧境?
目前较多的手段则是通过链路追踪的方式来辅助运维团队。市面上也有很多 APM 厂商,国内外也有很多开源的链路追踪系统,比如国外的 Jaeger、Pinpoint、Zipkin、Elastic APM,国内的 Skywalking。由于我也参与 Apache Skywalking 项目管理以及相关贡献,那我此处就以 Skywalking 举例:
我从 Skywalking 抓取了一张截图,Skywalking 通过探针将一次请求将链路串联起来,以此实现在用户抱怨之前提前洞察出一些问题。
另外,我也抓取了 Elastic APM 的架构图以供参考:
到这里可观测性的四步法也基本上讲述完了,假设你一步一步地进行建设走过来,那么我也相信你也初步构建好了你的可观测性。但是,任何系统都不一定能满足你的所有需求,只能多做一些准备,准备得越充分,才能走得更远更牢固。
现在再来回顾一下构建可观测性,他并不是非黑即白的东西,更像是一个光谱,
越靠左的作用是帮助我们了解、监控系统的可靠性,越靠右则是帮助我们进行排错与一些探索。
最后来总结一下实施可观测性的关键:
- 成本低的追踪埋点:针对运维团队或者公司层面来讲,降低实施可观测性的成本。
- 浏览和查询友好:支持日志、指标和链路之间的关联,支持复杂运维场景的定制。