前言
众所周知,大数据作为底层平台,其运维监控一直是生产实践的痛点难点,且在稳定运行的基础之上,往往还需要对性能进行评估优化,所以其监控系统的建设显得尤为重要。Prometheus 作为云原生时代最火的监控软件,很多大数据组件或原生或以第三方插件 / exporter 的形式对 Prometheus 做了支持。
我使用的大数据平台是基于 kubernetes 运行的,有部署灵活管理方便的优点,更容易与 Prometheus 进行结合。 下面将对设计思路和技术实现进行阐述探讨。设计思路
监控系统的核心任务是将暴露出来的指标数据进行抓取,在此之上进行分析、告警 所以有以下几个要明确的问题:- 监控对象是什么
- 监控对象如何暴露指标数据
- 监控系统如何对指标进行抓取
- 如何实现告警规则动态配置、管理
监控对象
以 pod(容器)形式运行在 kubernetes 集群上的各个大数据组件。指标暴露方式
各组件根据对 Prometheus 的支持程度,可分为 3 种类型的指标暴露方式:- 直接暴露 Prometheus 指标数据 (直接,拉)
- 主动将指标数据推送到 prometheus-pushGateway,由 pushGateway 暴露数据(间接,推)
- 自定义 exporter 将其他形式的指标数据转换为符合 Prometheus 标准的格式进行暴露(exporter,直接,拉)
指标抓取方式
不管是 exporter 还是 pushGateway,到最后必然是由 Prometheus 主动对这些目标进行抓取。 Prometheus 主要通过 Pull 的方式来抓取目标服务暴露出来的监控接口,因此需要配置对应的抓取任务来请求监控数据并写入到 Prometheus 提供的存储中,目前 Prometheus 服务提供了如下几个任务的配置:- 原生 Job 配置:提供 Prometheus 原生抓取 Job 的配置。
- Pod Monitor:在 K8S 生态下,基于 Prometheus Operator 来抓取 Pod 上对应的监控数据。
- Service Monitor:在 K8S 生态下,基于 Prometheus Operator 来抓取 Service 对应 Endpoints 上的监控数据。
kubernetes_sd_config 赋予了 Prometheus 通过 kubernetes rest api 感知 kubernetes 资源的功能,利用该能力,可以使用原生 Job 配置自动发现 pod,将其作为监控目标。再利用 Prometheus 的 Relabel 功能可以改写发现的标签,进行前置处理、转换。实现 pod 筛选,修改抓取配置的效果。而自动发现的 pod 的标签的来源又可以是 pod 资源的 label/annotation 等。
最终实现的效果如下: 这是一个 pushGateway 的 pod 的配置,则 Prometheus 会通过其 19091 端口访问 / metrics 路径获取其指标数据这部分的内容主要参考:
annotations:
prometheus.io/scrape: "true"
prometheus.io/scheme: "http"
prometheus.io/path: "/metrics"
prometheus.io/port: "19091"
- https://godleon.github.io/blog/Prometheus/Prometheus-Relabel
- https://yunlzheng.gitbook.io/prometheus-book/part-iii-prometheus-shi-zhan/readmd/service-discovery-with-kubernetes
告警设计
告警流程
prometheus 的监控告警基本流程是:- 服务发生异常
- 触发 prometheus 服务器发出告警信息(alert)
- alertmanager 收到告警信息
- alertmanager 根据预配置的规则对告警信息进行处理,实现业务逻辑,如分组、抑制、触发短信邮箱等
告警的动态配置
kube-prometheus 的告警规则分两部分:- alertmanager: 即对告警信息的处理策略
- alertRule: 即具体的告警规则
接入自定义告警平台
从个人实践的角度来看,AlertManager 处理 web hook 之外的告警接收插件,如短信、邮箱等只适合测着玩。生产使用还是要通过 web hook 将告警信息发送到自己的告警平台。可以根据业务需要对告警信息做高度定制化处理、记录等。另外可以在告警信息中携带具体告警规则等信息指导告警平台进行处理。 这里要做个区分,AlertManager 是告警信息的前置处理,负责非业务性前置操作,如告警信息分组、平抑等。而自定义告警平台则负责告警信息的业务处理,如记录、去敏、发送到多终端等。 AlertManager 可能收到 1w 条告警信息,经过处理最终只发了 1 条到自定义告警平台。而自定义告警平台可以将这 1 条告警信息记录起来,修改内容,同时使用邮箱、短信通知到多个负责人。告警层级标签设计
监控对象的粒度决定告警的层级,体现在配置上则是告警规则的分组。分组信息决定 alertManager 的处理方式。 alertManager 对告警信息的路由策略是树状的,所以可通过多个分组标签实现多层级路由处理。 具体设计应结合业务需求,不在这里展开,感兴趣的可以看我下面的实现举例。技术实现
技术实现主要分以下几部分:- kubernetes 环境下 prometheus 的部署 (kube-prometheus)
- kube-prometheus 的增强配置 : 即 kubernetes_sd_config+relabel 方案的实现
- bigdata-exporter 的实现
- 告警设计实例
kubernetes 环境下 prometheus 的部署
- kube-prometheus vs prometheus-operator
- kube-prometheus 使用前说明
- 安装教程
kubernetes_sd_config+relabel 方案的实现见:https://github.com/linshenkx/kube-prometheus-enhance ### bigdata-exporter 的实现 hdfs、yarn、hbase、yarn 等组件都提供了 web 获取 jmx 指标的方式。 这里的思路是使用一个 bigdata-exporter,去采集多个组件多个节点的指标数据,并进行转换,然后以 Prometheus 规定的格式对外公开。 指标数据的转换规则可以查看 github 上的一些项目,要注意版本,也可以像我一样自己写,更可靠。 bigdata-exporter 如何感知到采集目标? 除了部署 ip 不同,不同组件不同角色的指标对外端口、路径、内容(解析规则)也都不一样。 这里可以参考上面 kubernetes_sd_config+relabel 的方案,做得优雅一些:
#Create the namespace and CRDs, and then wait for them to be availble before creating the remaining resources
$ kubectl create -f manifests/setup
$ until kubectl get servicemonitors --all-namespaces ; do date; sleep 1; echo ""; done
$ kubectl create -f manifests/
- 授予 bigdata-exporter 调用 kubernetes app 的能力,
- 利用 label 和 annotations 进行筛选和信息传递,确定捕捉目标和途径。
- 使用 role 代表解析内容的类型,根据 role 确定解析规则
labels:
bigData.metrics.object: pod
annotations:
bigData.metrics/scrape: "true"
bigData.metrics/scheme: "https"
bigData.metrics/path: "/jmx"
bigData.metrics/port: "29871"
bigData.metrics/role: "hdfs-nn,common"
告警设计示例
这里以组和实例两个维度为例,用 groupId 和 instanceId 表示。- alertManager 配置示例
$ kubectl -n monitoring create secret generic alertmanager-main --from-file=alertmanager.yaml --dry-run -o yaml | kubectl -n=monitoring apply -f -
global:
resolve_timeout: 5m
receivers:
- name: 'default'
- name: 'test.web.hook'
webhook_configs:
- url: 'http://alert-url'
route:
receiver: 'default'
group_wait: 30s
group_interval: 5m
repeat_interval: 2h
group_by: [groupId,instanceId]
routes:
- receiver: 'test.web.hook'
continue: true
match:
groupId: node-disk-usage
- receiver: 'test.web.hook'
continue: true
match:
groupId: kafka-topic-highstore
alertRule 配置示例
组代表一个类型的所有目标:即所有节点。实例则代表具体的某个节点。 disk-usage.yaml 磁盘使用率告警配置示例如下: 注意:{thresholdValue} 为使用率阈值,需自行替换。labels 中的 userIds 和 receivers 为传递给自定义告警平台的参数,以指导告警平台如何操作。 在这个任务中,我们的目标是组粒度的(所有节点),所以不需要设置 instanceId。kafka-topic-highstore.yaml kafka 队列消费堆积告警配置示例如下: 我们只关心个别队列的消费情况,所以这里的粒度为 instance。 注意:{consumergroup} 为消费组名称,${thresholdValue} 为堆积数量阈值。
apiVersion: monitoring.coreos.com/v1
kind: PrometheusRule
metadata:
creationTimestamp: null
labels:
role: alert-rules
name: node-disk-usage
namespace: monitoring
spec:
groups:
- name: node-disk-usage
rules:
- alert: node-disk-usage
expr: 100*(1-node_filesystem_avail_bytes{mountpoint="${path}"}/node_filesystem_size_bytes{mountpoint="${path}"} ) > ${thresholdValue}
for: 1m
labels:
groupId: node-disk-usage
userIds: super
receivers: SMS
annotations:
title: "磁盘警告:节点{{$labels.instance}}的 ${path} 目录使用率已达到{{$value}}%"
content: "磁盘警告:节点{{$labels.instance}}的 ${path} 目录使用率已达到{{$value}}%"
apiVersion: monitoring.coreos.com/v1
kind: PrometheusRule
metadata:
creationTimestamp: null
labels:
role: alert-rules
name: kafka-topic-highstore-${uniqueName}
namespace: monitoring
spec:
groups:
- name: kafka-topic-highstore
rules:
- alert: kafka-topic-highstore-${uniqueName}
expr: sum(kafka_consumergroup_lag{exporterType="kafka",consumergroup="${consumergroup}"}) > ${thresholdValue}
for: 1m
labels:
groupId: kafka-topic-highstore
instanceId: ${uniqueName}
userIds: super
receivers: SMS
annotations:
title: "KAFKA警告:消费组 ${consumergroup}的堆积数量达到:{{$value}}"
content: "KAFKA警告:消费组 ${consumergroup}的堆积数量达到:{{$value}}"
其他
告警流程示例
这里以两个节点 node1 和 node2 配置了磁盘空间监控为例,空间使用到达阈值则触发告警。(测试过程中可通过生成、删除指定体积的文件来控制空间占用) 其中配置项如下:- 告警规则 : node-disk-usage - for 为 1m
- 告警中心 : alertManager - group_wait: 30s - group_interval: 5m - repeat_interval: 10m
总共收到 5 次短信,第 1 次是 node1 异常,第 2 到 4 次是 node1 和 node2 都异常,因为属于同个分组 group,所以合并发送。第 5 次是已经恢复正常了。 根据短信内容和时间,整理出告警逻辑时间线如下:
10:23:14 收到第一次警报:node1 于 10:22:44 进入异常
10:28:14 收到第二次警报:node1 于 10:22:44 进入异常 node2 于 10:24:44 进入异常
10:38:29 收到第三次警报:node1 于 10:22:44 进入异常 node2 于 10:24:44 进入异常
10:48:44 收到第四次警报:node1 于 10:22:44 进入异常 node2 于 10:24:44 进入异常
10:58:44 收到第五次警报:恢复告警 node1 于 10:22:44 进入异常,并于 10:55:44 恢复 node2 于 10:24:44 进入异常,并于 10:49:14 恢复
注意,这里 node1,node2 都恢复正常用的也是 repeat_interval。 综上:
node1 等待 for 1 分钟 后警报进入 group
node1 记录异常时间为 10:22:44,实际异常状态至少在 10:22:44 的一分钟前
group 等待 group_wait 30s 后发送第一次告警
firing:node1
node2 等待 for 1 分钟 后警报进入 group
node2 记录异常时间为 10:24:44,实际异常状态至少在 10:24:44 的一分钟前,此时 group 中有两个异常目标 node1 和 node2。
group 等待 group_interval 5m 后发送第二次告警
firing:node1,node2
注意:因为 group 发生了变化,所以这里用的是 group_interval。
group 等待 repeat_interval 10m 后发送第三次告警
firing:node1,node2
注意:因为 group 没有变化,属于重复告警,用的是 repeat_interval。
group 等待 repeat_interval 10m 后发送第四次告警
firing:node1,node2
同上一次。
第四次告警后的 前 5 分钟:node2 恢复正常
第四次告警后的 后 5 分钟:node1 恢复正常
group 等待 repeat_interval 10m 后发送第五次告警
resolved:node1,node2
需要注意,恢复正常不属于 group 变化,用的是 repeat_interval。这有点反直觉,且个人认为不是很合理,不知道是不是测试有问题,也没有找到比较好的资料说明。希望知道的可以指教一下。
for 是告警规则个体的监控配置,用来衡量服务多久检测不通过才算异常。
group_wait:初次发送告警的等待时间
用于 group 创建后的等待,这个值通常设置较小,在几分钟以内。
group_interval:同一个组其他新发生的告警发送时间间隔
是 group 内容发生变化后的告警间隔。
repeat_interval:重复发送同一个告警的时间间隔
group 内容没有变化且上一次发生成功时用的发生间隔。
exporter 的位置
exporter 可以以 sidecar 的形式和原容器放在同一个 pod 内(1 对 1),也可以以独立部署的形式存在(1 对 1/1 对多)。 这个视具体情况而定,技术上没什么不同,sidecar 可以绑定生命周期,视为对原有组件的补充。独立部署则耦合度更低,更灵活。像单节点的 mysql,用 sidecar 则只需要 1 个 pod,不会太复杂。 而如果像多节点的 kafka 集群,用独立部署则只需要一个 exporter 就可以实现对多个节点的采集监控。 这里出于减小耦合、节省资源的目的,我主要使用的是独立部署形式。使用 promtool 检查指标格式是否正确
promtool 使用方法:比方说 指标 name、labelname 不能使用小数点
# 进入pod
$ kubectl -n=monitoring exec -it prometheus-k8s-0 sh
# 查看帮助
$ promtool -h
# 检查指标格式
$ curl -s http://ip:9999/metrics | promtool check metrics
使用 port-forward 临时提供 Prometheus 外部访问
用
# prometheus
$ nohup kubectl port-forward --address 0.0.0.0 service/prometheus-k8s 19090:9090 -n=monitoring &
# grafana
$ nohup kubectl port-forward --address 0.0.0.0 service/grafana 13000:3000 -n=monitoring &
# alertmanager
$ nohup kubectl port-forward --address 0.0.0.0 service/alertmanager-main 9093:9093 -n=monitoring &
<font style="color:rgb(53, 53, 53);">jobs -l</font>
可以查看
kube-prometheus 对 arm 的支持
目标是找到 kube-prometheus 用到的镜像的 arm 版本。 可以使用 https://quay.io/搜索。 以下是用到的镜像和各自对 arm 的支持情况。注意对照自己使用的版本,很多镜像高版本都支持 arm 了。 未标记(不支持)其实也是可以用,但不保证:- quay.io/prometheus/prometheus:v2.11.0 支持 arm(v2.10.0 开始)
- quay.io/prometheus/alertmanager:v0.18.0 支持 arm(v0.17.0 开始)
- quay.io/coreos/kube-state-metrics:v1.8.0 未标记(不支持)
- quay.io/coreos/kube-rbac-proxy:v0.4.1 未标记(不支持)
- quay.io/prometheus/node-exporter:v0.18.1 支持 arm(v0.18.0 开始)
- quay.io/coreos/prometheus-operator:v0.34.0 不支持 arm(v0.39 开始)。修改成使用 0.39.0,0.39 以后的 prometheus 要求 k8s 必须 >=1.16
- quay.io/coreos/configmap-reload:v0.0.1 未标记(不支持)
- grafana/grafana:6.4.3 官方说支持 arm,但其实不支持,有 bug,见:https://bleepcoder.com/cn/grafana/501674494/docker-arm-images-doesn-t-work-since-v6-4-x
- quay.io/coreos/k8s-prometheus-adapter-amd64:v0.5.0 未找到 arm 版本镜像。最新的官方版本已经改用 directxman12/k8s-prometheus-adapter:v0.8.4。而 directxman12/k8s-prometheus-adapter:v0.5.0 开始支持 arm
- 见:https://hub.docker.com/r/directxman12/k8s-prometheus-adapter/tags