用Prometheus监控MongoDB慢查询

本文介绍了如何用Prometheus监控MongoDB的慢查询,涉及用Docker搭建Prometheus+Grafana,针对MongoDBsystem.profile编写自定义监控指标等。

概述

运行高业务要求系统时,监控系统运行状态是必备工作。传统上,监控的通常是运行环境、中间件这些标准对象,业务应用的监控通常是靠输出日志。在实践中,这种方式并不理想,第一,环境指标的监控和业务日志通常是分离的,很难准确定位问题;第二,日志输出主要凭开发人员的经验,缺乏标准和一致性,输出了作用也不大。

目前,prometheus已经是云和容器环境事实上的监控标准,它本质上是一个时序数据库(Time Series Database),核心数据是Metric(指标)prometheus通过各种Exporter从各类系统中将要监控的数据转换为统一格式的指标,拉取后进行管理。Metric有四种类型:CounterGaugeHistogramSummary。每个Metricnamelabelvalue进行描述,例如:

mongodb_sys_cpu_user_ms{cl_role="mongod", instance="localhost:9216", job="mongo", rs_state="0"} 1564410

其中,打括号前面的部分是name,大括号里逗号分隔的部分是label,打括号后面的是value

具体描述可参考:Prometheus Metric

既然有了标准的指标监控工具,是否可以针对我们自己编写应用提取监控指标?答案是肯定的,prometheus官方提供了GoJavaPythonRuby的客户端基础库,社区提供了CNode.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_connectionsdb.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_exporter

自定义监控指标

使用mongodb的应用出现性能问题时,通常都会涉及到分析慢查询,这里我们通过一个示例看看怎么输出慢查询的监控指标。

首先在被监控的库里打开慢查询日志,例如:记录执行时间超过100毫秒的操作:

db.setProfilingLevel(1, { slowms: 100 } )

如果需要清空数据,可以执行如下两条命令:

db.setProfilingLevel(0)

db.system.profile.drop()

设置监控参数后,mongodb会在数据库中自动建立system.profile集合,查询其中的内容就可以知道慢查询的情况。

参考:MongoDB Databse Profiler

我们实现两个监控指标: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,分别指定了namehelplabelNameslabelNames包含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客户端急促库实现应用级业务指标监控比较简单,核心问题是监控指标的设计。虽然在代码中加入监控指标的跟踪给代码增加了一定的复杂度,但是在设计和实现阶段就充分考虑监控指标,必然有利于提高代码的整体质量,有利于研发和运维的分工配合,应该积极采用。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,126评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,254评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,445评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,185评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,178评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,970评论 1 284
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,276评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,927评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,400评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,883评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,997评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,646评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,213评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,204评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,423评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,423评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,722评论 2 345

推荐阅读更多精彩内容