一、为何还要做时序监控?
之前写过一系列的文章去介绍系统监控和业务监控的重要性,特别是在云平台环境中。云环境中的指标很多,Linux系统指标就有上百个,这些指标合理的取值很难准确的知道,且跟VM上运行的服务的类型是有很大的关系的,因此指标的多样性和指标之间复杂的关系让用户很难确定,当有任何小问题都会在不同维度的指标中只产生异常的表现,那么我们如何能利用算法的能力,最大限度的提升自动化的能力,让算法帮助我们监控这些指标,并将监控得到的告警通过一类类似”人脑“组件,提供给我们更准确的信息,方便运维人员进行快速发现问题并进行诊断。


在实际场景中会遇到各种问题,结合上述图稍微总结下(各位读者可以在评论去进行补充,我会及时更新相关文档)
同时随着各个云上服务的SLA要求的提升,企业服务也需要不断的提供问题发现的准确性和速度,在这一点上,自动化的主动巡检监控和秒级别的监控越来越被广大客户所重视。
二、在云上如何做好监控?
2.1 云监控中的指标数据
云监控(CloudMonitor) 是一项针对阿里云资源和互联网应用进行监控的服务。涵盖IT设施基础监控,外网网络质量拨测监控,基于事件、自定义指标、日志的业务监控。为您全方位提供更高效、全面、省钱的监控服务。云监控服务可用于收集阿里云资源或用户自定义的监控指标,探测服务可用性,以及针对指标设置警报。使您全面了解阿里云上的资源使用情况、业务的运行状况和健康度,并及时收到异常报警做出响应,保证应用程序顺畅运行。但是云监控仅能实现分钟粒度的指标监控和告警通知,需要按照相关的经验参数提供不同场景的阈值,导致配置和修改时会带来较大的成本。
2.2 基于开源的Zabbix的监控
Zabbix是一个用于网络,操作系统和应用程序的开源监控软件,它旨在监视和跟踪各种网络服务,服务器和其他网络硬件的状态。简单的讲,主动模式和被动模式都是将Agent作为参照的
- 主动模式:由Agent主动建立TCP链接并向Server端发送请求。
- 被动模式:由Server建立TCP链接并向Agent端发送请求。
通过Agent将周期性采集到的数据写入到后端存储中去,默认是MySQL中,然后通过模板配置,生成监控脚本,对各个监控对象进行监控,触发条件后进行相关告警。
2.3 基于开源的Prometheus监控
Prometheus是一个开源的服务监控系统,它通过HTTP协议从远程的机器收集数据并存储在本地的时序数据库上。
- 多维数据模型(时序列数据由metric名和一组key/value组成)
- 在多维度上灵活的查询语言(PromQl)
- 不依赖分布式存储,单主节点工作.
- 通过基于HTTP的pull方式采集时序数据
- 可以通过push gateway进行时序列数据推送(pushing)
- 可以通过服务发现或者静态配置去获取要采集的目标服务器
- 多种可视化图表及仪表盘支持
Prometheus通过安装在远程机器上的exporter来收集监控数据,后面我们将使用到node_exporter收集系统数据。
2.4 基于SLS的整体监控
日志服务团队提供完整的从指标采集、存储、消费、分析、可视化、告警等一站式的解决方案,用户只需要关注监控对象本身的服务质量就好,其它的流程都可以一键式搞定。平台提供的监控能力如下:
三、我们怎么做监控?
在特定的场景中,我们要稍微对指标数据进行下结构化处理,具体的方法可以参考如下操作。
3.1 如何生成结构化数据
3.1.1 从云监控直接导入监控数据
我们从云监控中直接拉取对应的数据,具体的操作步骤如下(稍后会将对应的代码同步到Github中,供大家使用)
# 安装阿里云SDK的核心库
pip install aliyun-python-sdk-core-v3
# 安装云服务器ECS的SDK
pip install aliyun-python-sdk-ecs
# 安装cms SDK软件包
pip install aliyun-python-sdk-cms
- 需要一个具有访问云监控资源的AK,可以获取对应的指标数据
-
从官方文档中,我们可以拉取到对应的指标列表
3.1.2 在SLS中生成结构化的聚合数据
日志服务这边提供了简单的操作,通过SQL命令,直接将结果写入到相同Project下面的LogStore中去,具体的操作流程如下:
- 用户先写好可以符合预期的SQL语句
- 然后在本Project下创建一个Logstore,并按照SQL的结果创建好对应的索引配置
- 将第一个写好的SQL后,使用insert into语法完成数据的插入,同时将这个SQL语句配置成告警定时任务(按照整分钟执行一次)这样就可以完成小批量数据的特征生成了
*|
INSERT INTO analysis_cluster
(__time__,
agent,
projectid,
logstore,
cumulativememory,
processedrows,
taskid)
SELECT __time__,
'query_master' AS agent,
projectid,
logstore,
"querystate.cumulativememory" / 1024 / 1024 AS cumulativeMemory,
processedrows,
queryid
FROM query_master_log
WHERE projectid IS NOT NULL
AND queryid IS NOT NULL
AND projectid IN (SELECT DISTINCT Cast(projectid AS VARCHAR) AS ProjectId
FROM sls_operation_log
WHERE projectid > 0
AND projectid IS NOT NULL
AND ( status = 500
OR status = 400
OR latency > 30000000 ))
3.2 如何发挥算法的能力
3.2.1 在SQL中定制化告警
在SLS中也提供了很强大的告警能力,具有一定SQL能力和业务经验的同学,可以对相关的业务指标进行监控。具体可以按照如下的操作来划分:
使用SQL检测算法的弊端:
- 在SQL中进行计算,每次都要捞取一段时间数据,对底层资源的消耗较大
- 由于存储在数据捞取和计算的过程,导致目前平台提供的最小检测粒度是分钟级别
- 也是因为每次都从底层拉取数据在进行SQL计算,并未将模型存储或者状态存储下来,导致计算资源被浪费
- 且不同的指标、不同实例对象之间的数据异常很难定义出来,需要人工去编写各种SQL,还要为不同的SQL结果调整阈值,导致这种方法在管理和升级起来很麻烦
3.2.2 如何实现流式的检测
如果我们数据是按照一个固定的频率写入到SLS中的LogStore,我们可以订阅这个LogStore的数据,并告知算法该数据的Schema,选择用户需要的检测算法,就可以完成针对数据中的实例(比如:访问日志中的域名、机器指标数据中的机器)进行巡检。
- 我们假设,一个数据采集Agent(比如:logtail等)按照一个固定的频率将200台机器的多维度指标写入到LogStore中去,如下图中所展示的:每隔10秒,将每台机器的[CPU, DISK, Latency, Load, memory]等指标写入到LogStore中;
- 接着,我们开启 “数据加工”模式,经过如下截图中的操作步骤
- 来到创建数据加工规则的界面,我们要保证填写的AK信息具有对当前LogStore具有可读且能创建消费组的权限;同时在存储目标中,目前仅支持写入到本Project下面,已将创建好的LogStore中,且存储目标中的AK要具有写入权限
-
下一步,选择“加工范围”,这里的加工范围所对应的时间是数据达到服务器端的时间,接着就到了模型参数配置部分,针对各个参数的含义我们做一个介绍:
- 时间列:表示某条日志的时间戳,单位为秒;一般选择
__time__
这个字段
- 时间粒度:表示数据写入的频率,这里要求数据对象写入LogStore的频率一致,单位是秒
- 实例列:表示能唯一标记巡检对象的字段,要求是text文本列,可多选
- 特征列:表示某巡检对象所对应的特征,要求为数值列,可多选
- 算法类型:目前仅提供 “流式异常检测”,算法所对应的参数如下
-
算法参数(在选择了流式异常检测算法后)
- 模型保存间隔:该参数表示后台定期保存模型的频率,单位是分钟;
- 滑动窗口大小:该参数表示底层算法在对单个实例对象检测时需要观察的最近的窗口长度,单位是点的数目;
- 建模节点个数:该参数表示后台建模节点的数量,上限是5个节点
-
关于算法参数的几点补充说明
= 如何选择 “建模节点个数”?这个提供一个例子,让大家更好的理解如何进行选择:单个建模节点,能对10,000条实例曲线,在滑动窗口为258个观测点的数据进行建模,其中10,000条实例曲线的计算逻辑是:实例维度 * 特征维度,流式算法是针对当前的每个特征维度进行单独计算
-
检测结果说明
__time__: detector_run_time_stamp
entity: {
"address": "xxx",
"machine": "xxx"
}
job_name: "xxxx",
result: {
"model_detail": {
"cpu": {
"dim_name": "cpu",
"is_anomaly": true/false,
"score": 0.04
},
"disk": {
"dim_name": "disk",
"is_anomaly": true/false,
"score": 0.04
}
},
"model_name": "xxxx",
"model_ready": true/false,
}
