本文介绍了如何用
Prometheus
监控MongoDB
的慢查询,涉及用Docker
搭建Prometheus+Grafana
,针对MongoDB
的system.profile
编写自定义监控指标等。
概述
运行高业务要求系统时,监控系统运行状态是必备工作。传统上,监控的通常是运行环境、中间件这些标准对象,业务应用的监控通常是靠输出日志。在实践中,这种方式并不理想,第一,环境指标的监控和业务日志通常是分离的,很难准确定位问题;第二,日志输出主要凭开发人员的经验,缺乏标准和一致性,输出了作用也不大。
目前,prometheus
已经是云和容器环境事实上的监控标准,它本质上是一个时序数据库(Time Series Database),核心数据是Metric(指标)
。prometheus
通过各种Exporter
从各类系统中将要监控的数据转换为统一格式的指标,拉取后进行管理。Metric
有四种类型:Counter
,Gauge
,Histogram
,Summary
。每个Metric
由name
,label
,value
进行描述,例如:
mongodb_sys_cpu_user_ms{cl_role="mongod", instance="localhost:9216", job="mongo", rs_state="0"} 1564410
其中,打括号前面的部分是name
,大括号里逗号分隔的部分是label
,打括号后面的是value
。
具体描述可参考:Prometheus Metric
既然有了标准的指标监控工具,是否可以针对我们自己编写应用提取监控指标?答案是肯定的,prometheus
官方提供了Go
,Java
,Python
和Ruby
的客户端基础库,社区提供了C
,Node.js
等语言的基础库,以它们为基础可以很容易的实现应用级的监控指标。
使用mongodb
时,监控慢查询是一项必不可少的工作,下面以监控system.profile
集合为例,体验一下自定义监控指标的开发。
搭建prometheus和grafana平台
为了看到最终成果,我们首先用docker搭建prometheus+grafana
环境。
准备docker-compose.yml
文件
version: '3.7'
services:
prometheus:
image: prom/prometheus:latest
ports:
- 9990:9090
volumes:
- ./prometheus.yml:/etc/prometheus/prometheus.yml
grafana:
image: grafana/grafana:latest
ports:
- 3000:3000
在docker-compose中需要指定配置文件prometheus.yml
,其中设置了从哪些目标地址抓取指标,默认设置抓取prometheus
自己的监控指标。
准备prometheus.yml
文件
# my global config
global:
scrape_interval: 15s # Set the scrape interval to every 15 seconds. Default is every 1 minute.
evaluation_interval: 15s # Evaluate rules every 15 seconds. The default is every 1 minute.
# scrape_timeout is set to the global default (10s).
# Alertmanager configuration
alerting:
alertmanagers:
- static_configs:
- targets:
# - alertmanager:9093
# Load rules once and periodically evaluate them according to the global 'evaluation_interval'.
rule_files:
# - "first_rules.yml"
# - "second_rules.yml"
# A scrape configuration containing exactly one endpoint to scrape:
# Here it's Prometheus itself.
scrape_configs:
# The job name is added as a label `job=<job_name>` to any timeseries scraped from this config.
- job_name: 'prometheus'
# metrics_path defaults to '/metrics'
# scheme defaults to 'http'.
static_configs:
- targets: ['localhost:9090']
详细说明可参考:Prometheus配置文件
启动服务,Prometheus+Grafana
就可以用了。为了看到效果,Grafana
需要进行一些配置。(网上做配置的教程非常多,就不重复了。)
监控mongoDB
已经有人实现了mongodb的exporter
,支持获取如下命令提供的监控指标:
- $collStats
- $indexStats
- getDiagnosticData
- replSetGetStatus
- serverStatus
例如指标mongodb_ss_connections
和db.serverStatus().connections
的结果对应。
我们在容器中部署mongodb_exporter
,先从github上下载进行文件,例如:mongodb_exporter-0.20.5.linux-amd64.tar.gz。
准备Dockerfile
FROM nginx:alpine
# 添加exporter,自动就进行解压
ADD ./mongodb_exporter-0.20.5.linux-amd64.tar.gz /opt/
WORKDIR /opt/mongodb_exporter-0.20.5.linux-amd64
# 启动exporter
CMD ['/opt/mongodb_exporter-0.20.5.linux-amd64/mongodb_exporter', '--mongodb.uri=mongodb://localhost:30004']
修改配置文件prometheus.yml
,添加抓取目标:
- job_name: mongo
static_configs:
- targets: ['docker.for.mac.host.internal:9216']
重启prometheus
使配置生效。
在浏览器中输入http://localhost:9990/targets
检查是否生效。确认生效后,就可以开始监控mongodb的基础指标了。
自定义监控指标
使用mongodb的应用出现性能问题时,通常都会涉及到分析慢查询,这里我们通过一个示例看看怎么输出慢查询的监控指标。
首先在被监控的库里打开慢查询日志,例如:记录执行时间超过100毫秒的操作:
db.setProfilingLevel(1, { slowms: 100 } )
如果需要清空数据,可以执行如下两条命令:
db.setProfilingLevel(0)
db.system.profile.drop()
设置监控参数后,mongodb会在数据库中自动建立system.profile
集合,查询其中的内容就可以知道慢查询的情况。
我们实现两个监控指标:1、在指定的时间范围内产生了多少次慢查询;2、在指定范围内慢查询消耗的总时间。在prometheus中这两个数据都可以用Counter
实现,就是记录到指定时间点累积的数值。prometheus提供了运算方法可以对一系列这样的指标进行允许,从而实现更直观的分析,后面我们会做简单演示。
下面看看如何用Node.js
实现。
const register = new Registry()
let { collectDefault, systemProfile } = metricsConfig
if (collectDefault === true) {
let msg = '提供默认系统监控指标'
logger.info(msg)
const collectDefaultMetrics = PromClient.collectDefaultMetrics
collectDefaultMetrics({ register })
}
上述代码进行exporter的初始化,生成用于记录指标的register
。可以控制register
中是否包含默认监控指标。
const { Counter } = require('prom-client')
...
const total = new Counter({
name: `${prefix}_mongodb_system_profile_total`,
help: '慢查询累积发生的次数',
labelNames: ['ns'],
registers: [metricsContext.register],
collect: async () => {
await OnlyOnceFetch.run(this).then((result) => {
result.data.forEach((nsData, ns) => {
total.labels({ ns }).inc(nsData.total)
})
})
},
})
const mills = new Counter({
name: `${prefix}_mongodb_system_profile_millis`,
help: '慢查询累积执行的时间',
labelNames: ['ns'],
registers: [metricsContext.register],
collect: async () => {
await OnlyOnceFetch.run(this).then((result) => {
result.data.forEach((nsData, ns) => {
mills.labels({ ns }).inc(nsData.millis)
})
})
},
})
...
上面的代码创建了两个Counter
,分别指定了name
,help
,labelNames
。labelNames
包含ns
,对应的是system.profile
中的ns
,记录操作的对象(db.collection),实现按操作的对象分组记录慢查询指标。指标通过registers
属性注册到exporter上。collect
是prometheus每次拉取数据时exporter调用的方法,因此,可以在该方法中更新指标的值,不用自己做定时循环。
router.get(prefix, async (ctx) => {
let { request, response } = ctx
const metricsContext = MetricsContext.insSync()
const metrics = await metricsContext.register.metrics()
response.body = metrics
})
抓取指标时只需要返回register.metrics()
的内容就可以,是格式化好的文本。
把程序跑起来,运行curl http://localhost:3001/metrics
,返回如下结果:
# HELP tms_slowquery_mongodb_system_profile_total 慢查询累积发生的次数
# TYPE tms_slowquery_mongodb_system_profile_total counter
tms_slowquery_mongodb_system_profile_total{ns="mydb.awfuldata"} 450
# HELP tms_slowquery_mongodb_system_profile_millis 慢查询累积执行的时间
# TYPE tms_slowquery_mongodb_system_profile_millis counter
tms_slowquery_mongodb_system_profile_millis{ns="mydb.awfuldata"} 348021
完整代码参见:tms-koa
total
的含义是到目前为止产生的慢查询总数,但是我们通常需要的是一段时间内慢查询发生的次数,例如:5分钟,这类需求可以通过prometheus提供的increase
方法实现
increase(tms_slowquery_mongodb_system_profile_total[5m])
总结
使用已有的exporter客户端急促库实现应用级业务指标监控比较简单,核心问题是监控指标的设计。虽然在代码中加入监控指标的跟踪给代码增加了一定的复杂度,但是在设计和实现阶段就充分考虑监控指标,必然有利于提高代码的整体质量,有利于研发和运维的分工配合,应该积极采用。