Kubernetes集群监控之Prometheus监控方案
如果说Kubernetes是事实上的容器平台标准,那么Prometheus就是云原生监控领域事实上的标准了。Kubernetes + Prometheus的组合自然就成了云原生基础设施的标准搭配。
下图是Kubernetes + Prometheus的通用监控方案
方案简介
此方案涉及如下几个重要的角色:
- Prometheus Server
- Exporter
- Alert Manager
- Grafana
- Kubernetes API Server
- DB,MiddleWare, Spring Application
Prometheus Server
Prometheus的核心,Prometheus Server 主要由以下几个组件组成:
-
数据存储(Storage):Prometheus 使用自己的时间序列数据库来存储采集的指标数据。这个数据库被设计成高效、快速、稳定,并且能够处理大规模的指标数据。
-
抓取器(Scraper):抓取器负责定期从各种数据源(如应用程序、操作系统、服务等)中抓取指标数据,并将其存储到时间序列数据库中。Prometheus 支持多种抓取方式,包括 HTTP 抓取、PushGateway 推送、服务发现等。
-
查询引擎(Query Engine):查询引擎负责处理用户发起的查询请求,并从时间序列数据库中检索和处理指标数据。Prometheus 使用自己的查询语言 PromQL 来进行查询和分析操作,支持丰富的函数和操作符,能够满足各种复杂的查询需求。
-
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 列表:
- 【Prometheus Exporter List】。
需要说明的是,有一些Exporter需要和被监控的对象部署在一起,而有一些则不需要,例如NodeExporter就是部署在需要被监控的Node节点上,用于采集当前节点的系统监控指标数据。
RedisExporter则可以独立部署,不需要和Redis集群部署在一起,这也是通常的做法。
AlertManager
Prometheus 中的 Alertmanager 是一个用于处理和管理告警的组件。它主要负责以下几个重要的事情:
-
告警路由和分组:Alertmanager 负责根据配置的路由规则将接收到的告警分配给相应的接收者。路由规则可以基于告警的标签匹配条件来定义,以确保告警能够被发送到正确的接收者。
-
告警去重和抑制:Alertmanager 会对接收到的告警进行去重和抑制,以避免发送重复的告警和避免告警风暴。它可以根据配置的策略对相同的告警进行合并或者抑制一段时间,从而减少告警的数量和频率。
-
告警通知:Alertmanager 负责将处理过的告警发送给配置的接收者,如电子邮件、Slack、PagerDuty 等。它支持多种通知方式,并且可以根据告警的严重程度和优先级进行灵活的配置和调整。
-
告警静默:Alertmanager 支持对指定的告警规则进行静默,即在一段时间内暂时禁止发送某个规则产生的告警通知。这对于临时性的维护和问题排查非常有用。
Grafana
Grafana 是一个开源的数据可视化和监控平台,主要用于展示和分析各种数据源的监控数据。它提供了丰富的图表和面板,可以直观地展示时间序列数据、日志数据、指标数据。
Grafana 提供了丰富的Dashboard,大家可以去官方查找和下载。
- 【Grafana Dashboards】
Redis Exporter 源码初探
Redis Exporter 源码下载
我们可以在Github上下载Redis Exporter源码
- 【Redis Exporter Github Repo】
打开Bash终端,并在终端执行git clone指令:
git clone git@github.com:oliver006/redis_exporter.git
结果如下:
vian@txzq1899-ubuntu:~/ws/redis-exporter$ git clone git@github.com: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 5月 12 18:30 ./
drwxrwxr-x 11 vian vian 4096 5月 12 18:29 ../
drwxrwxr-x 7 vian vian 4096 5月 12 18:30 redis_exporter/
使用Visual Studio Code打开 Redis_Exporter 项目
Redis Exporter 是一个Go语言应用程序,所以我们需要在本机安装Go语言环境,我们需要安装两个必备依赖:
- 最新版本的Go语言
- Visual Studio Code Go 语言插件
>>>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
>>>打开Redis_Exporter项目
当前两步准备就绪后,我们就可以使用Visual Studio Code方便的阅读Redis_Exporter的源码了,打开Redis_Exporter的源码文件夹后,如下图所示:
Redis Exporter 项目概览
我们先看根目录:
- Docker
- Dockerfile:用于构建RedisExporter镜像
- Exporter
- Exporter的实现细节
- go.mod:Go 模块的依赖管理
- go.sum:Go 模块的依赖管理
- main.go:RedisExporter 入口程序
- Makefile:RedisExporter 构建文件
Redis_Exporter主口程序main.go解读
main()函数
main函数从上往下,主要是做了以下事情
-
命令行参数解析:
- 使用
flag
包解析各种命令行参数,涵盖了 Redis 连接信息、监控配置、日志格式、TLS 加密等方面。
- 使用
-
日志配置:
- 根据
logFormat
参数设置日志输出格式(文本或 JSON)。 - 处理
showVersion
参数,如果为true
则打印版本信息后退出程序。 - 设置日志输出级别(调试或信息)。
- 根据
-
连接超时解析:
- 解析
connectionTimeout
参数,将其转换为时间间隔类型 (time.Duration
)。
- 解析
-
密码处理:
- 如果
redisPwd
为空且redisPwdFile
不为空,则从指定文件中读取密码。
- 如果
-
Lua 脚本加载:
- 如果
scriptPath
不为空,则依次加载指定路径的 Lua 脚本。
- 如果
-
指标注册:
- 创建
prometheus
注册器,用于收集和注册监控指标。 - 根据
redisMetricsOnly
参数决定是使用单独的注册器还是默认注册器。
- 创建
-
☆☆☆Redis 连接器创建:☆☆☆
- 创建
exporter.NewRedisExporter
对象,用于连接 Redis 并收集监控数据。 - 将解析到的配置信息作为参数传入构造函数。
- 创建
exp, err := exporter.NewRedisExporter(
*redisAddr,
exporter.Options{}
-
TLS 配置验证:
- 检查客户端证书和密钥文件是否成对出现。
- 创建客户端 TLS 配置并进行验证。
-
☆☆☆启动 web 服务:☆☆☆
- 打印提供监控指标的地址信息。
- 创建
http.Server
对象,用于监听指定端口并处理客户端请求。 - 根据
tlsServerCertFile
和tlsServerKeyFile
参数判断是否启用 TLS 加密。 - 启动 goroutine 运行 web 服务。
- go func() {
…
server.ListenAndServe()
…
}()
- go func() {
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)
}
}
}()
- 优雅退出:
- 创建信号通道
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()方法抓取监控指标
接口定义请查看prometheus collector源码:
home/vian/go/pkg/mod/github.com/prometheus/client_golang@v1.19.1/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的初始化(含指标定义)
所有的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/client_golang@v1.19.1/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/client_golang@v1.19.1/prometheus/registry.go
- 调用Collect方法()
最终调用Exporter中的Collect方法
代码跟到这里,指标采集的链路就形成了闭环。从Part3开始就是Prometheus的实现了,RedisExporter只需要实现相应的接口即可。
总结
当然Redis Exporter还有很多实现细节等着我们去探索,本文仅仅只是把Exporter的大致路径摸索了一下,刚刚找到入口,具体的细节还需要我们进一步的去探究。希望感兴趣的朋友一起交流和探索。
关注我的公众号
欢迎大家关注、点赞、转发,一起交流软件开发、架构设计、云原生技术。