来源 | 运维开发故事
分享 | 乔克
监控是整个运维以及产品整个生命周期最重要的一环,它旨在事前能够及时预警发现故障,事中能够结合监控数据定位问题,事后能够提供数据用于分析问题。
监控贯穿应用的整个生命周期。即从程序设计、开发、部署、下线。其主要的服务对象有:
技术通过监控系统可以了解技术的环境状态,可以帮助检测、诊断、解决技术环境中的故障和问题。然而监控系统的最终目标是业务,是为了更好的支持业务运行,确保业务的持续开展。
所以监控的目的可以简单归纳如下:1、能够对系统进行7*24小时的实时监控 2、能够及时反馈系统状态 3、保证平台的稳定运行 3、保证服务的安全可靠 4、保证业务的持续运行
监控由上至下可以分为:
其中业务监控主要是研发提供一些业务指标、业务数据,对其增长率、错误率等进行告警或者展示,需要提前定义规范甚至埋点。应用程序的监控主要有探针和内省。其中探针主要是从外部探测应用程序的特征,比如监听端口是否有响应。内省主要是查看应用程序内部的内容,应用程序通过检测并返回其内部的状态,内部的组件,事务和性能等度量,它可以直接将事件、日志和指标直接发送给监控工具。操作系统主要是监控主要组件的使用率、饱和度以及错误,比如CPU的使用率、CPU的负载等。
监控的主要方式有:
在上述4中监控方式中,健康检查是云平台等基础设施提供的能力,日志则一般有单独的日志中心进行日志的采集、存储、计算和查询,调用链监控一般也有独立的解决方案进行服务调用的埋点、采集、计算和查询,指标监控则是通过一些exporter抓取目标暴露的指标,然后对这些指标数据进行清理、聚合,我们通过聚合出来的数据进行展示、告警等。
❝说明:该方案主要是针对指标监控 ❞
云平台提供健康检查能力,直接在云平台中配置。
成熟的开源日志解决方案是ELK。
调用链健康使用第三方的健康软件,常用的有skywalking、zikpin、pinpoint、elastic APM、Cat。其中zikpin和cat对代码有一定的侵入性,而skywalking、pinpoint、elastic APM是基于字节码注入技术,对代码没有侵入性,而且改动最小。
pinpoint的agent仅支持java和php,而skywalking和elastic APM都支持多种语言,比如java/nodejs/go等。
在云原生环境下,skywalking和elastic APM更适合。
elastic APM直接使用es作为存储,可以在kibana上直接看应用信息,但是其关系图是需要产生一定的费用。skywalking是国人开源的一款产品,已经毕业于Apache 基金会,社区非常活跃,版本迭代很快,专为微服务、云原生架构和基于容器(Docker、K8s、Mesos)架构而设计。
pinpoint和skywalking的对比可参考:http://skywalking.apache.org/zh/blog/2019-02-24-skywalking-pk-pinpoint.html
在云原生环境、传统的监控方式并不适合,在传统环境,zabbix无疑是首选,但是在云原生环境,Prometheus却成为了热门,其主要原因有:
❝注意:由于采集的数据有可能丢失,Prometheus并不适合对采集数据要求100%准确的场景。 ❞
监控系统的整体框架如下:
其流程很简单,Prometheus server端可以直接接收或者通过pushgateway获取到数据,存储到TSDB中,然后对数据进行规则整理,通过Altermanager进行报警或者通过Grafana等工具进行展示。
监控系统一般采用分层的方式划分监控对象。在我们的监控系统中,主要关注以下几种类型的监控对象:
对于应用服务和第三方接口监控,我们常用的指标包括:响应时间、请求量QPS、成功率。
主机是系统的载体,一切系统应用都运行在主机之上,如果某台或者几台主机宕机,会导致上面运行的所以应用都没办法正常提供服务,严重者导致生产事故。所以对主机的监控与预警是非常有必要的,我们可以在其出故障之前对其进行处理,避免严重的事故发生。
主机的监控主要从一下三个方面来综合考虑其状态:
主机的主要资源对象有:
在Prometheus监控方案中,主机的资源指标是通过node-exporter来进行采集,然后存储在Prometheus时序数据库里,然后可以通过PromQL来查询各个指标的具体情况。
CPU主要从使用率和饱和度来进行监控。(1)、使用率,指标node_cpu_seconds_total
通常会根据CPU使用率超过多少来进行告警,比如当CPU使用率大于80%,则进行告警,当然CPU是一个Gauge类型的,它的数据是会上下增减的,所以我们在判断CPU使用率的时候通常是一段时间内CPU持续高达多少的时候才进行告警,比如下面的表达式就是统计5分钟内CPU使用率大于60%的主机:
100-(avg(irate(node_cpu_seconds_total{mode="idle"}[5m])) by(instance)* 100) > 60
CPU指标还有用户态、内核态的指标数据,这个根据情况来进行监控。(2)、饱和度,指标node_load
CPU的饱和度通常指的是CPU的负载情况。正常情况下CPU的整体负载不超过CPU的总数,比如2颗CPU,则负载不超过2。我们收集到的指标有1分钟、5分钟、15分钟的负载数据,在配置监控的时候选择好统计时间,一般情况下会选择5分钟的负载作为统计,如下表示5分钟的负载大于CPU的总数的2倍:
node_load5 > on (instance) 2 * count by(instance)(node_cpu_seconds_total{mode="idle"})
内存主要从使用率和饱和度来进行监控。(1)、使用率 内存的使用率可以直观的看到整体CPU的使用情况,其计算方式使用(free + buffer + cache)/ total。指标主要有:
比如下面的表达式是用于统计内存使用率大于80%:
100 - sum(node_memory_MemFree_bytes{job="node-exporter"} + node_memory_Buffers_bytes{job="node-exporter"} + node_memory_Cached_bytes{job="node-exporter"})by (instance) / sum(node_memory_MemTotal_bytes{job="node-exporter"})by(instance)*100 > 80
(2)、饱和度 内存的饱和度是指内存和磁盘的读写情况来监控。指标有:
磁盘的监控有点特殊,我们不按着USE的方法去测量。如果单考虑其使用率并没有多大的效果,因为10G剩余20%和1T剩余20%对我们的影响是不一样的,所以我们可以监控其增长趋势以及方向。比如:根据前面1h的磁盘增长情况来预测在4h内是否会把磁盘用完。
predict_linear(node_filesystem_free_bytes{job="node-exporter",mountpoint!=""}[1h], 4*3600)
当然,如果仅仅这样预测也会产生很多垃圾告警,因为在某一小时的增长速度可能很快,这样算下来预测会在接下来的4小时内使用完,但是我们登上主机一看,才用了40%,这时候就算有告警我们也不会处理。所以我们还可以再加一个条件,比如磁盘使用率大于80%并且在接下来的4小时内会使用完。如下:
(100 - (node_filesystem_avail_bytes{fstype!="",job="node-exporter"} / node_filesystem_size_bytes{fstype!="",job="node-exporter"} * 100)>80) and (predict_linear(node_filesystem_free_bytes{job="node-exporter",mountpoint!="",device!="rootfs"}[1h],4 * 3600) < 0)
除此之外,我们还需要监控磁盘的IO,不论是云主机磁盘还是物理磁盘,每块盘都有其对应的IOPS,如果某个主机的IO很高,也会导致其他的问题,比如系统繁忙、负载很高等情况。node-exporter中定义了其指标,仅需要对其进行聚合,然后对聚合后的数据进行页面展示或者告警处理。其聚合公式如下:
100-(avg(irate(node_disk_io_time_seconds_total[1m])) by(instance)* 100)
可用性是指的主机可用性,我们可以通过up
指标来判断主机是否可用,如果其值等于0表示宕机,等于1表示存活。比如下面即可表示主机不可用:
up{job="node-exporter"}==0
服务状态旨在监控关键服务,比如docker.service,ssh.service,kubelet.service等。指标为:
比如监听docker.service的服务状态为存活:
node_systemd_unit_state{name="docker.service",state="active"} == 1
监控主要服务,以便于我们能在服务出问题的第一时间收到消息进行处理。
网络主要是监控其在每台主机上的出入流量,还有TCP连接状态。prometheus的node-exporter会抓取每台主机的网卡以及其出入网卡的流量,还有每台主机的TCP状态,我们可以将需要的指标进行聚合,根据聚合后的指标数据再进行页面展示或者告警处理。比如统计流入的流量:
((sum(rate (node_network_receive_bytes_total{device!~'tap.*|veth.*|br.*|docker.*|virbr*|lo*'}[5m])) by (instance)) / 100)
统计流出的流量:
((sum(rate (node_network_transmit_bytes_total{device!~'tap.*|veth.*|br.*|docker.*|virbr*|lo*'}[5m])) by (instance)) / 100)
以及统计TCP状态为ESTABLISHED的数量:
node_netstat_Tcp_CurrEstab
我们可以根据每个指标做对应的监控以及告警。
在云原生时代,容器是我们应用的载体,它相当于应用的基础设施,所以对其的监控是很有必要的。我们在创建一个容器的时候往往会给其cpu和内存一个限制值,特别是内存,如果其使用达到了限制值,就会导致OOM,这时候我们就会做升级配置或查找原因处理。
监控对象主要有一下:
我们使用cAdvisor来获取容器指标(kubelet已经集成了这个服务)。
在容器中,就简单通过其使用率来监控其状态,我们通过其(使用量/limit)来得到其使用率。如下:
sum(
node_namespace_pod_container:container_cpu_usage_seconds_total:sum_rate
* on(namespace,pod)
group_left(workload, workload_type) mixin_pod_workload
) by (workload, workload_type,namespace,pod)
/sum(
kube_pod_container_resource_limits_cpu_cores
* on(namespace,pod)
group_left(workload, workload_type) mixin_pod_workload
) by (workload, workload_type,namespace,pod) * 100 > 80
如果CPU的使用率持续大于我们设定的阈值,则考虑增加CPU的Limit值。
和CPU一样,通过其使用率来观察容器的内存是否充足。如下:
sum(
container_memory_working_set_bytes
* on(namespace,pod)
group_left(workload, workload_type) mixin_pod_workload
) by (namespace,pod) / sum(
kube_pod_container_resource_limits_memory_bytes
* on(namespace,pod)
group_left(workload, workload_type) mixin_pod_workload
) by (namespace,pod) * 100 / 2 > 80
如果内存的使用率大于我们设定的阈值,则考虑是否需要增加Pod的内存了。
这里的事件针对的是kubernetes的pod事件。在Kubernetes中,事件分为两种,一种是Warning事件,表示产生这个事件的状态转换是在非预期的状态之间产生的;另外一种是Normal事件,表示期望到达的状态,和目前达到的状态是一致的。我们用一个Pod的生命周期进行举例,当创建一个Pod的时候,首先Pod会进入Pending的状态,等待镜像的拉取,当镜像录取完毕并通过健康检查的时候,Pod的状态就变为Running。此时会生成Normal的事件。而如果在运行中,由于OOM或者其他原因造成Pod宕掉,进入Failed的状态,而这种状态是非预期的,那么此时会在Kubernetes中产生Warning的事件。那么针对这种场景而言,如果我们能够通过监控事件的产生就可以非常及时的查看到一些容易被资源监控忽略的问题。
在kubernetes中通过kube-eventer
来进行事件监控,然后针对不同的事件来进行告警通知。
应用是业务的载体,也是用户最直观的体验,应用的状态与否直接关系到业务的优良以及用户的体验。如果没有对其做好一定的监控措施,可能会出现以下问题:
应用程序指标可以衡量应用程序的性能和状态,包括应用程序最终用户的体验,如延迟和响应时间。在这背后,我们测量了应用程序的吞吐量:请求、请求量、事务和事务时间。「(1)、HTTP接口监控」可以使用prometheus的blackbox_exporter来进行接口存活的监控,可以用于对http,https,tcp,dns以及ICMP协议进行探测,从而抓取数据进行监控。
「(2)、JVM监控」通过在应用中埋点来暴露JVM数据,使用Prometheus监控采集JVM数据,借助Prometheus Grafana大盘来展示JVM数据,并创建报警,即可实现利用Prometheus监控JVM的目的。(1)、在pom.xml文件中添加Maven依赖。
<dependency>
<groupId>io.prometheus</groupId>
<artifactId>simpleclient_hotspot</artifactId>
<version>0.6.0</version>
</dependency>
(2)、在可以执行初始化的位置添加初始化JVM Exporter的方法。
@PostConstruct
public void initJvmExporter() {
io.prometheus.client.hotspot.DefaultExports.initialize();
}
(3)、在/src/main/resources/application.properties文件中配置用于Prometheus监控的端口(Port)和路径(Path)
management.port: 8081
endpoints.prometheus.path: prometheus-metrics
(4)、在/src/main/java/com/monitise/prometheus_demo/PrometheusDemoApplication.java文件中打开HTTP端口
@SpringBootApplication
// sets up the prometheus endpoint /prometheus-metrics
@EnablePrometheusEndpoint
// exports the data at /metrics at a prometheus endpoint
@EnableSpringBootMetricsCollector
public class PrometheusDemoApplication {
public static void main(String[] args) {
SpringApplication.run(PrometheusDemoApplication.class, args);
}
}
然后在部署应用的时候暴露接口和数据就可以进行采集了。由于应用比较多就可以通过自动发现的方式来做。我们在service中加上下面的annotations,就可以自动发现了。
prometheus.io/scrape: 'true'
prometheus.io/path: '/prometheus-metrics'
prometheus.io/port: '8081'
业务指标是应用程序指标的更进一层,它们通常与应用程序指标同义。如果你考虑将对特定服务的请求数量作为应用程序指标进行测量,那么业务指标通常会对请求的内容执行某些操作。一个应用程序指标的示例可能是测量支付交易的延迟,相应的业务指标可能是每个支付交易的价值。业务指标可能包括新用户/客户的数量、销售数量、按价值或位置划分的销售额,或者其他任何有助于衡量业务状况的指标。
第三方接口的优良直接影响自身业务,所以对第三方接口的异常情况监控是非常重要的。主要是其的响应时间、存活性以及成功率。
可以使用prometheus的blackbox_exporter来进行接口的监控。通过第三方接口监控的维度,我们可以方便地将自身服务与所使用到的第三方服务关联起来,以统一的视图展示服务用到了哪些第三方服务接口、这些第三方服务接口的响应时间和成功率是多少。当服务出现异常时,对于定位问题有很大帮助;同时,一些内部的服务可能监控报警并不全面,第三方监控也能帮助他们提升服务质量。
达到什么阈值需要告警?对应的故障等级是多少?不需要处理的告警不是好告警,可见定义合理的阈值有多重要,否则只会降低运维效率或者让监控系统失去它的作用。
Prometheus允许基于PromQL定义报警的触发条件,Prometheus周期性的对PromQL进行计算,当满足条件时就会向Alertmanager发送报警信息。
在配置告警规则的时候,我们将按组进行分类,这样就可以对相同组下的告警进行聚合,方便配置以及查看。Alertmanager在接收到报警后,可以对报警进行分组、抑制、静默等额外处理,然后路由到不同的接收器。Alertmanager支持多种报警通知方式,除常用的邮件通知外,还支持钉钉、企业微信等方式,也支持通过webhook自定义通知方式。我们可以按轻重缓急定义不同的通知方式,这样就可以根据不同通知方式采取不同的措施。
收到故障告警后,一定要有相应的处理流程和oncall机制,让故障及时被跟进处理。
在处理故障之前,需要先清晰的认识是什么样的故障,然后再采取什么样的措施。所以我们就需要对故障等级做一个划分。例如将系统故障等级按照《信息系统安全等级保护基本要求》具体划分为四个等级,一级和二级故障为重大故障;三级和四级故障为一般性故障。
系统发生故障,预计将已经严重影响公司生产业务系统,导致相关生产业务系统中断1小时以上,并预计24小时以内无法恢复的,具备以下一个或几个特征,既定义为一级故障。
信息系统发生故障,预计将或已经严重影响公司生产业务系统,导致相关生产业务系统中断1小时以上,并预计24小时以内可以恢复的,具备以下一个或几个特征,即定义为二级故障。
满足以下条件之一,即定义为三级故障。
满足以下条件之一,即定义为四级故障。
工作人员在发现故障或接收到故障报告后,首先要记录故障发生时间和发现时间,及发现部门,发现人及联系电话,对故障的等级进行初步判定,并报告相关人员进行处理。
根据故障等级和发生的时限,要对故障的情况进行及时的上报,并对报告人,告知人际时间内容进行记录。重大故障由故障处理组领导负责上报,一般性故障由故障处理人员负责上报。故障升级上报时限如下表所示:
上报时限 | 一级故障 | 二级故障 | 三级故障 | 四级故障 |
---|---|---|---|---|
立即 | 运维主管 | 运维人员 | 运维人员 | 运维人员 |
半小时 | 技术总监 | 运维主管 | ||
1小时 | 技术总监 | 运维主管 | ||
4小时 | 技术总监 | |||
12小时 | 运维主管 | |||
24小时 |
完