说明

本文介绍如何使用Prometheus监控一个3节点的RabbitMQ集群(4.1版本),并实现告警。主机规划如下。

主机名

IP地址

说明

gw14

172.31.2.14

RabbitMQ集群节点

gw15

172.31.2.15

RabbitMQ集群节点

gw16

172.31.2.16

RabbitMQ集群节点

gw18

172.31.2.18

Prometheus+Grafana节点

RabbitMQ启用rabbitmq_prometheus插件及配置Prometheus对接RabbitMQ metric接口的部分不再赘述。本文主要关注Grafana模板,及Prometheus告警规则。

导入Grafana模板及配置

RabbitMQ官方提供了一组预制的Grafana仪表板(https://grafana.com/orgs/rabbitmq)。

各仪表板的用途不同,包括:

  • 一个概览仪表板(RabbitMQ-Overview);
  • Erlang运行时内存分配器仪表板(Erlang-Memory-Allocators);
  • 节点间通信仪表板(Erlang-Distribution);
  • Raft指标仪表板(RabbitMQ-Quorum-Queues-Raft);
  • Perf性能测试仪表板(RabbitMQ-PerfTest)。

除Perf性能测试仪表板外,可将另外4个仪表板的JSON文件下载下来,导入Grafana,导入时数据源选择Prometheus。

Prometheus监控RabbitMQ集群_Prometheus

导入后,需注意,部分图形会没有数据,需要将其查询语句中rate( )函数中的时间跨度改为至少4倍于Prometheus中的指标抓取时间间隔。比如,我前面设置的Prometheus中的指标抓取时间间隔为60秒,即1分钟,那么,rate( )函数中的时间跨度则至少需要为4分钟。

如下是概览仪表板的一个截图:

Prometheus监控RabbitMQ集群_RabbitMQ集群_02

核心监控指标及告警规则确定

RabbitMQ官方提供的监控图形模板是比较全面的,但还是需要自己识别下哪些是关键核心监控指标,有缺失的则需要加上,对冗余繁杂的图形进行归类整理,调整图形名称、阈值等,使日常运维中能更聚焦于核心的指标,提高发现问题的效率。另外,针对核心监控指标,制定相应告警规则。原则是,监控全面但不繁琐,告警不缺漏但也不误报。

根据RabbitMQ官网文档,及我个人经验习惯,梳理如下监控指标及告警规则:

  • 集群范围的指标

反映整个集群范围的状态的指标。

指标

/metrics中的指标名称

模板中是否有图形

告警规则

总连接数量(connections)

rabbitmq_connections

“概览仪表板”中有


总通道数量(channels)

rabbitmq_channels

“概览仪表板”中有


总队列数量(queues)

rabbitmq_queues

“概览仪表板”中有


总消费者数量(consumer)

rabbitmq_global_consumers

“概览仪表板”中有


总消息数量(ready状态加上unacknowledged状态的)

rabbitmq_queue_messages

无图形,可自行在“概览仪表板”中添加,Time series类型的,Stack series为Normal。

查询语句参考:

sum(rabbitmq_queue_messages * on(instance, job) group_left(rabbitmq_cluster, rabbitmq_node) rabbitmq_identity_info{rabbitmq_cluster="$rabbitmq_cluster", namespace="$namespace", rabbitmq_endpoint="$endpoint"}) by(rabbitmq_node)

可设置“总消息数≥10000”时告警,以发现消息堆积的问题,阈值根据实际调整。但对于Stream类型的队列,消息数量是一直增加的或轮动的,需要注意。

ready状态的消息数量

rabbitmq_queue_messages_ready

“概览仪表板”中有


unacknowledged状态的消息数量

rabbitmq_queue_messages_unacked

“概览仪表板”中有

可选,设置“未确认消息数≥1000”时告警,以发现可能的消息未确认的问题,阈值根据实际调整。

消息发布速率

rabbitmq_global_messages_received_total,使用rate()计算速率

“概览仪表板”中有


消息送达速率

rabbitmq_global_messages_delivered_consume_auto_ack_total、rabbitmq_global_messages_delivered_consume_manual_ack_total,使用rate()计算速率之和

“概览仪表板”中有


集群可用性

自定义图形

集群可用性,即监控集群节点间是否正常通信(无脑裂)。本来有个指标rabbitmq_unreachable_cluster_peers_count,表示“当前节点无法访问的对端节点数量”,但实际测试发现,该指标在脑裂也无异常,故不适用。

借助node-exporter在节点上执行rabbitmqctl cluster_status命令并获取其输出中的“Network Partitions”部分可判断是否出现脑裂(见官网文档)。当出现异常时,进行告警。

  • 节点指标

反映每个RabbitMQ节点实例状态的指标。

指标

/metrics中的指标名称

模板中是否有图形

告警规则

内存限值

rabbitmq_resident_memory_limit_bytes

这是RabbitMQ实例的内存限值

无图形,可自行在“概览仪表板”中添加,Time series类型的,Stack series为Off。

查询语句参考:

rabbitmq_resident_memory_limit_bytes * on(instance, job) group_left(rabbitmq_cluster, rabbitmq_node) rabbitmq_identity_info{rabbitmq_cluster="$rabbitmq_cluster", namespace="$namespace", rabbitmq_endpoint="$endpoint"}


已使用内存量

rabbitmq_process_resident_memory_bytes

这是RabbitMQ实例的已使用内存量

无图形,可自行在“概览仪表板”中添加,Time series类型的,Stack series为Off,图形添加阈值线(内存限值)。

查询语句参考:

rabbitmq_process_resident_memory_bytes * on(instance, job) group_left(rabbitmq_cluster, rabbitmq_node) rabbitmq_identity_info{rabbitmq_cluster="$rabbitmq_cluster", namespace="$namespace", rabbitmq_endpoint="$endpoint"}

“内存限值(rabbitmq_resident_memory_limit_bytes)”减去“已使用内存量(rabbitmq_process_resident_memory_bytes)”可得RabbitMQ实例可用内存量。

当可用内存量小于等于0时,将无法发布新消息到RabbitMQ。通常可用内存量低于某个阈值(如1GB)时发送个warning级别的告警,低于某个更低的阈值(如100MB)时发送个critical级别的告警。

预留磁盘空间

rabbitmq_disk_space_available_limit_bytes

这是RabbitMQ实例要求的最低需预留的磁盘空间

无图形,可自行在“概览仪表板”中添加,Time series类型的,Stack series为Off。

查询语句参考:

rabbitmq_disk_space_available_limit_bytes * on(instance, job) group_left(rabbitmq_cluster, rabbitmq_node) rabbitmq_identity_info{rabbitmq_cluster="$rabbitmq_cluster", namespace="$namespace", rabbitmq_endpoint="$endpoint"}


可用磁盘空间

rabbitmq_disk_space_available_bytes

这是RabbitMQ实例数据目录所在分区的可用磁盘空间

“概览仪表板”中有,图形可添加阈值线(RabbitMQ实例预留的磁盘空间)。

“可用磁盘空间(rabbitmq_disk_space_available_bytes)”减去“预留磁盘空间(rabbitmq_disk_space_available_limit_bytes)”可得RabbitMQ实例实际可用的磁盘空间。

当可用磁盘空间小于等于0时,将无法发布新消息到RabbitMQ。通常可用磁盘空间低于某个阈值(如剩余20%时,假设为40GB)时发送个warning级别的告警,低于某个更低的阈值(如剩余5%时,假设为10GB)时发送个critical级别的告警。

最大文件描述符数量

rabbitmq_process_max_fds

这是RabbitMQ进程的最大文件描述符数量

无图形,可自行在“概览仪表板”中添加,Time series类型的,Stack series为Off。

查询语句参考:

rabbitmq_process_max_fds * on(instance, job) group_left(rabbitmq_cluster, rabbitmq_node) rabbitmq_identity_info{rabbitmq_cluster="$rabbitmq_cluster", namespace="$namespace", rabbitmq_endpoint="$endpoint"}


已使用的文件描述符数量

rabbitmq_process_open_fds

这是RabbitMQ进程已使用的文件描述符数量

无图形,可自行在“概览仪表板”中添加,Time series类型的,Stack series为Off,图形添加阈值线(最大文件描述符数量)。

查询语句参考:

rabbitmq_process_open_fds * on(instance, job) group_left(rabbitmq_cluster, rabbitmq_node) rabbitmq_identity_info{rabbitmq_cluster="$rabbitmq_cluster", namespace="$namespace", rabbitmq_endpoint="$endpoint"}

“最大文件描述符数量(rabbitmq_process_max_fds)”减去“已使用的文件描述符数量(rabbitmq_process_open_fds)”可得RabbitMQ实例可用的文件描述符数量。

由于一个网络连接就会占用一个文件描述符,因此当并发高时,已使用的文件描述符数量会剧烈上升。达到限值时,新连接将无法建立。

通常已使用的文件描述符数量高于某个阈值(如10000)时发送个warning级别的告警,这样可以提前介入发现潜在异常情况;在可用的文件描述符低于某个阈值(如10000)时发送个critical级别的告警,以提醒文件描述符即将耗尽。

最大进程数

erlang_vm_process_limit

这是RabbitMQ实例的最大进程数

无图形,可自行在“概览仪表板”中添加,Time series类型的,Stack series为Off。

查询语句参考:

erlang_vm_process_limit * on(instance, job) group_left(rabbitmq_cluster, rabbitmq_node) rabbitmq_identity_info{rabbitmq_cluster="$rabbitmq_cluster", namespace="$namespace", rabbitmq_endpoint="$endpoint"}


当前进程数

erlang_vm_process_count

这是RabbitMQ实例的当前进程数

无图形,可自行在“概览仪表板”中添加,Time series类型的,Stack series为Off,图形添加阈值线(最大进程数)。

查询语句参考:

erlang_vm_process_count * on(instance, job) group_left(rabbitmq_cluster, rabbitmq_node) rabbitmq_identity_info{rabbitmq_cluster="$rabbitmq_cluster", namespace="$namespace", rabbitmq_endpoint="$endpoint"}

“最大进程数(erlang_vm_process_limit)”减去“当前进程数(erlang_vm_process_count)”可得RabbitMQ实例可用进程数。

通常可设置当前进程数高于某个阈值(如10000)时发送个warning级别的告警,这样可以提前介入发现潜在异常情况;在可用进程数低于某个阈值(如10000)时发送个critical级别的告警,以提醒进程数即将耗尽。

GC运行次数

(每分钟,按数据采集间隔)

rabbitmq_erlang_gc_runs_total

使用irate()函数对该指标求速率可得

无图形,可自行在“概览仪表板”中添加,Time series类型的,Stack series为Off。

查询语句参考:

sum by (rabbitmq_node) (irate(rabbitmq_erlang_gc_runs_total[5m]) * on (instance, job) group_left (rabbitmq_cluster, rabbitmq_node) rabbitmq_identity_info{namespace="$namespace",rabbitmq_cluster="$rabbitmq_cluster",rabbitmq_endpoint="$endpoint"})


GC回收的字节数

(每分钟,按数据采集间隔)

rabbitmq_erlang_gc_reclaimed_bytes_total

使用irate()函数对该指标求速率可得

无图形,可自行在“概览仪表板”中添加,Time series类型的,Stack series为Off。

查询语句参考:

sum by (rabbitmq_node) (irate(rabbitmq_erlang_gc_reclaimed_bytes_total[5m]) * on (instance, job) group_left (rabbitmq_cluster, rabbitmq_node) rabbitmq_identity_info{namespace="$namespace",rabbitmq_cluster="$rabbitmq_cluster",rabbitmq_endpoint="$endpoint"})


节点可用性

自定义图形

借助node-exporter在节点上执行rabbitmq-diagnostics -q check_running命令并获取该命令的输出,来判断节点是否正常运行。当节点异常时,进行告警。

Prometheus添加告警规则

上面的监控告警涉及到2个自定义指标(集群可用性、节点可用性),可以通过pushgateway或node_exporter实现,我是用node_exporter实现的。具体编写脚本等就不赘述了,根据上一小节表格中的说明在脚本中执行相应命令获取相应节点和集群状态就行。就结果来说,自定义了2个自定义指标rabbitmq_cluster_status(集群可用性,正常时值为0,异常时值为非0)、rabbitmq_node_status(节点可用性,正常时值为0,异常时值为非0)。

根据上一小节确定的告警规则,在Prometheus的告警规则目录下添加告警规则文件rabbitmq_cluster.yml,内容如下:

groups:
- name: RabbitMQ-Cluster
  # 所有阈值根据实际情况调整,部分也可采用百分比阈值,都行
  rules:
  - alert: 'RabbitMQ消息数超过阈值'
    expr: sum (rabbitmq_queue_messages) by (cluster_name) >= 10000
    for: 1m
    annotations:
      description: 'RabbitMQ集群({{ $labels.cluster_name }})消息数({{ $value }})超过阈值10000'
    labels:
      severity: 'warning'

  - alert: 'RabbitMQ实例可用内存低于阈值'
    expr: round((rabbitmq_resident_memory_limit_bytes - rabbitmq_process_resident_memory_bytes) / (1024 * 1024 * 1024), 0.01) < 1
    for: 1m
    annotations:
      description: 'RabbitMQ实例({{ $labels.instance }})可用内存({{ $value }}GB)低于阈值1GB'
    labels:
      severity: 'warning'

  - alert: 'RabbitMQ实例可用内存低于阈值'
    expr: round((rabbitmq_resident_memory_limit_bytes - rabbitmq_process_resident_memory_bytes) / (1024 * 1024), 0.01) < 100
    for: 1m
    annotations:
      description: 'RabbitMQ实例({{ $labels.instance }})可用内存({{ $value }}MB)即将耗尽,低于阈值100MB'
    labels:
      severity: 'critical'

  - alert: 'RabbitMQ实例可用磁盘空间低于阈值'
    expr: round((rabbitmq_disk_space_available_bytes - rabbitmq_disk_space_available_limit_bytes) / (1024 * 1024 * 1024), 0.01) < 40
    for: 1m
    annotations:
      description: 'RabbitMQ实例({{ $labels.instance }})可用磁盘空间({{ $value }}GB)低于阈值40GB'
    labels:
      severity: 'warning'

  - alert: 'RabbitMQ实例可用磁盘空间低于阈值'
    expr: round((rabbitmq_disk_space_available_bytes - rabbitmq_disk_space_available_limit_bytes) / (1024 * 1024 * 1024), 0.01) < 10
    for: 1m
    annotations:
      description: 'RabbitMQ实例({{ $labels.instance }})可用磁盘空间({{ $value }}GB)即将耗尽,低于阈值10GB'
    labels:
      severity: 'critical'

  - alert: 'RabbitMQ进程已使用文件描述符数量超过阈值'
    expr: rabbitmq_process_open_fds > 10000
    for: 1m
    annotations:
      description: 'RabbitMQ实例({{ $labels.instance }})进程已使用文件描述符数量({{ $value }})超过阈值10000'
    labels:
      severity: 'warning'

  - alert: 'RabbitMQ进程剩余可用文件描述符数量低于阈值'
    expr: rabbitmq_process_max_fds - rabbitmq_process_open_fds < 10000
    for: 1m
    annotations:
      description: 'RabbitMQ实例({{ $labels.instance }})进程剩余可用文件描述符数量({{ $value }})即将耗尽,低于阈值10000'
    labels:
      severity: 'critical'

  - alert: 'RabbitMQ实例当前进程数超过阈值'
    expr: erlang_vm_process_count > 10000
    for: 1m
    annotations:
      description: 'RabbitMQ实例({{ $labels.instance }})当前进程数({{ $value }})超过阈值10000'
    labels:
      severity: 'warning'

  - alert: 'RabbitMQ实例剩余可用进程数低于阈值'
    expr: erlang_vm_process_limit - erlang_vm_process_count < 10000
    for: 1m
    annotations:
      description: 'RabbitMQ实例({{ $labels.instance }})剩余可用进程数({{ $value }})即将耗尽,低于阈值10000'
    labels:
      severity: 'critical'

  - alert: 'RabbitMQ集群可用性故障'
    expr: rabbitmq_cluster_status != 0
    for: 1m
    annotations:
      description: 'RabbitMQ主机({{ $labels.instance }})报告RabbitMQ集群({{ $labels.cluster_name }})存在脑裂(Network Partitions),请登录RabbitMQ管理后台确认'
    labels:
      severity: 'critical'

  - alert: 'RabbitMQ实例可用性故障'
    expr: rabbitmq_node_status != 0
    for: 1m
    annotations:
      description: 'RabbitMQ主机({{ $labels.instance }})报告其RabbitMQ实例不可用,请处理'
    labels:
      severity: 'critical'

然后重新加载Prometheus配置。

告警测试

经测试,RabbitMQ监控告警正常生效:

Prometheus监控RabbitMQ集群_Grafana_03