原创 小斐Lab 网络小斐
在原生的 Prometheus 体系中,告警引擎评估模块是集成在 Prometheus 时序数据库中的,把告警规则定义好,放在 Prometheus 告警配置模块路径下即可实现对实例告警评估和触发。然后使用 Alertmanager 做告警路由和告警消息转发。
如下消息通知简单原理示意图:
而上面的告警规则: Alert rule 在 Prometheus 和 Alertmanager 结合的场景中所在位置和流程如下所示:
而在 VictoriaMetrics 体系中, VictoriaMetrics 时序库并没有集成告警引擎评估模块,而是把该模块单独拧出来,用单独的程序进行实现。也就是今天要讲的 vmalert 程序,它可以完全替代 Prometheus 时序库集成的的告警引擎评估模块,并且可以定义记录规则,根据需要可以把并记录规则的数据回写到 vmagent 或者 VictoriaMetrics 时序库中,最终的情况就是 vmalert 全面接管告警引擎评估模块,实现对告警规则做评估和发送告警通知,最终架构如下:
alertmanager 介绍
首先需要明白一个基本概念,时序库自身一般都不带告警功能(去重、抑制、静默、告警路由、发送告警)等功能,一般都需要使用单独的告警组件实现,不同的架构实现方式有点差异,这里我单独讲讲使用 Prometheus 时序库的架构和使用 VictoriaMetrics 时序库架构。
一般情况下触发一条告警的基本过程是什么样子的呢,这里以一副图来呈现:
在这些架构中告警都有 Alertmanager 配合,那 Alertmanager 在其中都起到什么作用呢?
Alertmanager 是一个专门用于实现告警的工具或组件,可以实现接收 Prometheus 或 vmalert 等其它应用发出的告警信息,并且可以对这些告警信息进行 分组 、 抑制 以及 静默 等操作,然后通过 路由 的方式,根据不同的告警规则配置,分发到不同的告警路由策略中。
除此之外, Alertmanager 还支持 邮件 、 "企业微信 、 Slack 、 WebHook 等多种方式发送告警信息,并且其中 WebHook 这种方式可以将告警信息转发到我们自定义的应用中,使我们可以对告警信息进行处理,所以使用 Alertmanager 进行告警,非常方便灵活、简单易用。
Alertmanager 常用的功能主要有:
抑制: 抑制是一种机制,指的是当某一告警信息发送后,可以停止由此告警引发的其它告警,避免相同的告警信息重复发送。
静默: 静默也是一种机制,指的是依据设置的标签,对告警行为进行静默处理。如果 AlertManager 接收到的告警符合静默配置,则 Alertmanager 就不会发送该告警通知。
发送告警: 支持配置多种告警规则,可以根据不同的路由配置,采用不同的告警方式发送告警通知。
告警分组: 分组机制可以将详细的告警信息合并成一个通知。在某些情况下,如系统宕机导致大量的告警被同时触发,在这种情况下分组机制可以将这些被触发的告警信息合并为一个告警通知,从而避免一次性发送大量且属于相同问题的告警,导致无法对问题进行快速定位。
那 Alertmanager 在 Prometheus 或 VictoriaMetrics 使用的时序库架构中是什么样的关系呢,下面用两张图去呈现?
两者区别就是 Prometheus 内部集成告警规则引擎,而 VictoriaMetrics 是通过外部单独组件 vmalert 实现告警规则引擎。
Prometheus 架构下的记录规则直接写入到 Prometheus 时序库中,而 VictoriaMetrics 架构下的记录规则可以直接写入 vmalert 定义的参数 -remoteWrite.url
中,一般配置为 vmagent 地址或 VictoriaMetrics 地址即可。
其实这里讲的告警只是一个很简单的告警通知逻辑,成熟的商业化告警有一系列的后续处理,比如:多渠道分级通知、告警静默、抑制、收敛聚合、降噪、排班、认领升级、协同闭环处理等等,习惯称呼为 OnCall
平台,如果有这一块需求,应该去了解商业化告警产品。
配置告警
按照上面的两个不同时序库的告警架构,我们可以看到首先需要定义告警规则,然后把定义好的告警规则放入到一个文件中,让不同的告警引擎可以加载这些规则,实现对时序库中的需要关注的指标做告警评估。
告警规则
告警规则的简单示例:
groups: # 定义一个告警规则组
- name: Instances # 规则组组名,可以将同一类型的报警放到一个分组中
rules: # 定义告警规则,可以有多个
- alert: 主机宕机 # 告警名称(alertname),也就是告警信息的标题,一个alert代表一个告警规则
expr: up == 0 # 表达式,根据表达式的值进行匹配
for: 5m # 表达式评估触发报警,发送告警消息前的等待时间,如果在这个实际内恢复,状态恢复到健康状态
labels: # 定义标签,可自定义多个标签
severity: Critical # 告警级别
annotations: # 定义告警内容
# 消息内容,$labels.instance 就是监控项中的标签变量
summary: 主机 {{ $labels.instance }} 停止工作
# 详细描述
description: "{{ $labels.instance }} job {{ $labels.job }} 已经宕机5分钟以上!"
详细介绍一条告警规则的字段:
alert :告警规则的名称( alertname ),在 Alertmanager 喜欢使用 alertname 进行告警分组。
expr :定义告警条件的 PromQL/MetricsQL 表达式,这里是判断主机状态表达式。
for :规定持续多长时间满足条件后触发告警。
labels :添加到告警标签中的标签,例如,定义告警的严重性。
annotations :提供有关告警的描述性信息,一般定义 summary (摘要信息)和 description (详细描述)。
如何针对这些字段定义呢?
alert 是指某条告警规则的名称,可以自定义,比如:内存使用率超限可以定义为 HostOutOfMemory ,名称支持中文定义。 expr 就是告警条件表达式,主要是查询表达式加逻辑符号组成。 for 和告警规则状态有关,一共有三种告警规则状态,下面会具体说下。 labels 是指在告警消息触发时加的告警标签,可作为后续告警消息处理使用。 annotations 就是具体告警内容,可以在内容中通过标签引用和指标样本值引用把标签值和样本值插入告警内容中。
告警规则状态:
Inactive :无任何报警,一切正常
Pending :已触发阈值,但未满足告警持续时间,也就是在告警规则中写的 for ,在 for 规定的时间内触发都不会发送给 AlertManager ,当 for 持续时间一过会立即发送给 AlertManager
Firing :已触发阈值且满足告警持续时间,告警发送给接收者
告警分级:
告警分级:告警规则文件中 labels 下的 severity 定义
critical(临界) :表示最严重的问题,需要立即采取紧急措施,通常涉及到重大的系统故障或安全问题。
high(高) :表示高级别的告警,需要尽快处理,通常是性能下降、服务不稳定等问题。
warning(警告) :表示一般警告,通常表示存在潜在的问题或异常情况,需要引起关注,但不需要立即采取紧急措施。
info(信息) :用于提供一般信息,通常不是告警,而是关于系统运行状态或事件的通知。
debug(调试) :用于调试目的,通常不会在生产环境中使用,而是用于开发和故障排除。
一般用前面: Critical 、 High 、 Warning 这三个,主要评估告警规则的严重程度,后续可以通过告警分级来处理告警信息,比如不同的告警级别路由发送给不同的机器人,夜莺中把告警分为(S1、S2、S3级别),告警分级可以根据各自组织实际情况去做定义。
下面演示下 Prometheus 下和 vmalert 下如何加载告警规则配置文件:
在 /etc/prometheus/rules/ 目录下新建 Prometheus 告警规则文件 alerts-prometheus.yml
,实现对 Prometheus 时序库的告警:
groups:
- name: prometheus.rules
rules:
- alert: PrometheusJobMissing
expr: absent(up{job="prometheus"})
for: 0m
labels:
severity: warning
annotations:
summary: Prometheus job missing (instance {{ $labels.instance }})
description: "A Prometheus job has disappeared\n VALUE = {{ $value }}\n LABELS = {{ $labels }}"
- alert: PrometheusTargetMissing
expr: up == 0
for: 0m
labels:
severity: critical
annotations:
summary: Prometheus target missing (instance {{ $labels.instance }})
description: "A Prometheus target has disappeared. An exporter might be crashed.\n VALUE = {{ $value }}\n LABELS = {{ $labels }}"
- alert: PrometheusAllTargetsMissing
expr: sum by (job) (up) == 0
for: 0m
labels:
severity: critical
annotations:
summary: Prometheus all targets missing (instance {{ $labels.instance }})
description: "A Prometheus job does not have living target anymore.\n VALUE = {{ $value }}\n LABELS = {{ $labels }}"
- alert: PrometheusTargetMissingWithWarmupTime
expr: sum by (instance, job) ((up == 0) * on (instance) group_right(job) (node_time_seconds - node_boot_time_seconds > 600))
for: 0m
labels:
severity: critical
annotations:
summary: Prometheus target missing with warmup time (instance {{ $labels.instance }})
description: "Allow a job time to start up (10 minutes) before alerting that it's down.\n VALUE = {{ $value }}\n LABELS = {{ $labels }}"
- alert: PrometheusConfigurationReloadFailure
expr: prometheus_config_last_reload_successful != 1
for: 0m
labels:
severity: warning
annotations:
summary: Prometheus configuration reload failure (instance {{ $labels.instance }})
description: "Prometheus configuration reload error\n VALUE = {{ $value }}\n LABELS = {{ $labels }}"
- alert: PrometheusTooManyRestarts
expr: changes(process_start_time_seconds{job=~"prometheus|pushgateway|alertmanager"}[15m]) > 2
for: 0m
labels:
severity: warning
annotations:
summary: Prometheus too many restarts (instance {{ $labels.instance }})
description: "Prometheus has restarted more than twice in the last 15 minutes. It might be crashlooping.\n VALUE = {{ $value }}\n LABELS = {{ $labels }}"
接下来打开 Prometheus 主配置文件,编辑 prometheus.yml
文件,如下所示:
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 的服务所在主机与端口
- 172.17.40.25:9093
# Load rules once and periodically evaluate them according to the global 'evaluation_interval'.
rule_files:
# 指定告警规则文件所在目录路径,最终文件可以通过通配符匹配或者单独指定具体文件
- "/etc/prometheus/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 服务主机上执行命令,实现配置的热加载:
curl -X POST http://localhost:9090/-/reload
打开 Prometheus Web UI 查看配置规则是否加载成功:
上面是针对 Prometheus 架构,如果是使用 VictoriaMetrics 架构,我们就需要部署 vmalert 告警引擎,然后在 vmalert 中配置相关参数关联 VictoriaMetrics 时序库和 Alertmanager 组件。
打开 vmalert 启动配置参数,修改启动配置文件,在我的脚本中都是放在 /etc/victoriametrics/vmalert/ 目录下的 vmalert.conf
,如下所示:
# -rule 指定告警规则文件路径,可使用通配符匹配整个目录的告警规则文件
-rule=/etc/victoriametrics/vmalert/alerts.yml
# -datasource.url 指定数据源,也就是时序库 API 地址,这里填写 VictoriaMetrics 数据源
-datasource.url=http://172.17.40.25:8428
# -notifier.url 指定告警触发后消息通知地址,也就是 Alertmanager 地址
-notifier.url=http://172.17.40.25:9093
# -notifier.url=http://172.17.40.26:9093 如果 Alertmanager 是集群部署可以指定多个 Alertmanager 地址
# 远程写入兼容的时序库,主要应用在记录规则中使用,这里可以指定 VictoriaMetrics 时序库地址也可以指定 vmagent 地址
-remoteWrite.url=http://172.17.40.25:8428
# 重启后从时序库查询中恢复内存中的告警状态
-remoteRead.url=http://172.17.40.25:8428
浏览器打开 vmalert 我们可以直接访问 http://172.17.40.25:8880/ 可以发现 vmalert 正常打开,但是每次单独打开都比较麻烦,我们可以设置 VictoriaMetrics 时序库代理地址打开:
打开 VictoriaMetrics 时序库的启动配置文件,我的安装文档都是在 /etc/victoriametrics/single/ 目录下的 vmsingle.conf
下:
-storageDataPath=/var/lib/victoria-metrics-data
-retentionPeriod=90d
-httpListenAddr=:8428
-selfScrapeInterval=15s
-vmui.defaultTimezone=Local
# 这里添加 -vmalert.proxyURL 为 vmalert 的地址与端口即可
-vmalert.proxyURL=http://172.17.40.25:8880
然后我们重加载 VictoriaMetrics 后,直接在浏览器中打开 VictoriaMetrics 地址加 vmalert ,也就是 http://
接下来需要把告警规则文件写好,这里我写针对 VictoriaMetrics 、 vmagent 以及 vmalert 这三个组件的告警规则文件,然后加载告警规则文件:
在目录 /etc/victoriametrics/vmalert 下新建 rules 目录,然后修改启动配置文件:
-rule=/etc/victoriametrics/vmalert/rules/*.yml
解析来在目录 /etc/victoriametrics/vmalert/rules 下新建告警规则文件即可,如下所示:
# File contains default list of alerts for VictoriaMetrics single server.
# The alerts below are just recommendations and may require some updates
# and threshold calibration according to every specific setup.
groups:
# Alerts group for VM single assumes that Grafana dashboard
# https://grafana.com/grafana/dashboards/10229 is installed.
# Pls update the `dashboard` annotation according to your setup.
- name: vmsingle
interval: 30s
concurrency: 2
rules:
- alert: DiskRunsOutOfSpaceIn3Days
expr: |
vm_free_disk_space_bytes / ignoring(path)
(
rate(vm_rows_added_to_storage_total[1d])
* scalar(
sum(vm_data_size_bytes{type!~"indexdb.*"}) /
sum(vm_rows{type!~"indexdb.*"})
)
) < 3 * 24 * 3600 > 0
for: 30m
labels:
severity: critical
annotations:
dashboard: "http://172.17.40.25:3000/d/wNf0q_kZk?viewPanel=73&var-instance={{ $labels.instance }}"
summary: "Instance {{ $labels.instance }} will run out of disk space soon"
description: "Taking into account current ingestion rate, free disk space will be enough only
for {{ $value | humanizeDuration }} on instance {{ $labels.instance }}.\n
Consider to limit the ingestion rate, decrease retention or scale the disk space if possible."
- alert: DiskRunsOutOfSpace
expr: |
sum(vm_data_size_bytes) by(job, instance) /
(
sum(vm_free_disk_space_bytes) by(job, instance) +
sum(vm_data_size_bytes) by(job, instance)
) > 0.8
for: 30m
labels:
severity: critical
annotations:
dashboard: "http://172.17.40.25:3000/d/wNf0q_kZk?viewPanel=53&var-instance={{ $labels.instance }}"
summary: "Instance {{ $labels.instance }} (job={{ $labels.job }}) will run out of disk space soon"
description: "Disk utilisation on instance {{ $labels.instance }} is more than 80%.\n
Having less than 20% of free disk space could cripple merge processes and overall performance.
Consider to limit the ingestion rate, decrease retention or scale the disk space if possible."
- alert: RequestErrorsToAPI
expr: increase(vm_http_request_errors_total[5m]) > 0
for: 15m
labels:
severity: warning
annotations:
dashboard: "http://172.17.40.25:3000/d/wNf0q_kZk?viewPanel=35&var-instance={{ $labels.instance }}"
summary: "Too many errors served for path {{ $labels.path }} (instance {{ $labels.instance }})"
description: "Requests to path {{ $labels.path }} are receiving errors.
Please verify if clients are sending correct requests."
- alert: RowsRejectedOnIngestion
expr: rate(vm_rows_ignored_total[5m]) > 0
for: 15m
labels:
severity: warning
annotations:
dashboard: "http://172.17.40.25:3000/d/wNf0q_kZk?viewPanel=58&var-instance={{ $labels.instance }}"
summary: "Some rows are rejected on \"{{ $labels.instance }}\" on ingestion attempt"
description: "VM is rejecting to ingest rows on \"{{ $labels.instance }}\" due to the
following reason: \"{{ $labels.reason }}\""
- alert: TooHighChurnRate
expr: |
(
sum(rate(vm_new_timeseries_created_total[5m])) by(instance)
/
sum(rate(vm_rows_inserted_total[5m])) by (instance)
) > 0.1
for: 15m
labels:
severity: warning
annotations:
dashboard: "http://172.17.40.25:3000/d/wNf0q_kZk?viewPanel=66&var-instance={{ $labels.instance }}"
summary: "Churn rate is more than 10% on \"{{ $labels.instance }}\" for the last 15m"
description: "VM constantly creates new time series on \"{{ $labels.instance }}\".\n
This effect is known as Churn Rate.\n
High Churn Rate tightly connected with database performance and may
result in unexpected OOM's or slow queries."
- alert: TooHighChurnRate24h
expr: |
sum(increase(vm_new_timeseries_created_total[24h])) by(instance)
>
(sum(vm_cache_entries{type="storage/hour_metric_ids"}) by(instance) * 3)
for: 15m
labels:
severity: warning
annotations:
dashboard: "http://172.17.40.25:3000/d/wNf0q_kZk?viewPanel=66&var-instance={{ $labels.instance }}"
summary: "Too high number of new series on \"{{ $labels.instance }}\" created over last 24h"
description: "The number of created new time series over last 24h is 3x times higher than
current number of active series on \"{{ $labels.instance }}\".\n
This effect is known as Churn Rate.\n
High Churn Rate tightly connected with database performance and may
result in unexpected OOM's or slow queries."
- alert: TooHighSlowInsertsRate
expr: |
(
sum(rate(vm_slow_row_inserts_total[5m])) by(instance)
/
sum(rate(vm_rows_inserted_total[5m])) by (instance)
) > 0.05
for: 15m
labels:
severity: warning
annotations:
dashboard: "http://172.17.40.25:3000/d/wNf0q_kZk?viewPanel=68&var-instance={{ $labels.instance }}"
summary: "Percentage of slow inserts is more than 5% on \"{{ $labels.instance }}\" for the last 15m"
description: "High rate of slow inserts on \"{{ $labels.instance }}\" may be a sign of resource exhaustion
for the current load. It is likely more RAM is needed for optimal handling of the current number of active time series.
See also https://github.com/VictoriaMetrics/VictoriaMetrics/issues/3976#issuecomment-1476883183"
- alert: LabelsLimitExceededOnIngestion
expr: increase(vm_metrics_with_dropped_labels_total[5m]) > 0
for: 15m
labels:
severity: warning
annotations:
dashboard: "http://172.17.40.25:3000/d/wNf0q_kZk?viewPanel=74&var-instance={{ $labels.instance }}"
summary: "Metrics ingested in ({{ $labels.instance }}) are exceeding labels limit"
description: "VictoriaMetrics limits the number of labels per each metric with `-maxLabelsPerTimeseries` command-line flag.\n
This prevents ingestion of metrics with too many labels. Please verify that `-maxLabelsPerTimeseries` is configured
correctly or that clients which send these metrics aren't misbehaving."
重新加载 vmalert 即可看到告警规则文件加载成功:
告警通知方也一样加载出来了:
告警规则评估触发告警,但还未达到持续时间,未立马发送告警,如果是已发送的告警,状态会呈现 Firing
:
那肯定很多人问我告警知道怎么弄了后,但是告警规则表达式有点难搞,我应该如何写,其实很多告警规则在开源平台中已经写好了,我们可以直接套用,具体可以看如下所示:
告警规则:(https://samber.github.io/awesome-prometheus-alerts/)
下一篇具体讲讲 Alertmanager 的使用。