Prometheus之exporter详解

何为exporter

Prometheus 监控基于一个很简单的模型: 主动抓取目标的指标接口(HTTP 协议)获取监控指标, 再存储到本地或远端的时序数据库. Prometheus 对于指标接口有一套固定的格式要求, 格式大致如下:

# HELP http_requests_total The total number of HTTP requests.
# TYPE http_requests_total counter
http_requests_total{method="post",code="200"} 1027
http_requests_total{method="post",code="400"} 3

对于自己写的代码, 我们当然可以使用 Prometheus 的 SDK 暴露出上述格式的指标. 但对于大量现有服务, 系统甚至硬件, 它们并不会暴露 Prometheus 格式的指标. 比如说:

  • Linux 的很多指标信息以文件形式记录在 proc 下的各个目录中, 如 /proc/meminfo 里记录内存信息, /proc/stat 里记录 CPU 信息;

  • Redis 的监控信息需要通过 INFO 命令获取;

  • 路由器等硬件的监控信息需要通过 `SNMP 协议获取;

要监控这些目标, 我们有两个办法, 一是改动目标系统的代码, 让它主动暴露 Prometheus 格式的指标, 当然, 对于上述几个场景这种办法完全是不现实的. 这时候就只能采用第二种办法:
编写一个代理服务, 将其它监控信息转化为 Prometheus 格式的指标
——这个代理服务就是exporter

简介

广义上讲所有可以向Prometheus提供监控样本数据的程序都可以被称为一个Exporter。而Exporter的一个实例称为target。

从Exporter的来源上来讲,主要分为两类:

  • 社区提供的
    Prometheus社区提供了丰富的Exporter实现,涵盖了从基础设施,中间件以及网络等各个方面的监控功能。这些Exporter可以实现大部分通用的监控需求。
    https://exporterhub.io/

  • 用户自定义的
    除了直接使用社区提供的Exporter程序以外,用户还可以基于Prometheus提供的Client Library创建自己的Exporter程序,目前Promthues社区官方提供了对以下编程语言的支持:Go、Java/Scala、Python、Ruby。同时还有第三方实现的如:Bash、C++、Common Lisp、Erlang,、Haskeel、Lua、Node.js、PHP、Rust等。

编写指导

Prometheus 官方文档中 Writing Exporter 这篇写得非常全面, 假如你要写 exporter 推荐先通读一遍, 限于篇幅, 这里只概括一下:

  • 做到开箱即用(默认配置就可以直接开始用)

  • 推荐使用 YAML 作为配置格式

  • 指标使用下划线命名

  • 为指标提供 HELP String (指标上的 # HELP 注释, 事实上这点大部分 exporter 都没做好)

  • 为 Exporter 本身的运行状态提供指标

  • 可以提供一个落地页

实现

Prometheus 官方提供了client library 来帮助开发者简化exporter的开发工作。
client library官方支持语言:

  • Go

  • Java or Scala

  • Python

  • Ruby

  • Rust

也有社区支持的其他语言库如C、C++、PHP等

下面以python简单介绍下实现过程

1. 实现collector

def collect(self):
    with self._lock:
        deployments = self.deployments
    for value in deployments.values():
        metric_labels = ["query_details"]

        # rabbitmq 总体状态
        if value and value.get('rabbitmq_status') is not None:
            total_labels_value = {}
            if value.get('list_queues_label_value') is not None:
                total_labels_value['list_queues'] = value.get('list_queues_label_value')
            if value.get('message_number_label_value') is not None:
                total_labels_value['message_number'] = value.get('message_number_label_value')
            rabbitmq_status_int = GaugeMetricFamily(
                name='rabbitmq_status_int',
                documentation='rabbitmq status',
                labels=metric_labels,
            )
            rabbitmq_status_int.add_metric([str(total_labels_value)], value.get('rabbitmq_status'))
            yield rabbitmq_status_int

        # rabbitmq server存活状态
        if value and value.get('rabbitmq_server_status') is not None:
            rabbitmq_server_status_int = GaugeMetricFamily(
                name='rabbitmq_server_status_int',
                documentation='rabbitmq server status',
                labels=['check']
            )
            rabbitmq_server_status_int.add_metric(['service rabbitmq-server status'],
                                                  value=value.get('rabbitmq_server_status'))
            yield rabbitmq_server_status_int

        # rabbitmq 消息数量
        if value and value.get('message_number') is not None:
            labels = ['queue', 'msg_obj']
            # rabbitmq_message_number
            rabbitmq_message_number = GaugeMetricFamily(
                name='rabbitmq_message_number',
                documentation='Number of queue message',
                labels=labels,
            )

            # 以msg_obj为纬度的消息数量采集:方便告警规则的创建
            consumers_rabbitmq_message_number = GaugeMetricFamily(
                name='consumers_rabbitmq_message_number',
                documentation='Number of queue message',
                labels=['queue'],
            )
            messages_rabbitmq_message_number = GaugeMetricFamily(
                name='messages_rabbitmq_message_number',
                documentation='Number of queue message',
                labels=['queue'],
            )
            messages_ready_rabbitmq_message_number = GaugeMetricFamily(
                name='messages_ready_rabbitmq_message_number',
                documentation='Number of queue message',
                labels=['queue'],
            )
            messages_unacknowledged_rabbitmq_message_number = GaugeMetricFamily(
                name='messages_unacknowledged_rabbitmq_message_number',
                documentation='Number of queue message',
                labels=['queue'],
            )

            queues = value.get('message_number')
            # queues = {
            # "queue_name": {
            # "consumers": 1
            # }
            # }
            for queue in queues.keys():
                for msg_obj in queues[queue].keys():
                    rabbitmq_message_number.add_metric([queue, msg_obj], value=queues[queue][msg_obj])
                consumers_rabbitmq_message_number.add_metric([queue], value=queues[queue]["consumers"])
                messages_rabbitmq_message_number.add_metric([queue], value=queues[queue]["messages"])
                messages_ready_rabbitmq_message_number.add_metric([queue], value=queues[queue]["messages_ready"])
                messages_unacknowledged_rabbitmq_message_number.add_metric([queue], value=queues[queue]["messages_unacknowledged"])
            yield rabbitmq_message_number
            yield consumers_rabbitmq_message_number
            yield messages_rabbitmq_message_number
            yield messages_ready_rabbitmq_message_number
            yield messages_unacknowledged_rabbitmq_message_number

2. 注册collector

REGISTRY.register(DeploymentCollector(logger, {}))
REGISTRY.register(CoreDumpCollector(logger, {}))
REGISTRY.register(ProcessCollector(logger, {}))
REGISTRY.register(RabbitmqCollector(logger, {}))
REGISTRY.register(CustomizeCollector(logger, {}))

3. 启动http服务

start_http_server(9200)

application = tornado.web.Application([
    (r"/", MainHandler),
    (r"/deregister/", DeregisterHandler),
    (r"/upload/", UploadHandler),
    (r"/alert/", AlertHandler)
])
application.listen(9201)
tornado.ioloop.IOLoop.current().start()
while True:
    time.sleep(1)

posted on   萌兰三太子  阅读(361)  评论(0编辑  收藏  举报  

相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· 三行代码完成国际化适配,妙~啊~
· .NET Core 中如何实现缓存的预热?
· 如何调用 DeepSeek 的自然语言处理 API 接口并集成到在线客服系统
< 2025年3月 >
23 24 25 26 27 28 1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31 1 2 3 4 5

导航

统计

点击右上角即可分享
微信分享提示