标签归档:监控

智能监控时代 – 5步道出监控建设之道

 

在正式阅读本文之前,我们先思考一个问题-几乎每个IT公司都有一套自己的运维监控系统,每家公司的运维都在做监控系统,而似乎每家都在面临一个问题,监控系统不好用,不能解决实际的监控问题,有没有更好的监控系统呢?答案是有的,本文将为您揭晓谜底。

 

 

1.什么是监控-凡事预则立不预则废

  
    运维曰:监控,业务之大事,死生之地,存亡之道,不可不察也。

    监控的建设,不亚于一场战争的准备,无论是使用监控的用户,还是建设监控的人员,都是面临着监控是否好用、好使的现实挑战。因此,我们必须充分认识并理解监控

 

 

    监控(Monitoring),顾名思义,一是监测,二是控制,重点在第一个字眼“”上,即监测、预防的意思。在计算机运维领域,特指对目标状态进行数据采样,从而判断其运行状态的一种方式,通常我们会关注以下监控数据。

    在本文中,我们重点探讨后4种监控,因APM属于特定领域监控,此处不详细展开论述。那么,既然用户关心如此多的监控数据,如何建设符合业务需求的监控呢?是不是存在一款监控系统,就完全能够满足用户的监控需求呢?

 

2.监控建设的现状-千里之行始于足下

    
    监控平台的建设,是一个长期的过程,而非一蹴而就。总体而言,有四种玩法,一是基于开源监控平台搭建,二是使用商业平台搭建,三是二次开发开源产品,四是完全自主研发。而每种玩法,均有自己的特点和其局限。

 

    1.基于开源监控软件搭建监控平台,可选Nagios,Cacti,Ganglia,Zabbix,Graphite,Prometheus,TIGK(Telegraf、InfluxDB、Grafana、Kapacitor)等,通常只需要将开源软件部署起来,然后丰富采集数据即可。其特点是开源,社区解决方案众多,可以自由定制。

 

    2.基于商业软件搭建监控平台,在监控的商业领域,以前基本都是国外公司的天下,如IBM,HP,卓豪等,但随着本土基础设施厂商的崛起,国产厂商奋发图强,出现了一些优秀的商业监控软件,如云智慧,监控易,OneAPM等,以及一些专有场景的监控软件。其特点是只要花钱,就可以实现相应的监控服务,免去了监控构建的重复摸索,适合于监控场景较复杂,缺少人力,又急需监控解决方案的项目。

 

    3.基于开源软件二次开发搭建监控平台,开源软件本身具备完备的功能,如提供API,提供数据查询接口,可以通过API进行管控等,如基于Zabbix、Prometheus、Open-falcon等的二次开发,可以实现完备的监控功能和友好的管理功能。其特点是可以按需扩展监控采集源,按需集成,自由定制,不满足于已有软件提供的功能,可以按场景灵活定制,建议在“花钱买服务不能解决问题”的情况下而为之。

 

    4.基于自主研发,从零开始构建监控平台,这是一个庞大的工程,对技术和工程管理都是个不小的挑战。为何需要自主从头开始研发一个监控系统,原因可能有以下几个,1.市面监控软件无法满足其业务需求,功能上不够用,性能上不满足,管理上不足以支撑其业务和组织架构的发展;2.生态上无法满足业务发展需求;3.基于开源软件二次开发版权存在风险,受制于他人;4.业务需要,管理层支持,技术人员充足,天时地利人和均具备。其特点是开发周期长,目标期望与现实差距,开发速度和业务发展速度能否及时跟进,稍有不慎就有软件项目失控风险,考验的是项目管理水平和工程实现能力。

 

    从上面四种方式来看,其实现成本从低到高,从易到难,而具体采用什么方式,需要根据实际情况定夺,那么,主要取决于什么呢?人力、物力、财力,还和公司所处的阶段有密切关系。

 

    如公司刚起步,追求速度和成本,则花半天时间搭建一个开源监控系统,则不失为明智选择,具体选择哪款开源软件,则可以选择自己最熟悉的,使用人群最多的。而公司已初具规模,业务需求较多,选择商业监控软件和基于开源二次开发,则可以根据具体业务需求进行详细评估,规则是,花多少成本,换取多少收益,还需考虑收益是长期的,还是短期的。

 

    当公司发展到一定规模,其组织架构和业务需求,决定了软件架构需求,因此此时的监控系统,也必须具备这种能力,因此这个时候,建设基础设施不仅仅是业务需求和产品能力那问题,而是战略规划紧密关联的问题,所以选择完全自主开发,还是选择商业、开源优良产品二次开发,均是可选方向,取决于技术储备和组织的执行力,天时地利人和缺一不可。
 
    诚如“孙子曰:凡用兵之法,驰车千驷,革车千乘,带甲十万,千里馈粮,则内外之费,宾客之用,胶漆之材,车甲之奉,日费千金,然后十万之师举矣。”,对应监控的自主开发,需要项目,产品,设计,开发,测试,运维,运营等多种人员共同参与,然后历经数月,出demo,测试验证,经过一个又一个的迭代,然后十万服务器可监控之。自主开发耗费人力物力财力,犹如一场战争的准备,不可轻举妄动,要详细周密的规划方可行动。

 

3.监控建设的挑战-吾将上下而求索

    在第2节中,我们探讨了监控建设方案选型问题,那么,除了上面提到的问题,我们在建设过程中还会有哪些问题呢?

 

1.系统关键指标缺失

    监控建设从来都是一个持续的过程,没有一劳永逸的解决方案。在持续不断的监控运维中,我们不断去丰富和完善相关监控,常见的系统和应用层面的监控指标如下所示。

 

 

    从上图中我们可以看到,监控的具体使用用户对监控指标的采集是非常宽泛的,不管是市面哪款监控系统,其提供的默认监控指标,可能是无法满足实际的场景需求。随着监控系统的持续运营和业务不断发展,采集更多监控指标的需求会越来越迫切,因此,我们希望监控系统能够提供自由扩充和灵活定制的能力。

 

2.功能扩展困难重重

 

    在持续的监控系统建设过程中,我们根据实际需求不断地完善采集指标。因此,监控平台是不是原生支持多种采集方式,可能会限制我们能力的发挥,比如监控网络、存储等硬件设备,我们就必须使用SNMP协议来获取监控指标数据,这应该是平台自带的一个基本能力。如果还要我们自己去从零实现,就相当于写了一个小型的监控软件。因此,这个监控系统应该提供扩展能力,要么是代码开源,要么是接口开放,我们可以根据实际需求去扩展模块和组件,从而达到我们业务不断发展的需求。

 

    监控指标的不断扩充,指标数据需要存储的数据量也会越来越大,此时,对监控系统的QPS,就有非常高的要求。监控系统能否支持高并发的请求,直接就决定了这个监控系统能否使用。试想,如果一个监控系统三天两头出问题,那么用户可能就会流失,甚至是放弃这个监控系统的使用,转而去寻求更好的解决方案。

 

    监控用户的使用过程中,对监控平台的期望SLA可能是100%,而实际能达到的SLA可能是99.9%(每年停机约9小时)。随着业务和技术的不断发展,监控用户对监控系统的要求也越来越高,SLA能不能再继续提高呢?

 

    实际上,SLA的提高1个9,对系统的挑战都是非常大的,比如我们的架构是否支持,架构设计是否合理,架构有没有冗余,能不能支持水平横向扩容,存不存在单点故障,服务器资源是否足够,系统的并发是否是一条直线,等等众多的因素,直接决定了我们提供的监控系统SLA能否再继续提高。理想情况是,架构有冗余,当链路出现故障时,能够自动切换,能够有备机顶替;当容量出现不足时,能够加服务器就扩容,而能够自动负载均衡。

 

    所以,我们在设计监控系统的时候,一定要学习互联网架构中的高并发,高可用,分布式架构设计方案。此后,无论是增加功能模块,或者是系统的整体升级,有了架构上的保障,可以做到按需进行升级扩展,而不用担心系统可用性问题,升级变更扩展对用户都是无感知的。

 

3.系统可靠性毫无保障

 

    当监控的主机规模达到5000设备,1万设备的时候,一般监控系统会出现瓶颈,系统的QPS持续增长,能否支撑7*24小时、365天稳定运行,是一个非常大的挑战,可以说,监控系统从来都是一个高并发系统,同时,也是一个大数据库系统,比如日增5T,10T,50T的数据,并且要求详细历史数据,保存周期需要7天,30天,甚至是1年,而趋势数据(将历史数据进行归档,如按小时的max,min,avg存储)则要求保留1年,2年或者是更久,那么监控系统的数据可能达到PB数据级别,其数据处理之道,和海量的大数据处理系统,有异曲同工之妙,采集->清洗->分析->入库->使用

 

    数据上报延迟,大致的原因有三个,一是采集器出现的问题,无法按照既定的周期采集到数据,或因原始数据不存在,或因采集器达到本身性能上限问题;二是监控系统的清洗,处理分析环节出现堵塞,表现为采集上报正常,数据未入库;三是数据处理后,不得正常入库,即为监控的数据库存在问题,表现为数据写入慢,查询慢,超过数据库的上限。这3种情况,不论是哪种情况,对用户来说,都是不可用的。

 

    不误报,不漏报,不延迟,这是对监控系统的基本要求。误报就是数据处理出现问题,让用户降低对监控的信任感,如果长期存在误报情况,那么用户就会失去对监控的信任,逐步抛弃此监控系统。漏报就是本该发出的告警而没有发送出来,这种情况就更严重了,严重影响了用户的正常使用,严重降低了用户的预期,宛如晚点的飞机,不能准时达到目的地。延迟就是告警现在产生,明天才收到,这个情况说明监控系统已处于不可用状态。在本该故障的时候,告警收不到,在故障结束时,告警发出来,用户会完全不信任这个监控系统。如果监控系统连告警这个基本事情都做不好,那么算不上一个合格的监控系统,用户会将这个监控系统当做一个噪音对待。

 

 

    当我们把数据上报,告警问题都解决后,系统能够正常工作,又会面临新的问题。用户反馈,能不能将告警做得更智能一点呢?试想一下,告警模块正常工作,用户每天收到1000条告警,甚至收到10000条告警,用户也会疯掉,这简直就是告警“轰炸机”,过多的告警,成为了噪音,干扰正常判断。因此,告警能否收敛的问题,成为了头等大事。

 

    告警收敛,是指将多条策略相同、目标范围不同的告警进行合并发送,按照一定的规则进行收敛的一种告警发送方式。比如,我们一个云区域的网络故障,会造成该区域下所有设备不可达,那么ping不可达告警,则会逐条发送,如果该区域下1000条机器,是发送1条告警好呢?还是1000条好呢?相信绝大多数正常人会只想接收1条重要告警即可。告警收敛,将告警极大提高告警的精确性,真正让我们做到运筹帷幄之中而不慌乱,不至于让我们天天神经紧张,天天处于狼来了的局面,因为过多的告警,有温水煮青蛙效应,让我们会逐步失去对告警的敏感度,逐渐会因为过多的告警而完全不关注告警。

 

    有了告警收敛,是不是就可以高枕无忧了呢?不,我们还需要故障关联 ,故障自动分析,为什么需要这个功能呢?试想,一个机架掉电,引起了15台设备全部宕机,从而引发了一系列故障,如API超时,HTTP拨测失败,DB连接数增长,那么能否找到root-cause呢?能否提供一条重要告警帮我们自动分析故障的根因呢?此时,故障关联和故障自动分析,显得格外重要。所以,监控系统,必须具备故障关联分析的能力,为我们的运维决策提供更准确的信息。

 

    此外,监控系统能否对当前环境的性能进行分析,对系统容量分析,也将是一个重要能力,如对趋势预测,什么时候我们应该对业务进行扩容服务器,什么时候应该进行缩容服务器,当前的性能是否足够,是否还有优化的空间。而监控系统,因为有数据存在,是完全可以提供这一系列数据作为重要依据的。

 

4.技术落后于业务发展

    随着业务不断发展,组织架构会根据业务形态进行调整,不同人员,需要对应不同的权限,需要划分更多角色,如超级管理员,分级管理员,普通管理员,普通用户,甚至于针对菜单按钮级别更细粒度权限管控需求。如果监控系统一开始没有考虑到这些需求,则很难应对业务的增长需求。随着业务的发展,公司为了降低成本,可以将一些常规事项外包出去,此时,对权限的分级控制显得格外重要。在这个时候,监控系统不再是一个孤立的系统,必须和统一用户登录认证系统集成,实现配置,查询分离,才能够满足组织的业务发展。

 

    随着业务不断发展,业务对监控系统提出了更高要求,比如要求“微秒级监控数据采样”,要求“秒级告警”,要求监控系统提供100%可靠信息,根据监控系统提供的系统容量指标数据,对业务应用的目标服务器、容器进行扩容或者缩容。监控系统作为一个底层依赖系统,此时将发挥更大价值。

 

4.智能监控建设的实践-纸上得来终觉浅

    在建设监控系统的时候,我们清楚以上的问题所在,那么,我们是如何解决以上这些问题的呢?下面,我们来详细看看具体的实践。
 

1.降低使用门槛-开箱即用

 

    对于服务器设备,系统初始化后,默认就安装了Agent,无需任何配置,就可以采集到主机监控数据,如在CMDB中配置了进程信息,则会采集进程状态数据。

    其中进程指标基本的如下所示

 

2.和告警风暴说再见

    当主机默认接入后,即可自动添加默认告警策略,如达到阈值,即可产生告警,如下所示,是一些默认告警策略。

 

    为了防止告警过多对用户造成干扰 ,我们使用了四大锦囊法宝,确保用户收到的告警是有效的。

 

    锦囊一-告警收敛。如下所示的事件中,可以看到收敛规则有效减少了告警事件收敛,产生的事件和通知比例1:100,甚至更高。从设计上原生支持的告警收敛功能,有效阻止了告警风暴产生。

    锦囊二-告警抑制。通常,由于用户不同的实际需求,会对同一告警内容配置不同的阈值。比如配置磁盘空间使用率告警,有80%预警,90%警告,95%严重,那么这3条策略如何工作呢?假如当前磁盘空间使用率已经达到96%,是产生3条告警通知吗?实际情况中,如果监控系统产生了3条告警,那这个监控系统就会被用户认为是智障了。所以,对于同纬度的策略,我们只会发送告警级别的告警,即只会产生95%严重级别的告警通知。

 

    锦囊三-告警汇总。既使我们的监控系统已经有了告警收敛,告警抑制这两大功能,我们依然不能解决同一时间发送大量告警问题,比如多条告警规则同时满足,那么还是可能产生告警洪水风暴问题。因此,对于告警汇总功能,实在是有必要。对于同一时候涌入的大量告警,对于同维度的告警进行汇总,对于不同策略,则进行合并告警。有了告警汇总功能,我们就可以放心接收告警了。

 

    锦囊四-告警分析。通过对历史告警数据的挖掘分析,我们还可以找到异常告警,以便更好的分析原始数据和告警阈值,从而为告警配置和无阈值告警提供更好的数据支撑。

3.功能扩展容易-即插即用

4.用户权限控制-按需授权

    按监控提供的功能进行查看和管理两种基本的划分,一般来说有如下几个用户场景:

  • 告警通知的接收者:如运维,开发,测试,产品等。适用申请查看类和屏蔽类操作。

  • 监控的配置者:如运维。适用于申请查看+管理类操作。

  • 监控平台的管理者:适用于全局的功能。

5.自动化筑基石-效率优先

    有别于其他的监控系统,定义采集,直接在系统中就可以完成,无需使用其他第三方控制系统或者登录服务器部署。所有的配置都能够在界面上完成,包括插件编写,我们只需要打开页面制作插件即可。

 

     动态采集目标,当模块中的主机有增加或减少的时候,可以自动下发插件到目标机器,而无法人工部署采集插件。

 

    同理,告警策略中的目标范围也是自动匹配,而不用对新增加的主机或模块进行策略编辑,范围自动生效。

 

5.监控建设的总结-身经百战忆往昔

 

    在监控平台的建设过程中,经过不断实践,不断总结经验,监控平台建设的功能会逐步成熟,但是,如果只把监控平台当做核心目标来建设,会遇到非监控本身的问题,比如怎么将发布和监控结合,如何在发布期间屏蔽告警。如何让监控联动CMDB,怎么打通监控系统和运维自动化系统,怎么打通监控与流水线发布系统,怎么打通监控和运维的各个环节?这个问题,不是独立的一个监控系统能解决的,而是需要一个可扩展的可定制的运维平台才能解决的。

 

    回顾监控体系的建设,我们总结如下的经验:

1.确定目标,搞清楚需求,请不要盲目行动,仔细分析业务需求,然后选择相应的监控方案,指定清晰的业务需求规划和项目规划,弄清楚是需要一个烟囱式监控系统还是一个互通有无的运维平台体系。

 

2.尽量使用成熟稳定的开源平台,如考虑使用Zabbix、Pormetheus等,如果监控规模过大,可能会存在性能和其他使用方面问题。这里,笔者推荐选择使用蓝鲸[5]这样成熟稳定的平台,则可以很好的解决容量和性能问题,因为蓝鲸平台是天然支持海量并发场景,能够水平扩容,遇到了容量不够的问题,也只需要扩容对应的服务模块即可。

 

3.伴随着业务不断的发展和技术的不断迭代更新,监控系统也会不断优化总结,迭代更新。因此,没有一劳永逸的方案,只有不断的动态发展平衡。

 

4.要从软件设计者的角度去做监控系统,面对监控需求,从现象到本质去更好的满足使用场景,而不是仅仅作为一个工具,因为监控和运维是密切关联的,是需要从数据生产到消费,再到分析关联,为运维的目标-“提效、提质、提能”而添砖加瓦。

 

     因此,运维曰:监控,业务之大事,死生之地,存亡之道,不可不察也

 

体验地址

如需下载体验,可点击下方 监控日志套餐 下载按钮。

监控日志套餐需在基础套餐上运行。

蓝鲸社区版V6.0.3正式版

部署福利活动:点击跳转

我们的文章到此就结束啦,如果你喜欢今天的 Python 教程,请持续关注Python实用宝典。

有任何问题,可以在公众号后台回复:加群,回答相应验证信息,进入互助群询问。

原创不易,希望你能在下面点个赞和在看支持我继续创作,谢谢!


​Python实用宝典 ( pythondict.com )
不只是一个宝典
欢迎关注公众号:Python实用宝典

Prometheus + Granafa 40分钟构建MySQL监控平台实战教程

Prometheus + Granafa 概述

对于MySQL的监控平台,相信大家实现起来有很多了:基于天兔的监控,还有基于zabbix相关的二次开发。相信很多同行都应该已经开始玩起来了。我这边的选型是Prometheus + Granafa的实现方式。简而言之就是我现在的生产环境使用的是prometheus,还有就是granafa满足的我的日常工作需要。在入门的简介和安装,大家可以参考这里:

https://blog.51cto.com/cloumn/detail/77

1、首先看下我们的监控效果、mysql主从

构建高大上的MySQL监控平台

2、mysql状态:

构建高大上的MySQL监控平台

构建高大上的MySQL监控平台

3、缓冲池状态:

构建高大上的MySQL监控平台

exporter 相关部署实战教程

1、安装exporter

    [root@controller2 opt]# https://github.com/prometheus/mysqld_exporter/releases/download/v0.10.0/mysqld_exporter-0.10.0.linux-amd64.tar.gz
    [root@controller2 opt]# tar -xf mysqld_exporter-0.10.0.linux-amd64.tar.gz 

2、添加mysql 账户:

    GRANT SELECT, PROCESS, SUPER, REPLICATION CLIENT, RELOAD ON *.* TO 'exporter'@'%' IDENTIFIED BY 'localhost';
    flush privileges;

3、编辑配置文件:

    [root@controller2 mysqld_exporter-0.10.0.linux-amd64]# cat /opt/mysqld_exporter-0.10.0.linux-amd64/.my.cnf 
    [client]
    user=exporter
    password=123456

4、设置配置文件:

    [root@controller2 mysqld_exporter-0.10.0.linux-amd64]# cat /etc/systemd/system/mysql_exporter.service 
    [Unit]
    Description=mysql Monitoring System
    Documentation=mysql Monitoring System

    [Service]
    ExecStart=/opt/mysqld_exporter-0.10.0.linux-amd64/mysqld_exporter \
             -collect.info_schema.processlist \
             -collect.info_schema.innodb_tablespaces \
             -collect.info_schema.innodb_metrics  \
             -collect.perf_schema.tableiowaits \
             -collect.perf_schema.indexiowaits \
             -collect.perf_schema.tablelocks \
             -collect.engine_innodb_status \
             -collect.perf_schema.file_events \
             -collect.info_schema.processlist \
             -collect.binlog_size \
             -collect.info_schema.clientstats \
             -collect.perf_schema.eventswaits \
             -config.my-cnf=/opt/mysqld_exporter-0.10.0.linux-amd64/.my.cnf

    [Install]
    WantedBy=multi-user.target

5、添加配置到prometheus server

      - job_name: 'mysql'
        static_configs:
         - targets: ['192.168.1.11:9104','192.168.1.12:9104']

6、测试看有没有返回数值:

http://192.168.1.12:9104/metrics

正常我们通过mysql_up可以查询倒mysql监控是否已经生效,是否起起来

    #HELP mysql_up Whether the MySQL server is up.
    #TYPE mysql_up gauge
    mysql_up 1

监控相关指标

在做任何一个东西监控的时候,我们要时刻明白我们要监控的是什么,指标是啥才能更好的去监控我们的服务,在mysql里面我们通常可以通过一下指标去衡量mysql的运行情况:mysql主从运行情况、查询吞吐量、慢查询情况、连接数情况、缓冲池使用情况以及查询执行性能等。

主从复制运行指标:

1、主从复制线程监控:

大部分情况下,很多企业使用的都是主从复制的环境,监控两个线程是非常重要的,在mysql里面我们通常是通过命令:

    MariaDB [(none)]> show slave status\G;
    *************************** 1. row ***************************
                   Slave_IO_State: Waiting for master to send event
                      Master_Host: 172.16.1.1
                      Master_User: repl
                      Master_Port: 3306
                    Connect_Retry: 60
                  Master_Log_File: mysql-bin.000045
              Read_Master_Log_Pos: 72904854
                   Relay_Log_File: mariadb-relay-bin.000127
                    Relay_Log_Pos: 72905142
            Relay_Master_Log_File: mysql-bin.000045
                 Slave_IO_Running: Yes
                Slave_SQL_Running: Yes

Slave_IO_Running、Slave_SQL_Running两个线程正常那么说明我们的复制集群是健康状态的。

MySQLD Exporter中返回的样本数据中通过mysql_slave_status_slave_sql_running来获取主从集群的健康状况。

    # HELP mysql_slave_status_slave_sql_running Generic metric from SHOW SLAVE STATUS.
    # TYPE mysql_slave_status_slave_sql_running untyped
    mysql_slave_status_slave_sql_running{channel_name="",connection_name="",master_host="172.16.1.1",master_uuid=""} 1

2、主从复制落后时间:

在使用show slave status
里面还有一个关键的参数Seconds_Behind_Master。Seconds_Behind_Master表示slave上SQL thread与IO thread之间的延迟,我们都知道在MySQL的复制环境中,slave先从master上将binlog拉取到本地(通过IO thread),然后通过SQL
thread将binlog重放,而Seconds_Behind_Master表示本地relaylog中未被执行完的那部分的差值。所以如果slave拉取到本地的relaylog(实际上就是binlog,只是在slave上习惯称呼relaylog而已)都执行完,此时通过show slave status看到的会是0

Seconds_Behind_Master: 0

MySQLD Exporter中返回的样本数据中通过mysql_slave_status_seconds_behind_master 来获取相关状态。

    # HELP mysql_slave_status_seconds_behind_master Generic metric from SHOW SLAVE STATUS.
    # TYPE mysql_slave_status_seconds_behind_master untyped
    mysql_slave_status_seconds_behind_master{channel_name="",connection_name="",master_host="172.16.1.1",master_uuid=""} 0

查询吞吐量:

说到吞吐量,那么我们如何从那方面来衡量呢? 
通常来说我们可以根据mysql 的插入、查询、删除、更新等操作来

为了获取吞吐量,MySQL 有一个名为 Questions 的内部计数器(根据 MySQL
用语,这是一个服务器状态变量),客户端每发送一个查询语句,其值就会加一。由 Questions 指标带来的以客户端为中心的视角常常比相关的Queries
计数器更容易解释。作为存储程序的一部分,后者也会计算已执行语句的数量,以及诸如PREPARE 和 DEALLOCATE PREPARE
指令运行的次数,作为服务器端预处理语句的一部分。可以通过命令来查询:

    MariaDB [(none)]> SHOW GLOBAL STATUS LIKE "Questions";
    +---------------+-------+
    | Variable_name | Value |
    +---------------+-------+
    |
 Questions     | 15071 |
    +---------------+-------+

MySQLD Exporter中返回的样本数据中通过mysql_global_status_questions反映当前Questions计数器的大小:

    # HELP mysql_global_status_questions Generic metric from SHOW GLOBAL STATUS.
    # TYPE mysql_global_status_questions untyped
    mysql_global_status_questions 13253

当然由于prometheus
具有非常丰富的查询语言,我们可以通过这个累加的计数器来查询某一短时间内的查询增长率情况,可以做相关的阈值告警处理、例如一下查询2分钟时间内的查询情况:

rate(mysql_global_status_questions[2m])

当然上面是总量,我们可以分别从监控读、写指令的分解情况,从而更好地理解数据库的工作负载、找到可能的瓶颈。通常,通常,读取查询会由 Com_select
指标抓取,而写入查询则可能增加三个状态变量中某一个的值,这取决于具体的指令:

Writes = Com_insert + Com_update + Com_delete

下面我们通过命令获取插入的情况:

    MariaDB [(none)]> SHOW GLOBAL STATUS LIKE "Com_insert";
    +---------------+-------+
    | Variable_name | Value |
    +---------------+-------+
    |
 Com_insert    | 10578 |
    +---------------+-------+

从MySQLD
Exporter的/metrics返回的监控样本中,可以通过global_status_commands_total获取当前实例各类指令执行的次数:

    # HELP mysql_global_status_commands_total Total number of executed MySQL commands.
    # TYPE mysql_global_status_commands_total counter
    mysql_global_status_commands_total{command="create_trigger"} 0
    mysql_global_status_commands_total{command="create_udf"} 0
    mysql_global_status_commands_total{command="create_user"} 1
    mysql_global_status_commands_total{command="create_view"} 0
    mysql_global_status_commands_total{command="dealloc_sql"} 0
    mysql_global_status_commands_total{command="delete"} 3369
    mysql_global_status_commands_total{command="delete_multi"} 0

慢查询性能

查询性能方面,慢查询也是查询告警的一个重要的指标。MySQL还提供了一个Slow_queries的计数器,当查询的执行时间超过long_query_time的值后,计数器就会+1,其默认值为10秒,可以通过以下指令在MySQL中查询当前long_query_time的设置:

    MariaDB [(none)]> SHOW VARIABLES LIKE 'long_query_time';
    +-----------------+-----------+
    | Variable_name   | Value     |
    +-----------------+-----------+
    |
 long_query_time | 10.000000 |
    +-----------------+-----------+
    1 row in set (0.00 sec)

当然我们也可以修改时间

    MariaDB [(none)]> SET GLOBAL long_query_time = 5;
    Query OK, 0 rows affected (0.00 sec)

然后我们而已通过sql语言查询MySQL实例中Slow_queries的数量:

    MariaDB [(none)]> SHOW GLOBAL STATUS LIKE "Slow_queries";
    +---------------+-------+
    | Variable_name | Value |
    +---------------+-------+
    |
 Slow_queries  | 0     |
    +---------------+-------+
    1 row in set (0.00 sec)

MySQLD
Exporter返回的样本数据中,通过mysql_global_status_slow_queries指标展示当前的Slow_queries的值:

    # HELP mysql_global_status_slow_queries Generic metric from SHOW GLOBAL STATUS.
    # TYPE mysql_global_status_slow_queries untyped
    mysql_global_status_slow_queries 0

同样的,更具根据Prometheus 慢查询语句我们也可以查询倒他某段时间内的增长率:

rate(mysql_global_status_slow_queries[5m])

连接数监控

监控客户端连接情况相当重要,因为一旦可用连接耗尽,新的客户端连接就会遭到拒绝。MySQL 默认的连接数限制为 151。

    MariaDB [(none)]> SHOW VARIABLES LIKE 'max_connections';
    +-----------------+-------+
    | Variable_name   | Value |
    +-----------------+-------+
    |
 max_connections | 151   |
    +-----------------+-------+

当然我们可以修改配置文件的形式来增加这个数值。与之对应的就是当前连接数量,当我们当前连接出来超过系统设置的最大值之后常会出现我们看到的Too many
connections(连接数过多),下面我查找一下当前连接数:

    MariaDB [(none)]> SHOW GLOBAL STATUS LIKE "Threads_connected";
    +-------------------+-------+
    | Variable_name     | Value |
    +-------------------+-------+
    |
 Threads_connected | 41     |
    +-------------------+-------

当然mysql 还提供Threads_running 这个指标,帮助你分隔在任意时间正在积极处理查询的线程与那些虽然可用但是闲置的连接。

    MariaDB [(none)]> SHOW GLOBAL STATUS LIKE "Threads_running";
    +-----------------+-------+
    | Variable_name   | Value |
    +-----------------+-------+
    |
 Threads_running | 10     |
    +-----------------+-------+

如果服务器真的达到 max_connections
限制,它就会开始拒绝新的连接。在这种情况下,Connection_errors_max_connections
指标就会开始增加,同时,追踪所有失败连接尝试的Aborted_connects 指标也会开始增加。

MySQLD Exporter返回的样本数据中:

    # HELP mysql_global_variables_max_connections Generic gauge metric from SHOW GLOBAL VARIABLES.
    # TYPE mysql_global_variables_max_connections gauge
    mysql_global_variables_max_connections 151         

表示最大连接数

    # HELP mysql_global_status_threads_connected Generic metric from SHOW GLOBAL STATUS.
    # TYPE mysql_global_status_threads_connected untyped
    mysql_global_status_threads_connected 41

表示当前的连接数

    # HELP mysql_global_status_threads_running Generic metric from SHOW GLOBAL STATUS.
    # TYPE mysql_global_status_threads_running untyped
    mysql_global_status_threads_running 1

表示当前活跃的连接数

    # HELP mysql_global_status_aborted_connects Generic metric from SHOW GLOBAL STATUS.
    # TYPE mysql_global_status_aborted_connects untyped
    mysql_global_status_aborted_connects 31

累计所有的连接数

    # HELP mysql_global_status_connection_errors_total Total number of MySQL connection errors.
    # TYPE mysql_global_status_connection_errors_total counter
    mysql_global_status_connection_errors_total{error="internal"} 0
    #服务器内部引起的错误、如内存硬盘等
    mysql_global_status_connection_errors_total{error="max_connections"} 0
    #超出连接处引起的错误

当然根据prom表达式,我们可以查询当前剩余可用的连接数:

mysql_global_variables_max_connections - mysql_global_status_threads_connected

查询mysq拒绝连接数

mysql_global_status_aborted_connects

缓冲池情况:

MySQL 默认的存储引擎 InnoDB
使用了一片称为缓冲池的内存区域,用于缓存数据表与索引的数据。缓冲池指标属于资源指标,而非工作指标,前者更多地用于调查(而非检测)性能问题。如果数据库性能开始下滑,而磁盘
I/O 在不断攀升,扩大缓冲池往往能带来性能回升。 
默认设置下,缓冲池的大小通常相对较小,为 128MiB。不过,MySQL 建议可将其扩大至专用数据库服务器物理内存的 80% 大小。我们可以查看一下:

    MariaDB [(none)]> show global variables like 'innodb_buffer_pool_size';
    +-------------------------+-----------+
    | Variable_name           | Value     |
    +-------------------------+-----------+
    |
 innodb_buffer_pool_size | 134217728 |
    +-------------------------+-----------+

MySQLD Exporter返回的样本数据中,使用mysql_global_variables_innodb_buffer_pool_size来表示。

    # HELP mysql_global_variables_innodb_buffer_pool_size Generic gauge metric from SHOW GLOBAL VARIABLES.
    # TYPE mysql_global_variables_innodb_buffer_pool_size gauge
    mysql_global_variables_innodb_buffer_pool_size 1.34217728e+08

    Innodb_buffer_pool_read_requests记录了正常从缓冲池读取数据的请求数量。可以通过以下指令查看

    MariaDB [(none)]> SHOW GLOBAL STATUS LIKE "Innodb_buffer_pool_read_requests";
    +----------------------------------+-------------+
    | Variable_name                    | Value       |
    +----------------------------------+-------------+
    |
 Innodb_buffer_pool_read_requests | 38465 |
    +----------------------------------+-------------+

MySQLD
Exporter返回的样本数据中,使用mysql_global_status_innodb_buffer_pool_read_requests来表示。

    # HELP mysql_global_status_innodb_buffer_pool_read_requests Generic metric from SHOW GLOBAL STATUS.
    # TYPE mysql_global_status_innodb_buffer_pool_read_requests untyped
    mysql_global_status_innodb_buffer_pool_read_requests 2.7711547168e+10

当缓冲池无法满足时,MySQL只能从磁盘中读取数据。Innodb_buffer_pool_reads即记录了从磁盘读取数据的请求数量。通常来说从内存中读取数据的速度要比从磁盘中读取快很多,因此,如果Innodb_buffer_pool_reads的值开始增加,可能意味着数据库的性能有问题。
可以通过以下只能查看Innodb_buffer_pool_reads的数量

    MariaDB [(none)]> SHOW GLOBAL STATUS LIKE "Innodb_buffer_pool_reads";
    +--------------------------+-------+
    | Variable_name            | Value |
    +--------------------------+-------+
    |
 Innodb_buffer_pool_reads | 138  |
    +--------------------------+-------+
    1 row in set (0.00 sec)

MySQLD
Exporter返回的样本数据中,使用mysql_global_status_innodb_buffer_pool_read_requests来表示。

    # HELP mysql_global_status_innodb_buffer_pool_reads Generic metric from SHOW GLOBAL STATUS.
    # TYPE mysql_global_status_innodb_buffer_pool_reads untyped
    mysql_global_status_innodb_buffer_pool_reads 138

通过以上监控指标,以及实际监控的场景,我们可以利用PromQL快速建立多个监控项。可以查看两分钟内读取磁盘的增长率的增长率:

rate(mysql_global_status_innodb_buffer_pool_reads[2m])

官方模板ID

上面是我们简单列举的一些指标,下面我们使用granafa给 MySQLD_Exporter添加监控图表:

  • 主从主群监控(模板7371):

  • 相关mysql 状态监控7362:

  • 缓冲池状态7365:

  • 简单的告警规则

除了相关模板之外,没有告警规则那么我们的监控就是不完美的,下面列一下我们的监控告警规则

    groups:
    - name: MySQL-rules
      rules:
      - alert: MySQL Status 
        expr: up == 0
        for: 5s 
        labels:
          severity: warning
        annotations:
          summary: "{{$labels.instance}}: MySQL has stop !!!"
          description: "检测MySQL数据库运行状态"

      - alert: MySQL Slave IO Thread Status
        expr: mysql_slave_status_slave_io_running == 0
        for: 5s 
        labels:
          severity: warning
        annotations: 
          summary: "{{$labels.instance}}: MySQL Slave IO Thread has stop !!!"
          description: "检测MySQL主从IO线程运行状态"

      - alert: MySQL Slave SQL Thread Status 
        expr: mysql_slave_status_slave_sql_running == 0
        for: 5s 
        labels:
          severity: warning
        annotations: 
          summary: "{{$labels.instance}}: MySQL Slave SQL Thread has stop !!!"
          description: "检测MySQL主从SQL线程运行状态"

      - alert: MySQL Slave Delay Status 
        expr: mysql_slave_status_sql_delay == 30
        for: 5s 
        labels:
          severity: warning
        annotations: 
          summary: "{{$labels.instance}}: MySQL Slave Delay has more than 30s !!!"
          description: "检测MySQL主从延时状态"

      - alert: Mysql_Too_Many_Connections
        expr: rate(mysql_global_status_threads_connected[5m]) > 200
        for: 2m
        labels:
          severity: warning
        annotations:
          summary: "{{$labels.instance}}: 连接数过多"
          description: "{{$labels.instance}}: 连接数过多,请处理 ,(current value is: {{ $value }})"  

      - alert: Mysql_Too_Many_slow_queries
        expr: rate(mysql_global_status_slow_queries[5m]) > 3
        for: 2m
        labels:
          severity: warning
        annotations:
          summary: "{{$labels.instance}}: 慢查询有点多,请检查处理"
          description: "{{$labels.instance}}: Mysql slow_queries is more than 3 per second ,(current value is: {{ $value }})"

2、添加规则到prometheus:

    rule_files:
      - "rules/*.yml" 

3、打开web ui我们可以看到规则生效了:

构建高大上的MySQL监控平台

总结

到处监控mysql的相关状态已经完成,大家可以根据mysql更多的监控指标去完善自己的监控,当然这一套就是我用在线上环境的,可以参考参考。

来源:https://blog.51cto.com/xiaoluoge/2476375
作者:小罗ge11

我们的文章到此就结束啦,如果你喜欢今天的 Python 教程,请持续关注Python实用宝典。

有任何问题,可以在公众号后台回复:加群,回答相应验证信息,进入互助群询问。

原创不易,希望你能在下面点个赞和在看支持我继续创作,谢谢!


​Python实用宝典 ( pythondict.com )
不只是一个宝典
欢迎关注公众号:Python实用宝典

Python 函数耗时异常自动化监控实战教程

来源:文呓

本文内容包括Python性能可视化分析,逻辑优化,及根据不同的模型动态计算安全阈值,实现各个函数耗时及程序总耗时的自动化监控预警

在做Python性能分析优化的时候,可以借助cProfile生成性能数据文件,通过pstats获取详细耗时分布数据,结合gprof2dot脚本生成函数调用栈结构图做可视化分析,提高性能分析的效率。

接着从具体的耗时分布,先从占用大头的函数分析具体逻辑实现,逐步优化,同时保存pstats函数耗时平均值数据作为后续异常自动化监控的样本数据。

实现耗时自动化监控必须是可以根据算法动态调整安全阈值,而不是人工定死安全阈值范围,这样才可以实现异常监控的自循环和迭代校准。

一、性能数据函数耗时采集及可视化报表生成

1. 性能数据文件保存(cProfile)

首先是性能数据文件的保存,cProfile和profile提供了Python程序的确定性性能分析。profile是一组统计数据,用来描述程序的各个部分执行的频率和时间。在程序开始的时候调用enable开始性能数据采集,结束的时候调用dump_stats停止性能数据采集并保存性能数据到指定路径的文件。

import cProfile
# 程序开始的时候打开数据采集开关
pr = cProfile.Profile()
pr.enable()

# 在程序运行结束的时候dump性能数据到指定路径文件中,profliePath为保存文件的绝对路径参数
pr.dump_stats(profliePath)

2. 详细性能数据读取查看

保存性能数据到文件之后,可以用pstats读取文件中的数据,profile统计数据可以通过pstats模块格式化为报表。

import pstats 
# 读取性能数据 
pS = pstats.Stats(profliePath) 
# 根据函数自身累计耗时做排序 
pS.sort_stats('tottime') 
# 打印所有耗时函数信息 
pS.print_stats()
print_stats()输出示例:
79837 function calls (75565 primitive calls) in 37.311 seconds
Ordered by: internal time
ncalls  tottime  percall  cumtime  percall  filename:lineno(function)
 2050    30.167    0.015   30.167    0.015  {time.sleep}
   16     6.579    0.411    6.579    0.411  {select.select}
    1     0.142    0.142    0.142    0.142  {method 'do_handshake' of '_ssl._SSLSocket' objects}
  434     0.074    0.000    0.074    0.000  {method 'read' of '_ssl._SSLSocket' objects}
    1     0.062    0.062    0.062    0.062  {method 'connect' of '_socket.socket' objects}
   37     0.046    0.001    0.046    0.001  {posix.read}
   14     0.024    0.002    0.024    0.002  {posix.fork}

输出字段说明:

  • ncalls  函数被调用次数(只有一个数字时表示不存在递归,有斜杠分割数字时,后面的数字表示非递归调用的次数)
  • tottime  函数总计运行时间,不包括子函数调用时间
  • percall  函数运行一次的平均时间,等于tottime/ncalls
  • cumtime 函数总计运行时间,包括子函数调用时间
  • percall  函数运行一次的平均时间,等于cumtime/ncalls
  • filename:lineno(function) 函数所在的文件名,函数的行号,函数名或基础框架函数类

如果要获取print_stats()里面各个字段信息可以通过如下方式:

# func————filename:lineno(function)
# cc ———— call count,调用次数 
# nc ———— ncalls
# tt ———— tottime
# ct ———— cumtime
# callers ———— 调用堆栈数组,每项数据包括了func, (cc, nc, tt, ct) 字段
for index in range(len(pS.fcn_list)): 
    func = pS.fcn_list[index] 
    cc, nc, tt, ct, callers = pS.stats[func]  
    print cc, nc, tt, ct, func, callers
    for func, (cc, nc, tt, ct) in callers.iteritems():
        print func,cc, nc, tt, ct

二、生成函数调用栈结构图(gprof2dot)教程

gprof2dot脚本把gprof或callgrind分析获得的信息,转化成一个以DOT语言描述的程序调用有向图对象,再通过Graphviz将DOT有向图对象渲染成图片,这样就可以很直观地看出整个程序的调用栈,包括函数所在的类和行数、耗时占比、函数递归次数、以及被调用的次数。

先从GitHub上下载gprof2dot.py脚本到本地,和执行的程序的脚本文件放在同一目录下,当然要使用这个脚本还需要安装graphviz,使用brew命令安装,若安装过程中遇到异常,根据异常提示执行命令安装需要的工具

brew install graphviz

生成程序函数调用栈结构图的逻辑可以参考如下逻辑实现,具体根据自身需求做下修改。

import os
# 获取当前gprof2dot.py脚本路径
gprof2dotPath = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'gprof2dot.py')
# 函数调用栈结构图保存文件名路径,这边使用生成PNG图片结果
dumpProfPath = profliePath.replace("stats", "png")
dumpCmd = "python %s -f pstats %s | dot -Tpng -o %s" % (gprof2dotPath, profliePath, dumpProfPath)
os.popen(dumpCmd)

三、性能分析及优化实战

在生成函数调用栈结构图之后,就可以很容易的看出各个函数之间的调用关系,每个方块内包括的信息包括函数所在的类和行数耗时百分比被调用次数,如果这个函数存在递归的情况,方块边缘会有一个回旋的箭头标明递归的次数

从结构图里面找到耗时占用较多的部分,分析具体函数的实现逻辑,定位具体耗时的原因,优化的策略如下:

  • 去除多余的逻辑:去除冗余代码
  • 优化递归函数:加日志打印递归时候的各个参数,如果发现很多参数都是重复的,可以加缓存,避免多余的递归消耗。
  • 归并通用逻辑调用:一个函数多次调用同一个子函数获取参数,查看这个子函数的调用是否可以进行整合归并,避免多余的函数调用。
  • 通过上下文环境判断测试程序的初始化是否必要,非必要情况下不进行测试环境的重置操作。

四、耗时异常自动化监控

如果是通过历史的耗时数据计算得到平均值+固定浮动百分比的方式,来配置耗时安全阈值参数实施异常监控存在很大的问题,因为函数执行的耗时容易受设备和运行环境的影响,人工固定浮动百分比的方式维护性差,数据本身不可迭代自循环,总不能每次出现误报问题之后都去手动调整参数。

这边监控的维度包括两方面,一方面是程序各个函数执行耗时的平均值,另一方面是完整程序执行的总耗时,在前期先把这些历史耗时数据保存在数据库中,供后续自动化异常监控的实现提供样本数据。

要实现自动化阈值调整,需要借助常规的模型算法实现,这边只对耗时单个维度的异常做自动化监控实现。

根据原理,无监督异常检测模型一般可分为以下几类:

  • 基于统计和概率模型:主要是对数据的分布做出假设,并找出假设下所定义的“异常”;
  • 线性模型:主要思想是通过线性方法找到合适的低维子空间使得异常点在其中区别于正常点;
  • 基于距离:这种方法认为异常点距离正常点比较远,通过比较数据点之间的距离区分异常点;
  • 基于密度:由于数据分布不均匀,绝对距离无法衡量数据点之间相对远近时,用局部密度表示数据点的异常情况;
  • 基于聚类:将数据点聚类,不属于任何簇、距离最近的簇较远、稀疏聚类里的点认为是异常点;
  • 基于树:通过划分子空间构建树模型寻找异常点。

异常耗时数据是波动的一维数据,这边就直接采用基于统计和概率模型的方式,根据保存的历史数据判断数据是否符合正态分布

若符合正态分布则用 μ+3δ(平均值+3倍标准差)的方式计算得到安全阈值

若不符合正态分布,则用Turkey 箱型图方案 Q+1.5IQR 计算安全阈值。

根据实际测试来看,随着样本数据的增加,会出现前期符合正态分布的函数耗时曲线,随着样本数据的增加会变成不符合正态分布。

Python中用于判断数据是否符合正态分布的代码如下,当pvalue值大于0.05时为正态分布,dataList是耗时数组数据:

from scipy import stats
import numpy
percallMean = numpy.mean(dataList) # 计算均值
# percallVar = numpy.var(dataList) # 求方差
percallStd = numpy.std(dataList) # 计算标准差
kstestResult = stats.kstest(dataList, 'norm', (percallMean, percallStd))
# 当pvalue值大于0.05为正态分布
if kstestResult[1] > 0.05:
    pass

1. 正态分布数据方案

在统计学中,如果一个数据分布近似正态,那么大约 68% 的数据值会在均值的一个标准差范围内,大约 95% 会在两个标准差范围内,大约 99.7% 会在三个标准差范围内。因此,如果任何数据点超过标准差的 3 倍,那么这些点很有可能是异常值或离群点。即正态分布的安全阈值上限为:percallMean + 3 * percallStd

 

2. Turkey 箱型图方案

基于正态分布的 3σ 法则或 Z 分数方法的异常检测是以假定数据服从正态分布为前提的,但实际数据往往并不严格服从正态分布。应用这种方法于非正态分布数据中判断异常值,其有效性是有限的。Tukey 箱型图是一种用于反映原始数据分布的特征常用方法,也可用于异常点识别。在识别异常点时其主要依靠实际数据,因此有其自身的优越性。

箱型图为我们提供了识别异常值的一个标准:异常值被定义为小于 Q1-1.5IQR 或大于 Q+1.5IQR 的值。虽然这种标准有点任意性,但它来源于经验判断,经验表明它在处理需要特别注意的数据方面表现不错。

计算箱型图安全阈值Python实现逻辑如下:

import numpy
percallMean = numpy.mean(dataList)  # 计算均值
boxplotQ1 = numpy.percentile(dataList, 25)
boxplotQ2 = numpy.percentile(dataList, 75)
boxplotIQR = boxplotQ2 - boxplotQ1
upperLimit =  boxplotQ2 + 1.5 * boxplotIQR

在程序实现中就是,在一个程序或用例执行完毕之后,先拿历史数据判断是否符合正态分布,当然历史样本数据至少要达到20个才比较准确,小于20个的时候就继续收集数据,不做异常判断。根据正态分布模型或箱型图模型计算安全阈值参数,判断当前各个函数耗时平均值或用例总耗时是否超过阈值,超过则预警。

高斯模型和箱型图两种方式阈值范围对比

这边给出stats文件数据汇总解析之后,根据相应的模型绘制耗时曲线及阈值或正态曲线及阈值的代码实现,statFolder参数替换成自己stats文件所在文件夹即可。

# coding=utf-8
import os
import pstats
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt
import traceback
from scipy import stats
import numpy

"""
汇总函数耗时平均值数据
"""
def dataSummary(array, fileName, fcn, percall):
    (funcPath, line, func) = fcn
    exists = False
    for item in array:
        if item["func"] == func and item["funcPath"] == funcPath and item["line"] == line:
            exists = True
            item["cost"].append({
                "percall": percall,
                "fileName": fileName
            })
    if not exists:
        array.append({
            "func": func,
            "funcPath": funcPath,
            "line": line,
            "cost": [{
                "percall": percall,
                "fileName": fileName
            }]
        })

"""
高斯函数计算Y值
"""
def gaussian(x, mu, delta):
    exp = numpy.exp(- numpy.power(x - mu, 2) / (2 * numpy.power(delta, 2)))
    c = 1 / (delta * numpy.sqrt(2 * numpy.pi))
    return c * exp

"""
读取汇总所有stats文件数据
"""
def readStatsFile(statFolder, filterData):
    for path, dir_list, file_list in os.walk(statFolder, "r"):
        for fileName in file_list:
            if fileName.find(".stats") > 0:
                fileAbsolutePath = os.path.join(path, fileName)
                pS = pstats.Stats(fileAbsolutePath)
             # 先对耗时数据从大到小进行排序
                pS.sort_stats('cumtime')
                # pS.print_stats()
                # 统计前100条耗时数据
                for index in range(100):
                    fcn = pS.fcn_list[index]
                    (funcPath, line, func) = fcn
                    # cc ———— call count,调用次数
                    # nc ———— ncalls,调用次数(只有一个数字时表示不存在递归;有斜杠分割数字时,后面的数字表示非递归调用的次数)
                    # tt ———— tottime,函数总计运行时间,除去函数中调用的子函数运行时间
                    # ct ———— cumtime,函数总计运行时间,含调用的子函数运行时间
                    cc, nc, tt, ct, callers = pS.stats[fcn]
                    # print fileName, func, cc, nc, tt, ct, callers
                    percall = ct / nc
                    # 只统计单次函数调用大于1毫秒的数据
                    if percall >= 0.001:
                        dataSummary(filterData, fileName, fcn, percall)

"""
绘制高斯函数曲线和安全阈值
"""
def drawGaussian(func, line, percallMean, threshold, percallList, dumpFolder):
    plt.title(func)
    plt.figure(figsize=(10, 8))
    for delta in [0.2, 0.5, 1]:
        gaussY = []
        gaussX = []
        for item in percallList:
            # 这边为了呈现正态曲线效果,减去平均值
            gaussX.append(item - percallMean)
            y = gaussian(item - percallMean, 0, delta)
            gaussY.append(y)
        plt.plot(gaussX, gaussY, label='sigma={}'.format(delta))
    # 绘制水位线
    plt.plot([threshold - percallMean, threshold - percallMean], [0, 5 * gaussian(percallMean, 0, 1)], color='red',
             linestyle="-", label="Threshold:" + str("%.5f" % threshold))
    plt.xlabel("Time(s)", fontsize=12)
    plt.legend()
    plt.tight_layout()
    # 可能不同类中包含相同的函数名,加上行数参数避免覆盖
    imagePath = dumpFolder + "cost_%s_%s.png" % (func, str(line))
    plt.savefig(imagePath)

"""
绘制耗时曲线和安全阈值
"""
def drawCurve(func, line, percallList, dumpFolder):
    boxplotQ1 = numpy.percentile(percallList, 25)
    boxplotQ2 = numpy.percentile(percallList, 75)
    boxplotIQR = boxplotQ2 - boxplotQ1
    upperLimit = boxplotQ2 + 1.5 * boxplotIQR
    # 不符合正态分布,绘制波动曲线
    timeArray = [i for i in range(len(percallList))]
    plt.title(dataItem["func"])
    plt.figure(figsize=(10, 8))
    # 绘制水位线
    plt.plot([0, len(percallList)], [upperLimit, upperLimit], color='red', linestyle="-",
             label="Threshold:" + str("%.5f" % upperLimit))
    plt.plot(timeArray, percallList, label=dataItem["func"] + "_" + str(dataItem["line"]))
    plt.ylabel("Time(s)", fontsize=12)
    plt.legend()
    plt.tight_layout()
    imagePath = dumpFolder + "cost_%s_%s.png" % (func, str(line))
    plt.savefig(imagePath)

if __name__ == "__main__":
    try:
        statFolder = "/Users/chenwenguan/Downloads/2aab7e17-a1b6-1253/"
        chartFolder = statFolder + "chart/"
        if not os.path.exists(chartFolder):
            os.mkdir(chartFolder)
        filterData = []
        readStatsFile(statFolder, filterData);
        for dataItem in filterData:
            percallList = map(lambda x: x["percall"], dataItem["cost"])
            func = dataItem["func"]
            line = dataItem["line"]
            # 样本个数大于20才进行绘制
            if len(percallList) > 20:
                percallMean = numpy.mean(percallList) # 计算均值
                # percallVar = numpy.var(percallMap) # 求方差
                percallStd = numpy.std(percallList)  # 计算标准差
                # pvalue值大于0.05为正太分布
                kstestResult = stats.kstest(percallList, 'norm', (percallMean, percallStd))
                print "percallStd:%s, pvalue:%s" % (percallStd, kstestResult[1])
                # 符合正态分布绘制分布曲线
                if kstestResult[1] > 0.05:
                    threshold = percallMean + 3 * percallStd
                    drawGaussian(func, line, percallMean, threshold, percallList, chartFolder)
                else:
                    drawCurve(func, line, percallList, chartFolder)
            else:
                pass
    except Exception:
        print 'exeption:' + traceback.format_exc()

两种耗时模型绘制的曲线效果图如下:

函数耗时高斯分布曲线及阈值效果示例

 

函数耗时曲线及Turkey箱型图阈值示例

我们的文章到此就结束啦,如果你喜欢今天的 Python 教程,请持续关注Python实用宝典。

有任何问题,可以在公众号后台回复:加群,回答相应验证信息,进入互助群询问。

原创不易,希望你能在下面点个赞和在看支持我继续创作,谢谢!


​Python实用宝典 ( pythondict.com )
不只是一个宝典
欢迎关注公众号:Python实用宝典

什么是腾讯蓝鲸运维体系?附部署教程

腾讯蓝鲸智云是一个高效的运维基础服务自动化体系,拥有支撑数百款腾讯业务的经验沉淀,是一个相对成熟稳定的运维系统。

简而言之,基于蓝鲸这套体系,你可以很方便地管控多个主机、执行作业、监控其运行状态。

此外,基于蓝鲸体系的PaaS平台,你可以非常方便地自动化部署那些使用Golang或Python开发面向内网的SaaS应用。

当然,蓝鲸这套体系不仅仅可以用于运维,比如蓝鲸监控赋予了用户比较大的灵活性,你可以配置脚本采集上报任意你需要监测的数据到蓝鲸监控并配置告警。

社区版是腾讯蓝鲸为运维社区用户免费开放的一套可独立搭建部署的版本,下面给大家提供单机部署的完整指引。

1.软件包准备

进入蓝鲸官网获取「蓝鲸社区版」安装包和部署脚本,并上传到服务器的data目录下,我这里在 xshell 使用 rz -E 上传文件包:

2.解压软件包

使用 tar -xvf 命令解压社区版软件包:

tar -xvf /data/bkce_src-xx.tar.gz -C /data/

xx 是你所下载的社区版版本号。

软件包比较大,解压需要一定时间。

3.安装证书:

运行以下命令,获取MAC地址,拷贝下来到官网输入证书并下载:

cat /sys/class/net/eth0/address
# 52:54:00:xx:xx:xx

将下载完成的证书传到/data/目录下,并解压证书文件到 /data/src/cert 目录:

cd /data/install
install -d -m 755 /data/src/cert
tar -xvf /data/ssl_certificates.tar.gz -C /data/src/cert/

4.修改蓝鲸配置参数

解压各个产品软件包并拷贝 rpm 软件包:

cd /data/src/; for f in *gz;do tar xf $f; done
cp -a /data/src/yum /opt

修改 bk_install 脚本,如图在 job 处添加以下内容:

vim /data/install/bk_install
sed -i '/JAVA_OPTS/c JAVA_OPTS="-Xms128m -Xmx128m"' /etc/sysconfig/bk-job-*

去除 install_minibk 的 .path 配置:

sed  -i '33,34d' /data/install/install_minibk

在 install.config.3ip.sample 文件追加一行空行:

echo >> /data/install/install.config.3ip.sample

install.config 这个文件安装脚本会自动生成,无需自行配置。

5.开始安装

启动安装脚本,运行命令:

cd /data/install 
./install_minibk -y

安装过程中遇到失败的情况,请先定位排查解决后,再重新运行失败时的安装指令。

执行完部署后,执行降低内存消耗脚本。以确保环境的稳定:

cd /data/install

sed -i '/^cheaper/d' /data/bkce/etc/uwsgi-*.ini 

# 执行降低内存消耗脚本
bash bin/single_host_low_memory_config.sh tweak all

6.安装完成,访问蓝鲸

初始化蓝鲸业务拓扑:

./bkcli initdata topo

由于没有实际域名分配,所以需要配置你本地 PC 的 hosts 文件来访问;打开你电脑里的 hosts文件(windows: C:\windows\system32\drivers\etc\hosts, linux/mac: /etc/hosts)

将下面域名配置复制粘贴至底部,并保存!

10.0.0.1 paas.bktencent.com cmdb.bktencent.com job.bktencent.com jobapi.bktencent.com nodeman.bktencent.com

其中 10.0.0.1 记得替换为你的服务器地址,然后在机器上运行下列命令获取ADMIN账号的用户名和密码:

grep -E "BK_PAAS_ADMIN_USERNAME|BK_PAAS_ADMIN_PASSWORD" /data/install/bin/04-final/usermgr.env

打开下面网址并输入用户名和密码,就能成功访问蓝鲸了。

如果你想了解蓝鲸中配置平台、作业平台、监控平台等产品的使用方法,可以访问蓝鲸官方文档查询:

https://bk.tencent.com/docs/document/6.0/128/5859

下一篇文章,我们将给大家探讨蓝鲸监控的几种有趣的使用方法,基于蓝鲸监控的采集配置功能,我们能实现一些非常有趣的告警策略,敬请期待。

我们的文章到此就结束啦,如果你喜欢今天的 Python 教程,请持续关注Python实用宝典。

有任何问题,可以在公众号后台回复:加群,回答相应验证信息,进入互助群询问。

原创不易,希望你能在下面点个赞和在看支持我继续创作,谢谢!


​Python实用宝典 ( pythondict.com )
不只是一个宝典
欢迎关注公众号:Python实用宝典

py-spy:Python 程序的性能监控器

py-spy是用于Python程序的性能监控器。它使你可以直观地看到Python程序花费的时间,而无需重新启动程序或以任何方式修改代码。

py-spy的开销非常低:为了最大化提高速度,它是用Rust编写的,并且与配置的Python程序不在同一进程中运行。这意味着 py-spy 可以安全地用于生产环境的Python程序。

py-spy 可在 Linux,OSX,Windows 和 FreeBSD 上运行,并支持所有最新版本的CPython解释器(2.3-2.7和3.3-3.8版)进行性能分析。

1.安装

可以通过以下方式从 PyPI 安装预构建的二进制wheel文件:

pip install py-spy

你也可以从 GitHub Release Page 下载预构建的二进制文件,如果网络无法连接GitHub,你也可在 Python实用宝典 公众号后台回复 pyspy 下载。

2.用法

py-spy 在命令行中进行工作,获取你要从监控的程序的PID或你要运行的python程序的文件。分别有三种分析方法  recordtop以及dump

record

py-spy支持使用record命令将配置文件记录到文件中。例如,您可以通过执行以下操作来生成python进程的热力图

py-spy record -o profile.svg --pid 12345
# OR
py-spy record -o profile.svg -- python myprogram.py

它将生成一个交互式SVG文件,如下所示:

你可以使用参数 –format 更改文件格式。请参阅参考资料,py-spy record --help 以获取有关其他选项的信息,包括更改采样率,仅包含GIL的线程进行过滤,对本机C扩展进行概要分析,显示线程ID,概要分析子进程等。

Top

Top显示了在python程序中花费最多时间的函数的实时视图,类似于Unix top命令。使用以下命令运行:

py-spy top --pid 12345
# OR
py-spy top -- python myprogram.py

将显示你的python程序的实时函数消耗:

Dump

py-spy 还可以使用 dump 命令显示每个 python 线程的当前调用堆栈:

py-spy dump --pid 12345

这会将每个线程的调用堆栈以及其他一些基本进程信息转发到控制台:

对于需要一个调用堆栈来确定python程序挂在何处的情况,这很有用。该命令还可以通过设置 --locals 标志来打印出与每个堆栈帧关联的局部变量。

我们的文章到此就结束啦,如果你喜欢今天的 Python 教程,请持续关注Python实用宝典。

有任何问题,可以在公众号后台回复:加群,回答相应验证信息,进入互助群询问。

原创不易,希望你能在下面点个赞和在看支持我继续创作,谢谢!


​Python实用宝典 ( pythondict.com )
不只是一个宝典
欢迎关注公众号:Python实用宝典

接近完美的监控系统—普罗米修斯

普罗米修斯(Prometheus)是一个SoundCloud公司开源的监控和告警系统。当年,由于SoundCloud公司生产了太多的服务,传统的监控已经无法满足监控需求,于是他们在2012决定着手开发新的监控系统,即普罗米修斯。

普罗米修斯(下称普罗)的作者 Matt T.Proud 在2012年加入该公司,他从google的监控系统Borgmon中获得灵感,与另一名工程师Julius Volz合作开发了开源的普罗,后来其他开发人员陆续加入到该项目,最终于2015正式发布。

普罗基于Go语言开发,其架构图如下:

其中:

  • Prometheus Server: 用数据的采集和存储,PromQL查询,报警配置。
  • Push gateway: 用于批量,短期的监控数据的汇报总节点。
  • Exporters: 各种汇报数据的exporter,例如汇报机器数据的node_exporter,汇报MondogDB信息的 MongoDB_exporter 等等。
  • Alertmanager: 用于高级通知管理。

1.怎么采集监控数据?

要采集目标(主机或服务)的监控数据,首先就要在被采集目标地方安装采集组件,这种采集组件被称为Exporter。prometheus.io官网上有很多这种exporter:exporter列表,比如:

Consul exporter (official)
Memcached exporter
 (official)
MySQL server exporter
 (official)
Node/system metrics exporter
 (official)
HAProxy exporter
 (official)
RabbitMQ exporter

Grok exporter

InfluxDB exporter
 (official)

等等…

这些exporter能为我们采集目标的监控数据,然后传输给普罗米修斯。这时候,exporter会暴露一个http接口,普罗米修斯通过HTTP协议使用Pull的方式周期性拉取相应的数据。

不过,普罗也提供了Push模式来进行数据传输,通过增加Push Gateway这个中间商实现,你可以将数据推送到Push Gateway,普罗再通过Pull的方式从Push Gateway获取数据。

这就是为什么你从架构图里能看到两个 Pull metrics 的原因,一个是采集器直接被Server拉取数据(pull);另一个是采集器主动Push数据到Push Gateway,Server再对Push Gateway主动拉取数据(pull)。

采集数据的主要流程如下:

1. Prometheus server 定期从静态配置的主机或服务发现的 targets 拉取数据(zookeeper,consul,DNS SRV Lookup等方式)

2. 当新拉取的数据大于配置内存缓存区的时候,Prometheus会将数据持久化到磁盘,也可以远程持久化到云端。

3. Prometheus通过PromQL、API、Console和其他可视化组件如Grafana、Promdash展示数据。

4. Prometheus 可以配置rules,然后定时查询数据,当条件触发的时候,会将告警推送到配置的Alertmanager。

5. Alertmanager收到告警的时候,会根据配置,聚合,去重,降噪,最后发出警告。

2.采集的数据结构与指标类型

2.1 数据结构

了解普罗米修斯的数据结构对于了解整个普罗生态非常重要。普罗采用键值对作为其基本的数据结构:

Key是指标名字,Value是该指标的值,此外Metadata(元信息)也非常重要,也可称之为labels(标签信息)。这些标签信息指定了当前这个值属于哪个云区域下的哪台机器,如果没有labels,数据有可能会被丢失。

2.2 指标类型

普罗米修斯的监控指标有4种基本类型:

1.Counter(计数器):

计数器是我们最简单的指标类型。比如你想统计某个网站的HTTP错误总数,这时候就用计数器。

计数器的值只能增加或重置为0,因此特别适合计算某个时段上某个时间的发生次数,即指标随时间演变发生的变化。

2.Gauges

Gauges可以用于处理随时间增加或减少的指标,比如内存变化、温度变化。

这可能是最常见的指标类型,不过它也有一定缺点:如果系统每5秒发送一次指标,普罗服务每15秒抓取一次数据,那么这期间可能会丢失一些指标,如果你基于这些数据做汇总分析计算,则结果的准确性会有所下滑。

3.Histogram(直方图)

直方图是一种更复杂的度量标准类型。它为我们的指标提供了额外信息,例如观察值的总和及其数量,常用于跟踪事件发生的规模。

比如,为了监控性能指标,我们希望在有20%的服务器请求响应时间超过300毫秒时发送告警。对于涉及比例的指标就可以考虑使用直方图。

4.Summary(摘要)

摘要更高级一些,是对直方图的扩展。除了提供观察的总和和计数之外,它们还提供滑动窗口上的分位数度量。分位数是将概率密度划分为相等概率范围的方法。

对比直方图:

直方图随时间汇总值,给出总和和计数函数,使得易于查看给定指标的变化趋势。

而摘要则给出了滑动窗口上的分位数(即随时间不断变化)。

3.实例概念

随着分布式架构的不断发展和云解决方案的普及,现在的架构已经变得越来越复杂了。

分布式的服务器复制和分发成了日常架构的必备组件。我们举一个经典的Web架构,该架构由3个后端Web服务器组成。在该例子中,我们要监视Web服务器返回的HTTP错误的数量。

使用普罗米修斯语言,单个Web服务器单元称为实例(主机实例)。该任务是计算所有实例的HTTP错误数量。

事实上,这甚至可以说是最简单的架构了,再复杂一点,实例不仅能是主机实例,还能是服务实例,因此你需要增加一个instance_type的标签标记主机或服务。

再再复杂一点,同样的IP,可能存在于不同云区域下,这属于不同的机器,因此还需要一个cloud标签,最终该数据结构可能会变为:

cpu_usage {job=”1″, instance=”128.0.0.1″, cloud=”0″, instance_type=”0″}

4.数据可视化

如果使用过基于InfluxDB的数据库,你可能会熟悉InfluxQL。普罗米修斯也内置了自己的SQL查询语言用于查询和检索数据,这个内置的语言就是PromQL。

我们前面说过,普罗米修斯的数据是用键值对表示的。PromQL也用相同的语法查询和返回结果集。

PromQL会处理两种向量:

即时向量:表示当前时间,某个指标的数据向量。

时间范围向量:表示过去某时间范围内,某个指标的数据向量。

如针对8核CPU的使用率:

知道怎么提取数据后,可视化数据就简单了。

Grafana是一个大型可视化系统,功能强大,可以创建自己的自定义面板,支持多种数据来源,当然也支持普罗米修斯。

通过配置数据源,Grafana会使用相应的SQL拉取并绘制图表,能直接看到普罗米修斯的各个指标数据图表:

更方便的是,Grafana有很多仪表盘模板供你使用,只要import模板进行简单的配置,就能得到以下效果:

5.应用前景

普罗米修斯非常强大,可以应用到各行各业。

5.1 DevOps

为了观察整个服务体系是否在正常运转,运维非常需要监控系统。在实例的创建速度和销毁速度一样快的容器世界中,灵活配置各类容器的监控项并迅速安装启动监控是非常重要的。

5.2 金融行业

金融服务巨头Northern Trust于2017年6月选择普罗米修斯,不是为了进行应用程序的监视,而是为了更好地了解其某些硬件的运作情况。Northern Trust使用普罗米修斯监控其平台上的750多种微服务。

5.3 汽车行业

Life360是一款用于定位、行车安全和家庭成员之间共享信息的移动应用程序,他们需要给用户提供稳定的定位服务,而原有的监控方案都非常局限,无法监视到所有组件的工作状态。

因此该公司使用普罗米修斯来监视其MySQL多主群集和一个12节点的Cassandra环,该环可容纳约4TB的数据。普罗米修斯在初步测试中表现良好。

在普罗米修斯的有限部署之后,Life360报告了监控方面的巨大进步,并设想在其数据中心基础架构的其他部分中使用它。 

总而言之,普罗米修斯这样的分布式监控系统,在未来的世界中用处可能会越来越大,它或许将会成为监控领域寡头式的存在,希望我们能熟悉这个工具,并在以后的架构和实践中使用它解决系统和应用监控的问题。

我们的文章到此就结束啦,如果你喜欢今天的 Python 教程,请持续关注Python实用宝典。

有任何问题,可以在公众号后台回复:加群,回答相应验证信息,进入互助群询问。

原创不易,希望你能在下面点个赞和在看支持我继续创作,谢谢!


​Python实用宝典 ( pythondict.com )
不只是一个宝典
欢迎关注公众号:Python实用宝典

healthchecks, 基于Django监控Cron任务的神器

在运维服务器的时候经常会用到一些Crontab任务。

当你的Crontab中的任务数超过10个的时候,你会发现这些任务管理起来非常困难。

尤其是当这些Cron任务执行失败的时候,比如 Python 实用宝典网 每个月初都会执行一次https证书刷新,有一次协议更新之后,我的脚本失效了三个月,导致证书过期时网站宕机了一天,直到我发现并修复了这个问题。

这就是Crontab任务的一个劣势:没有方便的通知功能。

不过,现在有一个非常方便的开源Django项目能在这些Crontab失效的时候通知你,它就是healthchecks.

它通过一个回调接口判断你的Crontab任务有没有顺利执行。

比如说你有一个python脚本定时执行,healthchecks给定的回调URL是:

http://localhost:8000/ping/880cb4d2

在配置Crontab脚本的时候,就需要这么写:

8 6 * * * python /home/user/test.py && curl -fsS -m 10 --retry 5 -o /dev/null http://localhost:8000/ping/880cb4d2

如果未按时调用回调接口,healthchecks将会通过邮件等通知方式告警。

那么这个“未按时”能否设定宽限呢?比如我有个任务要跑1个小时左右,那么这个任务应该是预计在一个半小时内调用(Ping)回调接口,超过一个半小时如果没有调用回调接口则告警。答案是肯定的。

上图中Period指的是两次Ping之间的时间间隔。下方Grace表示“宽限期”,自从上次Ping以来的时间已超过Period+Grace则会发送告警通知。

如果你用不习惯这种可视化的选择器,它还提供了Crontab表达式给你定义Period和Grace:

真乃神器啊!支持的通知方式如下:

国内用户可能一般只会用到Email和Teams,高级点的用户可能会用到IFTTT的Webhooks和普罗米修斯。总之,按你的爱好来就行。

本地开发

下面教大家如何在本地搭建这个项目:

1. 下载项目

https://github.com/healthchecks/healthchecks

如果你访问不了github,可在【Python 实用宝典】公众号后台回复 healthchecks 下载完整源代码

2.创建虚拟环境

推荐使用Python 3.6+,如果你有conda,那就非常方便了,创建healthchecks虚拟环境:

conda create -n healthchecks python=3.6
activate healthchecks

如果你没有conda,你需要先安装Python3.6,然后使用pip安装virtualenv,在终端输入以下命令创建healthchecks虚拟环境

python3 -m venv healthchecks
source healthchecks/bin/activate

不同系统中命令可能不太一样,遇到问题多利用搜索引擎查询就好了。

3.安装依赖

进入到上述创建好的虚拟环境后,cd进入项目根目录,输入以下命令安装依赖:

pip install -r requirements.txt

4.数据库配置(可选)

该项目默认使用SQLite,这意味着你不需要特殊配置也可照常运转。

如果你需要配置MySQL或PostgreSQL,请阅读hc/local_settings.py.example文件进行配置即可。

5.数据表迁移

Django项目当然少不了这个环节,虚拟环境下,在根目录里运行以下命令进行数据表的迁移:

python manage.py migrate

当然,还要创建超管用户:

python manage.py createsuperuser

6.运行项目

大功告成,输入以下命令即可运行项目:

python manage.py runserver

点击右上角login in登录到超管用户就可以开始使用了。

如果你需要对这个项目进行大规模的改动,建议使用Pycharm作为编程工具,因为使用Pycharm来写Django实在是太爽了,详细可以参考这篇文章:《Pycharm+Django 安装及配置指南》

我们的文章到此就结束啦,如果你喜欢今天的Python 实战教程,请持续关注Python实用宝典。

有任何问题,可以在公众号后台回复:加群,回答相应红字验证信息,进入互助群询问。

原创不易,希望你能在下面点个赞和在看支持我继续创作,谢谢!


​Python实用宝典 (pythondict.com)
不只是一个宝典
欢迎关注公众号:Python实用宝典