参考来源:
极客时间专栏-运维监控系统实战笔记,作者:秦晓辉
一、需求场景
学习监控知识,得先了解为什么,也就是监控是因何产生的,解决了什么问题,有哪些典型的方案,分别有什么优缺点。
1.1监控系统诉求
最初始的需求,其实就是一句话:系统出问题了我们能及时感知到。
随着时代的发展,
我们对监控系统提出了更多的诉求,比如:
通过监控了解数据趋势,知道系统在未来的某个时刻可能出问题,预知问题。
通过监控了解系统的水位情况,为服务扩缩容提供数据支撑。
通过监控来给系统把脉,感知到哪里需要优化,比如一些中间件参数的调优。
通过监控来洞察业务,提供业务决策的数据依据,及时感知业务异常。
1.2监控相关的问题
指标有哪些类型,哪类指标比较关键?
如何部署一套高可用的监控系统,存储应该如何选型?
如何监控 MySQL、Redis、Kafka、ElasticSearch?
如何监控 Kubernetes 这么复杂的平台?
如何埋点,如何分析日志?
如何做到事件闭环和告警自愈?
......
1.3学习监控知识的人员画像
每个关注高可用、关注服务稳定性的技术人员 都应该学习监控相关的知识。
在稳定性保障体系中,核心就是在干一件事,减少故障。
故障的生命周期 (三个相关指标:MTBF、MTTR、MTTF,两者之间B、修复R、失效F)
减少故障 有两个层面的意思,一个是做好常态预防,不让故障发生;另一个是如果故障发生,要能尽快止损,减少故障时长。而监控的典型作用,就是帮助我们发现及定位故障,这两个环节对于减少故障时长至关重要。
1.4 监控的作用:减少故障
监控的作用还有很多,比如用于日常巡检,作为性能调优的数据佐证,提前发现一些设备、中间件不合理的配置。之所以能做到这些,是因为所有优秀的软件,都内置了监控数据的暴露方法,让用户可以对其进行观测,了解其健康状况。可被监控和观测,也是我们开发软件
时必须考虑的一环。
二、可观测性三大支柱和代表产品:指标监控、日志监控、链路监控
2.1 指标监控
我们所说的监控系统,其实只是指标监控,通常使用折线图形态呈现在图表上,比如某个机器的 CPU 利用率、某个数据库实例的流量或者网站的在线人数,都可以体现为随着时间而变化的趋势图。
指标监控只能处理数字,但它的历史数据存储成本较低,实时性好,生态庞大,是可观测性领域里最重要的一根支柱。聚焦在指标监控领域的开源产品有 Zabbix、Open-Falcon、Prometheus、Nightingale 等。
2.2 日志监控
除了指标监控,另一个重要的可观测性支柱是日志。从日志中可以得到很多信息,对于了解软件的运行情况、业务的运营情况都很关键。比如操作系统的日志、接入层的日志、服务运行日志,都是重要的数据源。
从操作系统的日志中,可以得知很多系统级事件的发生;从接入层的日志中,可以得知有哪些域名、IP、URL 收到了访问,是否成功以及延迟情况等;从服务日志中可以查到 Exception的信息,调用堆栈等,对于排查问题来说非常关键。但是日志数据通常量比较大,不够结构化,存储成本较高。
处理日志这个场景,也有很多专门的系统,比如开源产品 ELK 和 Loki,商业产品 Splunk 和Datadog。
2.3 链路监控
可观测性最后一大支柱是链路追踪。随着微服务的普及,原本的单体应用被拆分成很多个小的服务,服务之间有错综复杂的调用关系,一个问题具体是哪个模块导致的,排查起来其实非常困难。
链路追踪的思路是以请求串联上下游模块,为每个请求生成一个随机字符串作为请求 ID。服务之间互相调用的时候,把这个 ID 逐层往下传递,每层分别耗费了多长时间,是否正常处理,都可以收集起来附到这个请求 ID 上。后面追查问题时,拿着请求 ID 就可以把串联的所
有信息提取出来。链路追踪这个领域也有很多产品,比如 Skywalking、Jaeger、Zipkin等,都是个中翘楚。
三、指标监控的解决方案
对指标监控领域的多个开源解决方案做了横评对比,帮助你做技术方案的选型
四、监控核心能力-监控、告警
4.1我们常说的监控指的是啥
监控系统就是围绕指标的采集、传输、存储、分析、可视化的一个系统。
监控:这个词在不同的上下文会有不同的语义,有的时候表示数据采集和可视化,有的时候表示整个监控系统。不过不管怎么理解,通常都不影响交流
4.2监控能力的关键概念1:监控指标
监控指标-三种典型方式和代表产品
监控指标是指数值类型的监控数据,比如某个机器的内存利用率,某个 MySQL 实例的当前连接数,某个 Redis 的最大内存上限等等。不同的监控系统,对于监控指标有不同的描述方式,
典型的方式有三种:
1.监控指标:全局的唯一字符串方式-graphite
监控指标通常是一个全局唯一的字符串,比如某机器的内存利用率host.10.2.3.4.mem_used_percent,这个字符串中包含了机器的信息,也包含了指标名,可以唯一标识一条监控指标。 //命名的艺术dog.jpg
相对老一些的监控系统,比如 Graphite,就是使用这种方式来标识监控指标的。一些老的监控数据采集器,比如 Collectd,也是这样标识监控指标的。
虽然这种方式一目了然,非常清晰,但是缺少对维度信息的描述,不便于做聚合计算
举例帮助理解
myhost 这个机器上部署了两个服务,分别是 service1 和 service2。有些请求状态码是
200,有些是 500,有些 HTTP method 是 get,有些是 post。我们想分别统计这些指标的
数量,就要把这些分类维度信息都拼到指标标识中。但是这样做会产生两个弊端:一是看起来
比较混乱,二是不方便聚合统计。
举个例子,比如我想计算 service1 这个服务的成功率 ,要把 service1 里所有状态码为 200
的请求数量拿到,除以 service1 所有请求的数量。如果预先知道所有状态码、所有 HTTP
method,则可以枚举指标名称,拉取监控数据。如果不知道所有状态码和 HTTP method,
就要先用正则匹配指标标识,查询出指标列表再拉取监控数据做计算,处理起来非常麻烦。
bash
myhost.service1.http_request.200.get
myhost.service1.http_request.200.post
myhost.service1.http_request.500.get
myhost.service1.http_request.500.post
myhost.service2.http_request.200.get
myhost.service2.http_request.500.post
其实我这是用马后炮视角解释这个问题的,实际上,在 Graphite、Collectd、Zabbix、Cacti这些软件盛行的时代,这个问题并不明显。因为那个时代主要关注的是机器设备、数据库、中间件的监控,不会有很多维度的信息。直到业界开始关注应用层面监控的时候,才暴露出这个问题
2.监控指标:标签集的组合作为指标标识-OpenTSDB
2010 年左右,有一款名叫 OpenTSDB 的时序数据库诞生了。虽然现在已经较少使用了,但是 OpenTSDB 描述指标的方式,对业界有很大影响。下面是通过文本协议推给OpenTSDB 的数据示例,我们可以从中看出指标标识的定义方式。
bash
mysql.bytes_received 1287333217 327810227706 schema=foo host=db1
mysql.bytes_sent 1287333217 6604859181710 schema=foo host=db1
mysql.bytes_received 1287333232 327812421706 schema=foo host=db1
mysql.bytes_sent 1287333232 6604901075387 schema=foo host=db1
mysql.bytes_received 1287333321 340899533915 schema=foo host=db2
mysql.bytes_sent 1287333321 5506469130707 schema=foo host=db2
上面这 6 条监控指标,都通过空格把指标分隔成了多个字段。第一段是指标名,第二段是时间戳(单位是秒),第三段是指标值,剩下的部分是多个标签(tags/labels),每个标签都是 key=value 的格式,多个标签之间使用空格分隔。
除了 OpenTSDB,新时代的时序库大都引入了标签的概念,比如 Prometheus,它们甚至认为指标名也是一种特殊的标签(其标签 key 是 name ),所以 Prometheus 仅仅使用标签集作为指标标识,从 Prometheus 的数据结构定义中就可以看出来。
3.监控指标:优雅高效的 Influx 指标格式
InfluxData 公司有一款开源时序库非常有名,叫InfluxDB,InfluxDB 在接收监控数据写入
时,设计了一个非常精巧的指标格式,一行可以传输多个指标。
bash
mesurement,labelkey1=labelval1,labelkey2=labelval2 field1=1.2,field2=2.3 timestam
总体来看,分为 4 个部分,measurement,tag_set field_set timestamp,其中 tag_set 是可选的,tag_set 与前面的 measurement 之间用逗号分隔,其他各个部分之间都是用空格来分隔的。我们可以通过下面的例子来理解。
bash
weather,location=us-midwest temperature=82 1465839830100400200
| -------------------- -------------- |
| | | |
| | | |
+-----------+--------+-+---------+-+---------+
|measurement|,tag_set| |field_set| |timestamp|
+-----------+--------+-+---------+-+---------+
我们把上面 OpenTSDB 的指标示例改写成 Influx 格式,结果是这样的。
bash
mysql,schema=foo,host=db1 bytes_received=327810227706,bytes_sent=6604859181710 12
mysql,schema=foo,host=db1 bytes_received=327812421706,bytes_sent=6604901075387 12
mysql,schema=foo,host=db2 bytes_received=340899533915,bytes_sent=5506469130707 12
注意,时间戳的单位是纳秒。这种写法设计很精巧,标签重复度低,field 越多的情况下它的优势越明显,网络传输的时候可以节省更多带宽。当然了,OpenTSDB 的格式或者Prometheus 的格式如果做了数据压缩,节省带宽的效果也是不错的,因为字符的压缩效果一
般比较明显。现如今机房内部通信动辄万兆网卡,这个流量的大小区别倒也不用太关注。
三类监控指标的优缺点对比
4.3监控能力的关键概念2:指标数据类型
##指标数据类型来源
有些监控系统是不区分指标类型的,有些会做区分。90 年代左右社区就出现了一款作品RRDtool,它是一个环形数据库,也是一个绘图引擎,很多监控工具都是使用 RRDtool 来存储或绘制监控趋势图的,比如 Cacti、MRTG、Zabbix 等等。RRDtool 还提出了数据类型的概念,支持 GAUGE、COUNTER、DERIVE、DCOUNTER、DDERIVE、ABSOLUTE 等多种数据类型。
四种常见监控数据类型的介绍
Prometheus 生态也支持数据类型,分为 Gauge、Counter、Histogram、Summary4 种,
下面我们简单了解一下 Prometheus 的这 4 种类型。
- Gauge
测量值类型,可大可小,可正可负。这种类型的数据,我们通常关注的是当前值,比如房间里的人数、队列积压的消息数、今年公司的收入和净利润。
2.Counter
表示单调递增的值,比如操作系统自启动以来网卡接收到的所有流量包的数量。每接收到一个包,操作系统就会加 1,所以这个值是持续递增的。但是操作系统可能会重启,导致这个值出现重置,比如第一次是从 0 一直涨到了 239423423,然后机器重启,新采集的数据是一些比239423423 小很多的值,这种情况怎么办?此时 Prometheus 看到值没有递增,就能感知到重置的情况,会把新采集的值加上 239423423 再做计算。
3.Histogram
直方图类型,用于描述数据分布,最典型的应用场景就是监控延迟数据,计算 90 分位、99分位的值。所谓的分位值,就是把一批数据从小到大排序,然后取 X% 位置的数据,90 分位就是指样本数据第 90% 位置的值。
有些服务访问量很高,每秒几百万次,如果要把所有请求的延迟数据全部拿到,排序之后再计算分位值,这个代价就太高了。使用 Histogram 类型,可以用较小的代价计算一个大概值。
当然,不是特别准确,但是在监控场景足够用了 ,监控毕竟只是一个采样的系统,对数据准确
性要求没有那么高。
Histogram 的做法是根据数据的 value 范围,规划多个桶(bucket),把样本数据点放入不同的桶来统计。比如我们有个服务 service1,它的接口延迟最小的通常在一两百毫秒,最大的通常在 1 秒,如果超过 3 秒,大概率就是系统不正常了。
虽然 Histogram 这种做法相比于把所有请求延迟数据都存储起来再计算延迟,性能有了巨大
的提升,但是要同时计算成千上万个接口的分位值延迟数据,还是非常耗费资源的 ,甚至会造
成服务端 OOM。于是就有了 Summary 类型。
- Summary
Summary 这种类型是在客户端计算分位值,然后把计算之后的结果推给服务端存储,展示的时候直接查询即可,不需要做很重的计算,性能大幅提升。
听起来不错,但是有个问题,Summary 的计算是在客户端计算的,也就意味着不是全局的分位值,比如某个服务 service1,部署在两个机器上,服务代码里通过内嵌 Prometheus 的SDK 做了埋点监控,SDK 里会计算 Summary 数据。也就是说,分位值延迟数据是进程粒度的,不是整个服务粒度的。
这个问题很严重吗?其实也没什么大不了的,这两个机器前面肯定有负载均衡,负载均衡会保证把请求均匀地打给后端的两个实例。一个实例内部计算的分位值,理论上和全局计算的分位值差别不会很大。另外,如果某个实例有故障,比如硬盘问题,导致落在这个实例的请求延迟大增,我们在实例粒度计算的延迟数据反而更容易发现问题。
为什么要做监控数据分类?存储能识别数据类型吗
为什么还需要划分这么多类型呢?最主要的作用是在采集侧埋点的时候,SDK 会根据数据类型做不同的计算逻辑,比如 Histogram 类型,每次请求进来的时候,代码里调用一下 SDK的 Observe 方法通知 SDK,SDK 就会自动计算生成多个指标,提升埋点便利性。
从存储角度还真的不需要知道,存储的时候只要知道指标标识、时间戳、值,就足够了。后续做 PromQL 查询计算的时候,不同的函数有不同的行为,比如 rate、increase 函数,我们就给它传入 Counter 类型的数据作为参数即可。对于 histogram_quantile 函数,就传入
带有 le 标签的 bucket 指标。
4.4 告警能力的核心概念1:告警收敛
告警收敛 (大事不好啦~~~)
基础设施层面的故障,比如基础网络问题,可能会瞬间产生很多告警事件,形成告警风暴,导致接收告警的媒介拥塞,比如手机不停接收到短信和电话呼入,没办法使用。这个时候,我们就要想办法让告警事件变少,用的方法就是告警收敛。
最典型的手段 是告警聚合发送,聚合可以采用不同的维度,比如时间维度、策略维度、监控对象维度等等。如果 100 台机器同时报失联,就可以合并成一条告警通知,减少打扰。
另外一个典型的收敛手段是把多个事件聚合成告警,把多个告警聚合成故障。比如某个机器的CPU 利用率告警,监控系统可能每分钟都会产生一条事件,这多个事件的告警规则、监控对象、监控指标都相同,所以可以收敛为一条告警。假设有 100 台机器都告警了,其中 50 台属于业务 A,另外 50 台属于业务 B,我们可以按照业务来做聚合,聚合之后产生两个故障,这样就可以起到很好的收敛效果。
4.5 告警能力的核心概念2:告警闭环
闭环这个词是个互联网黑话,表示某个事情有始有终,告警怎么判断是否闭环了呢?问题最终被解决,告警恢复,就算是闭环了 。产品怎么设计才能保证告警闭环呢?通常来讲,没人响应的告警能够升级通知,告警 oncall 人员可以认领告警,基本就有比较好的保障了。
快速回顾
监控:这个词在不同的上下文会有不同的语义,有的时候表示数据采集和可视化,有的时候表示整个监控系统。不过不管怎么理解,通常都不影响交流。
监控指标:这个概念很关键,不同的监控产品有不同的描述方式,不过随着 OpenMetrics标准的建立,指标描述方式会渐渐趋于一致。重点要了解 Prometheus 的指标描述方式metric + labels,当然 metric 也可以看作一个特殊的 label。Influx 格式也很重要,建议你掌握,如果使用 Telegraf 作为采集器,就绕不过去这个格式。
指标类型:针对时下流行的 Prometheus,我们讲解了 4 种指标类型及每个类型的适用场景,最后明确了指标类型最核心的作用:在采集侧埋点时,SDK 会根据数据类型做不同的计算逻辑。
时序库:存储时序序列数据的数据库,它已经成为了一个单独的数据库细分方向,而且随着IoT 的场景越来越多,以及微服务的发展,时序库这个话题也越来越流行。
告警收敛和告警闭环:告警事件层面的话题是所有监控系统都需要处理的。当然也可以作为一个专门的产品和多种监控系统对接,专注处理告警事件,希望国内能有超越 Bigpanda、Pagerduty 的产品出现。