Kubernetes + Prometheus监控体系之 - Exporter源码初探(以RedisExporter为例)

Kubernetes集群监控之Prometheus监控方案

如果说Kubernetes是事实上的容器平台标准,那么Prometheus就是云原生监控领域事实上的标准了。Kubernetes + Prometheus的组合自然就成了云原生基础设施的标准搭配。
下图是Kubernetes + Prometheus的通用监控方案
Prometheus监控方案

方案简介

此方案涉及如下几个重要的角色:

  • Prometheus Server
  • Exporter
  • Alert Manager
  • Grafana
  • Kubernetes API Server
  • DB,MiddleWare, Spring Application

Prometheus Server

Prometheus的核心,Prometheus Server 主要由以下几个组件组成:

  1. 数据存储(Storage):Prometheus 使用自己的时间序列数据库来存储采集的指标数据。这个数据库被设计成高效、快速、稳定,并且能够处理大规模的指标数据。

  2. 抓取器(Scraper):抓取器负责定期从各种数据源(如应用程序、操作系统、服务等)中抓取指标数据,并将其存储到时间序列数据库中。Prometheus 支持多种抓取方式,包括 HTTP 抓取、PushGateway 推送、服务发现等。

  3. 查询引擎(Query Engine):查询引擎负责处理用户发起的查询请求,并从时间序列数据库中检索和处理指标数据。Prometheus 使用自己的查询语言 PromQL 来进行查询和分析操作,支持丰富的函数和操作符,能够满足各种复杂的查询需求。

  4. Web 服务器(Web Server):Prometheus Server 包含一个内置的 Web 服务器,用于提供用户界面和 HTTP API。用户可以通过 Web 界面或 API 来查看监控指标、执行查询、设置告警规则等操作。

这些组件共同构成了 Prometheus Server 的核心功能。一般来讲,整体的监控流程如下:

  • 定义一个抓取任务
  • 访问任务中定义的Exporter抓取监控指标数据
  • 将指标数据存储在内部的时序数据库中
  • 通过HTTP API执行查询,以及设置告警规则等
  • 由查询引擎负责处理用户的查询请求

各种Exporter

Exporter 是一种用于从各种系统和服务中抓取指标数据并暴露给 Prometheus 进行监控的软件组件或工具。Exporter主要完成以下几件事情:

  • 连接到指定的服务器,或组件
  • 抓取指定的监控指标
  • 通过HTTP服务将指标数据暴露给Prometheus Server

常用的Exporter有 Node Exporter, JMX Exporter, Redis Exporter, MySQL Exporter, Kubernetes State Metrics Exporter等等。想要知道更多的Exporter可以查看Prometheus官方Exporter 列表:

需要说明的是,有一些Exporter需要和被监控的对象部署在一起,而有一些则不需要,例如NodeExporter就是部署在需要被监控的Node节点上,用于采集当前节点的系统监控指标数据。
RedisExporter则可以独立部署,不需要和Redis集群部署在一起,这也是通常的做法。

AlertManager

Prometheus 中的 Alertmanager 是一个用于处理和管理告警的组件。它主要负责以下几个重要的事情:

  1. 告警路由和分组:Alertmanager 负责根据配置的路由规则将接收到的告警分配给相应的接收者。路由规则可以基于告警的标签匹配条件来定义,以确保告警能够被发送到正确的接收者。

  2. 告警去重和抑制:Alertmanager 会对接收到的告警进行去重和抑制,以避免发送重复的告警和避免告警风暴。它可以根据配置的策略对相同的告警进行合并或者抑制一段时间,从而减少告警的数量和频率。

  3. 告警通知:Alertmanager 负责将处理过的告警发送给配置的接收者,如电子邮件、Slack、PagerDuty 等。它支持多种通知方式,并且可以根据告警的严重程度和优先级进行灵活的配置和调整。

  4. 告警静默:Alertmanager 支持对指定的告警规则进行静默,即在一段时间内暂时禁止发送某个规则产生的告警通知。这对于临时性的维护和问题排查非常有用。

Grafana

Grafana 是一个开源的数据可视化和监控平台,主要用于展示和分析各种数据源的监控数据。它提供了丰富的图表和面板,可以直观地展示时间序列数据、日志数据、指标数据。
Grafana 提供了丰富的Dashboard,大家可以去官方查找和下载。

Redis Dashboard

Redis Exporter 源码初探

Redis Exporter 源码下载

我们可以在Github上下载Redis Exporter源码

打开Bash终端,并在终端执行git clone指令:

git clone [email protected]:oliver006/redis_exporter.git

结果如下:

vian@txzq1899-ubuntu:~/ws/redis-exporter$ git clone [email protected]:oliver006/redis_exporter.git
正克隆到 'redis_exporter'...
remote: Enumerating objects: 3882, done.
remote: Counting objects: 100% (891/891), done.
remote: Compressing objects: 100% (317/317), done.
remote: Total 3882 (delta 684), reused 589 (delta 572), pack-reused 2991
接收对象中: 100% (3882/3882), 7.61 MiB | 1.07 MiB/s, 完成.
处理 delta 中: 100% (2156/2156), 完成.
vian@txzq1899-ubuntu:~/ws/redis-exporter$ ll
总计 12
drwxrwxr-x  3 vian vian 4096  512 18:30 ./
drwxrwxr-x 11 vian vian 4096  512 18:29 ../
drwxrwxr-x  7 vian vian 4096  512 18:30 redis_exporter/

使用Visual Studio Code打开 Redis_Exporter 项目

Redis Exporter 是一个Go语言应用程序,所以我们需要在本机安装Go语言环境,我们需要安装两个必备依赖:

  • 最新版本的Go语言
  • Visual Studio Code Go 语言插件

>>>Go 语言安装
可参考官方指引,安装步骤很简单:

【Step 1】下载go语言安装包
在Bash终端执行如下指令

wget https://go.dev/dl/go1.22.3.linux-amd64.tar.gz

等待建立连接和下载

vian@txzq1899-ubuntu:~/ws$ wget https://go.dev/dl/go1.22.3.linux-amd64.tar.gz
--2024-05-12 18:47:10--  https://go.dev/dl/go1.22.3.linux-amd64.tar.gz
正在解析主机 go.dev (go.dev)... 216.239.38.21, 216.239.34.21, 216.239.32.21, ...
正在连接 go.dev (go.dev)|216.239.38.21|:443... 已连接。
已发出 HTTP 请求,正在等待回应... 302 Found
位置:https://dl.google.com/go/go1.22.3.linux-amd64.tar.gz [跟随至新的 URL]
--2024-05-12 18:47:11--  https://dl.google.com/go/go1.22.3.linux-amd64.tar.gz
正在解析主机 dl.google.com (dl.google.com)... 114.250.70.33
正在连接 dl.google.com (dl.google.com)|114.250.70.33|:443... 已连接。
已发出 HTTP 请求,正在等待回应... 200 OK
长度: 68958945 (66M) [application/x-gzip]
正在保存至: ‘go1.22.3.linux-amd64.tar.gz’

go1.22.3.linux-amd64.tar.gz           100%[=======================================================================>]  65.76M  5.79MB/s    用时 13s   

2024-05-12 18:47:24 (4.98 MB/s) - 已保存 ‘go1.22.3.linux-amd64.tar.gz’ [68958945/68958945])

【Step 2】解压缩
在Bash终端执行如下命令,这条命令首先会删除原先已经安装的go环境,然后将最新版的go语言安装至/usr/local/go目录下。

 rm -rf /usr/local/go && tar -C /usr/local -xzf go1.22.3.linux-amd64.tar.gz

【Step 3】配置GO PATH
修改**~/.profile**文件,将go语言加入到PATH中

export PATH=$PATH:/usr/local/go/bin

【Step 4】查看Go 版本

vian@txzq1899-ubuntu:~/ws$ go version
go version go1.22.3 linux/amd64

>>>安装VisualStudioCode GO 语言插件
点击“扩展”(Ctrl+Shift+X),搜索"Go",第一个:Go Team at Google ☆ go.dev
Visual Studio Code Go 插件


>>>打开Redis_Exporter项目
当前两步准备就绪后,我们就可以使用Visual Studio Code方便的阅读Redis_Exporter的源码了,打开Redis_Exporter的源码文件夹后,如下图所示:Redis-Exporter-Project


Redis Exporter 项目概览

我们先看根目录:

  • Docker
    • Dockerfile:用于构建RedisExporter镜像
  • Exporter
    • Exporter的实现细节
  • go.mod:Go 模块的依赖管理
  • go.sum:Go 模块的依赖管理
  • main.go:RedisExporter 入口程序
  • Makefile:RedisExporter 构建文件

Redis_Exporter主口程序main.go解读

main()函数
main函数从上往下,主要是做了以下事情

  1. 命令行参数解析:

    • 使用 flag 包解析各种命令行参数,涵盖了 Redis 连接信息、监控配置、日志格式、TLS 加密等方面。
  2. 日志配置:

    • 根据 logFormat 参数设置日志输出格式(文本或 JSON)。
    • 处理 showVersion 参数,如果为 true 则打印版本信息后退出程序。
    • 设置日志输出级别(调试或信息)。
  3. 连接超时解析:

    • 解析 connectionTimeout 参数,将其转换为时间间隔类型 (time.Duration)。
  4. 密码处理:

    • 如果 redisPwd 为空且 redisPwdFile 不为空,则从指定文件中读取密码。
  5. Lua 脚本加载:

    • 如果 scriptPath 不为空,则依次加载指定路径的 Lua 脚本。
  6. 指标注册:

    • 创建 prometheus 注册器,用于收集和注册监控指标。
    • 根据 redisMetricsOnly 参数决定是使用单独的注册器还是默认注册器。
  7. ☆☆☆Redis 连接器创建:☆☆☆

    • 创建 exporter.NewRedisExporter 对象,用于连接 Redis 并收集监控数据。
    • 将解析到的配置信息作为参数传入构造函数。
exp, err := exporter.NewRedisExporter(
		*redisAddr,
		exporter.Options{
    
    }
  1. TLS 配置验证:

    • 检查客户端证书和密钥文件是否成对出现。
    • 创建客户端 TLS 配置并进行验证。
  2. ☆☆☆启动 web 服务:☆☆☆

    • 打印提供监控指标的地址信息。
    • 创建 http.Server 对象,用于监听指定端口并处理客户端请求。
    • 根据 tlsServerCertFiletlsServerKeyFile 参数判断是否启用 TLS 加密。
    • 启动 goroutine 运行 web 服务。
      • go func() {

        server.ListenAndServe()

        }()
server := &http.Server{
    
    
		Addr:    *listenAddress,
		Handler: exp,
	}

go func() {
    
    
		if *tlsServerCertFile != "" && *tlsServerKeyFile != "" {
    
    
			...
			if err := server.ListenAndServeTLS("", ""); err != nil && !errors.Is(err, http.ErrServerClosed) {
    
    
				log.Fatalf("TLS Server error: %v", err)
			}
		} else {
    
    
			if err := server.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) {
    
    
				log.Fatalf("Server error: %v", err)
			}
		}
	}()
  1. 优雅退出:
  • 创建信号通道 quit ,用于接收终止信号 (SIGINT, SIGTERM)。
  • 监听信号并捕获第一个收到的信号。
  • 创建带超时的 context,用于控制服务优雅关闭的时间。
  • 根据 context 关闭 web 服务。

exporter.go解读

  • 定义了Exporter结构体
  • 定义了Options结构体
  • 定义Redis监控指标的Exporter
    • func NewRedisExporter(redisURI string, opts Options) (*Exporter, error)
  • 实现了prometheus.Exporter接口
    • 实现了Collect()方法和Describe()方法
    • Collect()方法会调用scrapeRedisHost()方法抓取监控指标
      Exporter 整体结构
      接口定义请查看prometheus collector源码:
      home/vian/go/pkg/mod/github.com/prometheus/[email protected]/prometheus/collector.go
      在这里插入图片描述

Exporter 结构体解读

// Exporter implements the prometheus.Exporter interface, and exports Redis metrics.
type Exporter struct {
    
    
	sync.Mutex

	redisAddr string
	namespace string

	totalScrapes              prometheus.Counter
	scrapeDuration            prometheus.Summary
	targetScrapeRequestErrors prometheus.Counter

	//指标描述
	metricDescriptions map[string]*prometheus.Desc

	options Options

	//累计形指标
	metricMapCounters map[string]string
	//即时指标
	metricMapGauges   map[string]string

	//http多路复用器,用于处理http请求
	mux *http.ServeMux

	buildInfo BuildInfo
}

NewRedisExporter函数解读

  • 【Part1】Exporter的初始化(含指标定义)
    Exporter的初始化
    所有的Redis指标,均在NewRedisExporter中初始化Exporter时定义,从114行至301行:
	//累计形指标的详细定义
	metricMapCounters map[string]string
	//即时指标的详细定义 
	metricMapGauges   map[string]string
  • 【Part2】Exporter 的WEB 服务设置
    • 定义监听哪些HTTP请求的URI,以及对应的HTTP Handler

      • /
      • /metrics
      • /scrape
      • /health
      • /-/reload
      	......
      	if e.options.MetricsPath == "" {
              
              
      		e.options.MetricsPath = "/metrics"
      	}
      
      	e.mux = http.NewServeMux()
      
      	if e.options.Registry != nil {
              
              
      		e.options.Registry.MustRegister(e)
      		//这里用于定义/metrics的HTTP Handler
      		//promhttp.HandlerFor返回一个HTTP Handler
      		e.mux.Handle(e.options.MetricsPath, promhttp.HandlerFor(
      			e.options.Registry, promhttp.HandlerOpts{
              
              ErrorHandling: promhttp.ContinueOnError},
      		))
      		......
      		}
      	}
      
      	e.mux.HandleFunc("/", e.indexHandler)
      	e.mux.HandleFunc("/scrape", e.scrapeHandler)
      	e.mux.HandleFunc("/health", e.healthHandler)
      	e.mux.HandleFunc("/-/reload", e.reloadPwdFile)
      
  • 【Part3】promhttp.HandlerFor 源码解读
    源码路径:
    /home/vian/go/pkg/mod/github.com/prometheus/[email protected]/prometheus/promhttp/http.go
    • HandlerFor()方法
    func HandlerFor(reg prometheus.Gatherer, opts HandlerOpts) http.Handler {
          
          
    	return HandlerForTransactional(prometheus.ToTransactionalGatherer(reg), opts)
    }
    
    • HandlerForTransactional()方法
      • 调用reg.Gather()方法
      • 这里的register就是Exporter.Options.Register
        在这里插入图片描述
      • Register 实现 Gather接口 源码: /home/vian/go/pkg/mod/github.com/prometheus/[email protected]/prometheus/registry.go
      • 调用Collect方法()
        最终调用Exporter中的Collect方法
        调用collect方法
        代码跟到这里,指标采集的链路就形成了闭环。从Part3开始就是Prometheus的实现了,RedisExporter只需要实现相应的接口即可。

总结

当然Redis Exporter还有很多实现细节等着我们去探索,本文仅仅只是把Exporter的大致路径摸索了一下,刚刚找到入口,具体的细节还需要我们进一步的去探究。希望感兴趣的朋友一起交流和探索。

关注我的公众号

欢迎大家关注、点赞、转发,一起交流软件开发、架构设计、云原生技术。
TXZQ聊IT技术与架构

猜你喜欢

转载自blog.csdn.net/u011278722/article/details/138725540