刚毕业的几年,我的主攻方向是以zabbix为核心的监控系统构建与监控模板制作,那时候公司的基础设施形态以传统的裸金属集群为主,涵盖各种型号的服务器、交换机、防火墙、存储设施、专用设备等等。21年入职新司后,无缘再接触这些嗡嗡作响的大家伙,新司的基础设施则以虚拟机、云上服务、docker/k8s为主,主攻方向也由zabbix转向prometheus。

两者孰优孰劣,网络上一直都有讨论。不过没有争议的是,zabbix众多开箱即用的模板,使得它在监控以snmp协议为主的物理设施方面,拥有绝对的优势,prometheus的snmp exporter,实践中的体验则是难用且性能极差。而prometheus作为CNCF第二个毕业项目,则当之无愧成为云原生时代监控系统的事实标准,例如k8s的api-server、kubelet等,各种中间件如rabbitmq、etcd等甚至某些业务型服务,天生自带prometheus格式的监控指标。zabbix自6.0后也已原生支持k8s,未曾体验不做评价。

本文以prometheus为基本组件构建监控系统。

架构设计

分片采集

由于使用了本地TSDB存储,因此单个promethethus的承载能力严格受限于节点的计算、存储能力;由于使用了pull采集模式,因此单个prometheus不便于应对跨多个隔离网络的场景。基本以上原因,大规模监控场景下prometheus必须做分片,即由多个prometheus分别采集某一区域的数据。这带来了两个问题:

  • 无法提供全局查询视图;
  • 无法对prometheus及告警规则进行统一管理;

联邦集群

因此prometheus还为我们提供了联邦能力,即再用一个中心prometheus,去采集各个子节点prometheus的数据,由这个中心prometheus提供统一查询或告警能力。




prometheus 监控mysql服务端口 prometheus snmp监控_可观测性


但在实践中我们发现,联邦集群存在着一些难以解决的问题。

  1. 可靠性差且缺乏可拓展性,存在单点问题且配置要求高,有限存储能力,中心节点无法容忍宕机,告警能力需要下沉;
  2. 系统整体暴露面增加,每个子节点均需要暴露查询端口;
  3. 联邦查询造成边缘节点的压力升高,联邦查询的非实时性造成的中心节点与边缘节点的数据精度不同;
  4. HA场景下数据无法去重;

Thanos

因此一些开源的解决方案应运而生,这其中的优秀代表包括以Cortex、Thanos、Grafana Mimir为代表的对象存储型系统,和以VictoriaMetrics为代表的本地磁盘存储型系统,由于历史原因,我们的系统采用了Thanos(但并不意味这是最佳选择)。


prometheus 监控mysql服务端口 prometheus snmp监控_运维_02


Thanos具有如下优点:

  1. 社区足够活跃,2-3月一次小版本更新,密切跟进最新技术;
  2. 与Prometheus完全兼容,学习成本较低;
  3. 很好地解决了联邦集群存在的问题:各个组件灵活伸缩,基于对象存储实现指标无限存储,prometheus remote write重传机制、distributor的复制因子、receive的wal提高系统容错能力,提供统一的全局视图,暴露面较少,整个系统只有写入路径的负载均衡需要对外暴露一个端口,支持查询去重;
  4. 冷热数据分离,receive本地TSDB可看作热数据提供近期数据较快的查询能力,对象存储中则保存较少使用的冷数据;
  5. compactor可进行数据降采样,在不损失精度的情况下大幅提高长时间跨度查询的速度;
  6. 支持多租户,不同的租户可分配不同的receive,查询前端放置代理的情况下可实现不同租户查询数据隔离;

prometheus agent

在整个系统中,prometheus成为单纯的采集传输工具,近乎于无状态应用。因此,从v2.32.0开始,prometheus引入了agent模式,专门用于远程写。在边缘prometheus无需提供查询告警能力的情况下,可以采用该模式部署,使边缘节点更加轻量化。

标签设计

合理的标签设计是快速准确检索的关键,标签应为有限值、有明显意义的值。

节点监控推荐使用标签值host、env、az、project等以快速定位节点归属及位置,集群服务如redis、kafka、zookeeper等推荐使用redis_cluster、kafka_cluster、zk_cluster等以方便进行集群区分、数据聚合。

在远程写场景下,每个prometheus还应具备唯一external_label,以区分数据来源、避免指标冲突。

中心化exporter部署及去exporter化

通常来说,node exporter、process exporter必须随操作系统部署。而其他exporter一般采用sidecar方式部署,但有一些exporter,我们推荐采用中心化部署,即统一部署于采集节点远程获取指标,目前主要为两类:

  • 一类是exporter采集的指标为集群指标(如kafka exporter、rabbitmq exporter、rocketmq exporter、elasticsearch exporter等),目的避免sidecar方式部署时exporter所在主机故障导致无法采集集群数据;
  • 一类是exporter支持采集多实例(如redis exporter、mysql exporter、pgsql exporter、blackbox exporter等),目的是便于管理,新增监控只需在prometheus端增加target,无需重复部署exporter,exporter副本数可随监控实例数量自由拓展;

现在越来越多的应用已经无需使用exporter,本身即可暴露指标,这是一种趋势。自有业务程序的指标,也推荐采用这种方式暴露,可以避免不必要的侵入性并简化采集路径。

自动化部署

随便系统规模的扩展,边缘prometheus数量变得越来越多,如果对这些节点进行统一管理会成为老大难的问题。例如如何进行版本管理、如何进行配置管理,传统的ansible方式显然不够灵活。

因此我们采用的方式是将整套系统全部k8s化,中心Thanos采用yaml文件管理,部署于中心k8s集群。边缘prometheus及附属的exporter则采用kustomize管理,不同的节点即为不同的overlay。通过gitlab+argocd实现完整的CICD流程。


prometheus 监控mysql服务端口 prometheus snmp监控_Powered by 金山文档_03


边缘prometheus离线的nodata告警

prometheus生成的up指标可以解决监控目标离线问题,但在push模式下,或者说本系统的远程写模式下,如果某个prometheus离线了,系统是无法感知的,因为所有的up指标,包括prometheus对自身的监控up{job="prometheus"}都是由这个prometheus上传的,它的离线就代表的up指标消失,因此使用up指标是无法触发边缘prometheus离线告警的。

如何解决这个问题?我们可以使用absent函数进行穷举,例如:

- alert: Prometheus离线
  expr: |
    absent(up{cluster="cluster1", job="prometheus-k8s"})
    or
    absent(up{cluster="cluster2", job="prometheus-k8s"})
    or
    absent(up{cluster="cluster3", job="prometheus-k8s"})
  for: 2m
  labels:
    level: "3级"
    severity: Warning
    project: ops
  annotations:
    description: "prometheus节点{{$labels.cluster}}离线超过2分钟"

这无疑是个有效的笨方法。

最近我又有了新的灵感,可以写一个nodata exporter,以首次启动时的获得的指标形成字典,并定时更新,形成nodata指标。后续实现再行分享。

远程写速率限制及OOO写入

当边缘prometheus因为某些原因较长时间离线后,重试机制会让其一次发送大量数据,对中心Thanos形成冲击。对该问题的应对可参看我的另一篇文章:

在日志系统如loki中,可以前置一个kafka,实现数据写入缓冲。目前Thanos还不支持这种配置,不过这也与日志与指标的特性有关,日志更重完整性,而指标更重及时性。

指标与日志的融合

业内的可观测性标准OpenTelemetry为我们定义了可观测性的基本组成:logs、metrics、traces,这三者本身并非独立的,例如对日志进行分析其实可以形成指标与trace。列举三个典型的开源系统,loki、prometheus、jaeger,这三者可共同构建一个完整的可观测系统。如SigNoz、Observatorium则正在视图构建一个统一的可观测套件。

目前我们的系统是缺乏链路追踪的,这类数据的获取对应用程序具有一定的侵入性,因此未能应用。但是日志与指标,借助prometheus+loki,目前已经可以完美融合。使用Grafana,可以构建指标+日志的融合面板(前提是指标与日志的标签需规划并统一),这对系统的排障具有极大的益处。