Prometheus-06 altermanager

alertmanager

prometheus是一个按功能划分的平台,指标的收集和存储与警报是分开的。警报管理功能由名为Alertmanager的工具提供,该工具是监控体系中的独立组件。我们需要在Prometheus服务器上定义警报规则,这些规则可以触发事件,然后传播到Alertmanager。接下来,Alertmanager会决定如何处理相应的警报,进而解决去重等问题,还会确定在发送警报时使用的机制:实时消息、电子邮件或通过微信、钉钉等。

在本章中,我们将讨论如何构建良好的警报机制,如何安装和配置Alertmanager,并了解如何使用它来路由通知和管理维护。然后,我们将在Prometheus服务器上定义警报规则,继而触发一些警报。

1.alertmanager工作方式

alertmanager处理从客户端发来的警报,客户端通常时Prometheus服务器。它还可以接收其它工具的警报。Alertmanager对警报进行去重、分组,然后路由到不同的接收器,如电子邮件,短信,微信,钉钉等。

我们将在Prometheus服务器上编写警报规则[2],这些规则将使用我们收集的指标并在指定的阈值或标准上触发警报。我们还将看到如何为警报田间一些上下文。当指标达到阈值或标准时,会生成一个警报并将其推送到Alertmanager。警报在Alertmanager上的HTTP端点上接收。一个或多个Prometheus服务器可以将警报定向到单Alertmanager,或者你可以创建一个高可用的Alertmanager集群。

收到警报后,Alertmanager会处理警报并根据其标签进行路由。一旦路径确定,它们将由Alertmanager发送到外部目的地,如电子邮件、短信或聊天工具。

2.二进制安装

在此处下载alertmanager

# 192.168.1.121虚拟机
tar xfzv alertmanager-0.23.0.linux-amd64.tar.gz
useradd --system alertmanager
install -m 0755 alertmanager-0.23.0.linux-amd64/{alertmanager,amtool} /usr/bin
mkdir /var/lib/alertmanager
install -d -o alertmanager -g alertmanager /var/lib/alertmanager

mv alertmanager-0.23.0.linux-amd64/alertmanager.yml /root/prometheus/alertmanager.yml.bak

vim alertmanager.yml
global:
  resolve_timeout: 5m

route:
  group_by: ['alertname']
  group_wait: 10s
  group_interval: 10s
  repeat_interval: 5m
  receiver: 'web.hook'
receivers:
- name: 'web.hook'
  webhook_configs:
  - url: 'http://127.0.0.0.1:5001'


vim alertmanager.service
[Unit]
Description=Alertmanager handles alerts sent by client applications such as the Prometheus
Documentation=https://prometheus.io/docs/alerting/alertmanager/
After=network.target

[Service]
User=alertmanager
ExecStart=/usr/bin/alertmanager \
    --config.file=/etc/alertmanager/alertmanager.yml \
    --storage.path=/var/lib/alertmanager
ExecReload=/bin/kill -HUP $MAINPID
Restart=on-failure

[Install]
WantedBy=multi-user.target


install -m 0644 ./systemd-service/alertmanager.service  /etc/systemd/system/
install -m 0644 -D alertmanager.yml /etc/alertmanager/alertmanager.yml

systemctl daemon-reload
systemctl enable alertmanager.service
systemctl start alertmanager.service # alertmanager默认监听在9093端口


3.alertmanager集群部署

虚拟机:
192.168.1.121 # Prometheus服务监控主机
192.168.1.125
192.168.1.126

在192.168.1.125,192.168.1.126两台虚拟机上也做如同在192.168.1.121虚拟机部署alertmanager方法一样去配置。。。

# 在192.168.1.121机器上:
修改alertmanager服务配置:
vim /etc/systemd/system/alertmanager.service
在ExecStart后添如下选项:
--cluster.listen-address=192.168.1.121:9001

# 启动192.168.1.121机器上的alertmanager
systemctl daemon-reload
systemctl start alertmanager.service
systemctl status alertmanager.service


# 在192.168.1.125机器上:
修改alertmanager服务配置:
vim /etc/systemd/system/alertmanager.service
在ExecStart后添如下选项:
--cluster.listen-address=192.168.1.125:9001
--cluster.peer=192.168.1.121:9001

# 启动192.168.1.125机器上的alertmanager
systemctl daemon-reload
systemctl start alertmanager.service
systemctl status alertmanager.service

# 在192.168.1.126机器上:
修改alertmanager服务配置:
vim /etc/systemd/system/alertmanager.service
在ExecStart后添如下选项:
--cluster.listen-address=192.168.1.126:9001
--cluster.peer=192.168.1.121:9001

# 启动192.168.1.126机器上的alertmanager
systemctl daemon-reload
systemctl start alertmanager.service
systemctl status alertmanager.service

4.alertmanager的配置

与Prometheus一样,Alertmanager配置也是基于YAML的配置文件。

现在让我们为文件添加一些配置。我们的基本配置将通过电子邮件发送任何收到的警报。然后以此不断的扩展。

1.alertmanager配置内容如下:

global:
  smtp_smarthost: 'localhost:25'
  smtp_from: 'alertmanager@example.com'
  smtp_require_tls: false

templates:
- '/etc/alertmanager/template/*.tmpl'

route:
  receiver: email

receivers:
- name: 'email'
  email_configs:
  - to: 'alerts@example.com'
  

此配置文件包含一个基本设置,用于处理警报并通过电子邮件将其发送到一个地址。让我们一次看一下每个块的具体内容。

  • global包含Alertmanager的全局配置。这些选项为所有其它块设置默认值,并在这些块中作为覆盖生效。在本示例中,我们只是配置一些电子邮件和SMTP设置:发送电子邮件的邮件服务器、发送电子邮件的地址,并且我们禁用自动使用TLS的要求。

  • template块包含保存警报模板的目录列表。由于Alertmanager可以发送到各种目的地,因此你通常需要能够定义警报的外观及其包含的数据。创建目录

    mkdir -p /etc/alertmanager/template
    
  • route块,它会告诉Alertmanager如何处理特定的传入警报。警报根据规则进行匹配然后采取相应的操作。可以把路由想象成有树枝的树,每个警报都从树的根(基本路由或路基)进入。除了基本节点之外,每个路由都有匹配的标准,会采取某些特定的操作。例如,来自特定集群的所有警报可能由特定的子路由处理。

但是我们之定义一个参数receiver。这是我们警报的默认目的地,在本示例中时email电子邮件。接下来定义接收器。

  • 最后一个块receivers指定警报目的地。可以通过电子邮件发送警报,或者发送到微信,钉钉等。

[info]随Alertmanager一起附带的还有一个命令行工具amtool,允许你查询警报、管理silence和使用Alertmanager服务器等。

2.打开浏览器输入:http://192.168.1.121:9093,会弹出如下所示结果:

5.Prometheus和alertmanager集成

1.静态配置

现在需要告诉你prometheus关于Alertmanager的信息。如果使用的默认prometheus.ymk的配置,应该会包含如下信心:

alerting:
  alertmanagers:
    - static_configs:
      - targets:
        - alertmanager: 9093

alerting块包含允许Prometheus识别一个或多个alertmanager的配置。为此,Prometheus使用与查找抓取目标时相同的发现机制,在默认配置中时static_configs。与监控作业一样,它指定目标列表,此处时主机名alertmanager加端口9093的形式。该列表假设你的Prometheus服务器可以解析alertmanager主机名为IP地址,并且Alertmanager在该主机的端口9093上运行。

2.服务发现

由于我们可以使用服务发现机制,因此也可以使用这些机制来识别一个或多个Alertmanager。让我们添加一条DNS SRV记录,让Prometheus发现Alertmanager。

SRV记录案例如下:

_alertmanager._tcp.example.com. 300 IN SRV 10 1 9093
alertmanager1.example.com.

在这里,我们以SRV记录的形式指定一个名为_alertmanager的TCP服务。我们的记录返回主机名alertmanager1.example.com和端口号9093,prometheus将在此处找到正在运行的Alertmanager。

配置如下:

alerting:
  alertmanagers:
  - dns_sd_configs:
    -name: [ '_alertmanager._tcp.example.com' ]

在这里,Prometheus将查询alertmanager.tcp.example.com SRV记录以获得Alertmanager的主机名。此外,我们用其它服务发现机制,让Prometheus识别Alertmanager。

3.本实验配置如下:

alerting:
  alertmanagers:
    - static_configs:
      - targets:
        - prometheus.alertmanager.com: 9093

4.监控Alertmanager

与Prometheus一样,Alertmanager暴露了自身的相关指标。

- job_name: 'alertmanager'
  static_configs:
  - targets: [ 'prometheus.alertmanager.com: 9093' ]

浏览器查询alertmanager服务暴露的指标:http://192.168.1.121:9090

alertmanager指标信息:

5.添加警报规则

警报规则在Prometheus服务器配置中加载的规则文件也使用YAML语句定义。

1.定义警报规则文件

cd rules/
touch mode_alerts.yml

2.不需要单独将此文件添加到prometheus.yml配置文件中的rule_files块,可以使用globbing通配符加载该目录中以rules.yml或alerts.yml结尾的所有文件。

rule_files:
  - "rules/*_rules.yml"
  - "rules/*_alerts.yml"

1.添加警报规则

现在添加一个CPU警报规则。如果5分钟内的节点平均CPU使用率在至少60分钟内超过80%,则会触发警报。

# vim /etc/prometheus/rules/node_alert.yml
groups:
- name: node_alerts
  rules:
  - alert: HighNodeCPU
    expr: instance:node_cpu_avg_rate5m > 80
    for: 60m
    labels: 
      severity: warning
    annotations:
      summary: High Node CPU for 1 hour
      console: You might want to check the Node Dashboard at http://grafana.example.com/dashboard/db/node-dashboard

添加一个服务是否正常启动的的警报规则。

# vim /etc/prometheus/rules/node_alert.yml
groups:
- name: general.rules
  rules:
  # Alert for any instance that is unreachable for >5 minute
  - alert: InstanceDown
    expr: up == 0
    for: 1m
    labels:
      severity: error
    annotations:
      summary: "Instance {{ $labels.instance }} down"
      description: "{{ $labels.instance }} of job {{ $labels.job }} has been down for more than 5 minute."

添加警报规则之后,重启Prometheus服务,查看警报规则:

模拟关闭192.168.1.121上监听在3000端口的grafana服务,查看Prometheus的alerts是否触发:

这即是警报规则的摘要,也是查看每个警报状态的方法。

2.警报触发

Prometheus以一个固定时间间隔来评估所有规则,这个时间由evaluate_interval定义,我们将其设置为15秒。在每个评估周期,Prometheus运行每个警报规则中定义的表达式并更新警报状态。

警报可能有以下三种状态:

  • Inactive:警报未激活
  • Pending:警报已满足测试表达式条件,但仍在等待for子句中指定的持续时间。
  • Firing:警报以满足测试表达式条件,并且Pending的时间已经超过for子句的持续时间

Pending到Firing的转换可以确保警报更有效,且不会来回浮动。没有for子句的警报会自动从Inactive转换为Firing,只需要一个评估周期即可触发。带有for子句的警报将首先转换为Pending,然后转换为Firing,因此至少需要两个评估周期才能触发。

到目前为止,警报的生命周期如下:

  • 节点的CPU不断变化,每隔一段时间由scrape_interval定义的时间被Prometheus抓取一次,对我们来说是15秒。
  • 根据每个evaluation_interval的指标来评估警报规则,对我们来说还是15秒。
  • 当警报表达式为true时(对我们来说是CPU超过80%),会创建一个警报并转换到pending状态,执行for子句。
  • 在下一个评估周期中,如果警报测试表达式仍然为true,则检查for的持续时间。如果超过了持续时间,则警报将转换为Firing,生成通知并将其推送到Alertmanager。
  • 如果警报测试表达式不再为true,则Prometheus会将警报规则的状态从pending更改为Inactive.

3.Alertmanager的警报

如果Prometheus的警报处于Firing的状态,并且已经将通知推送到Alertmanager。可以在Prometheus web界面192.168.1.121:9090/alerts中看到这个警报及其状态。

[info]Alertmanager API在/api/v1/alerts路径接收警报。

Prometheus将为pending和firing状态中的每个警报创建指标,这个指标被称为ALERT,并且会像HighNodeCPU警报实例那样创建。

ALERTS{alertname="HighNodeCPU",alertstate="firing",severity=warning,instance="192.168.121:9100"}

每个alert指标都绑定具有固定值1,并且在警报处于Pending或Firing状态期间存在。在此之后,他将不接受任何更新,并且最终会过期。

通知将被发送到Prometheus配置中定义的alertmanager,在示例中是alertmanager主机和端口9093,通知被推送到如下http端点。

http://192.168.1.121.9093/api/v1/alerts

假设一个HighNodeCPU警报被触发了,我们将能在Alertmanager Web控制台http://192.168.1.121:9093/#/qlerts 上看到该警报。

根据当前警报的标签,可以使用这个界面来进行搜索、查询和分组。

在当前Alertmanager配置中,警报将立即发送到email接收。

如果我们有很多团队,或者不同严重程度的警报,那么上面的内容似乎不太实用。这就是alertmanager路由可以提供帮助的地方。

4.添加新警报和模板

为了有更多的警报可以路由,快速的添加一些其它警报规则到node_alert.yml规则文件中。

groups:
- name: node_alerts
  rules:
  - alert: DiskWillFillIn4Hours
    expr: predict_linear(node_filesystem_free_bytes{mountpoint="/"}[1h], 4*3600) < 0
    for: 5m
    labels:
      severity: critical
    annotations:
      summary: Disk on {{ $labels.instance }} will fill in approximately 4 hours.
  - alert: InstanceDown
    expr: up{job="node"} == 0
    for: 10m
    labels:
      severity: critical
    annotations:
      summary: Host {{ $labels.instance }} for {{ $labels.job }} is down!
        
4.1模板

模板是一种在警报中 使用的时间序列数据的标签和值的方法,可用于注解和标签。模板使用标准的Go模板语法,并暴露一些包含时间序列的标签和值的标量。标签以变量$labels形式表示,指标的值是变量¥value。

要在summary注解中引用instance标签,我们使用{{$labels.instance}}。如果想要引用时间序列的值,那么我们会使用{{$value}}。

4.2prometheus警报

我们应始终牢记:Prometheus服务器也可能出问题。让我们添加一些规则来识别问题并对它们发出告警。我们将在rules目录中创建一个新文件prometheus_alerts.yml以保存它们。因为这符我们的glob规则,它也会被prometheus加载。

cd /etc/prometheus/
touch rules/prometheus_alerts.yml
vim /rules/prometheus_alerts.yml
groups:
- name: prometheus_alerts
  rules:
  - alert: PrometheusConfigReloadFailed
    expr: prometheus_config_last_reload_successful == 0
    for: 10m
    labels:
      serverity: warning
    annotations:
      description: Reloading Prometheus configuration has failed on {{ $labels.instance }}.
  - alert: PrometheusNotConnectedToAlertmanagers
    expr: prometheus_notifications_alertmanagers_discovered < 1
    for: 10m
    labels:
      serverity: warning
    annotations:
      description: Prometheus {{ $labels.instance }} is not connected to any Alertmanagers
  

在这里,我们添加了两个新规则,第一个是PrometheusConfigReloadFailed,他让我们知道Prometheus配置重新加载是否失败。如果上次重新加载失败,则使用指标prometheus_config_last_reload_successful,且指标的值为0。

第二条规则确保Prometheus服务器可以发现Alertmanager。这是用prometheus_notifications_alertmanagers_discovered指标,该指标是服务器找到的Alertmanager计数。如果小于1,则表面prometheus没有发现任何Alertmanager,并且这个警报将会触发。由于没有任何Alertmanager,因此它只会显示在Prometheus控制台的/alerts页面上。

4.3可用性警报

最后的警报可以帮助我们确定主机和服务的能力。第一个警报利用了我们使用Node Exporter 收集的systemd指标。如果我们在节点上控制的服务不再活动,则会生成一个警报。

- alert: NodeServiceDown
  expr: node_systemd_unit_state{state="active"} != 1
  for: 60s
  labels:
    severity:critical
  annotations:
    summary: Service {{ $labels.name }} on {{ $labels.instance }} is not longer active!
    description: Werner Heisenberg says - "OMG Where's my service?"

如果带有active标签的node_systemd_unit_state指标值为0,则会触发此报警,表示服务故障至少60秒。

下一个警报使用up指标,该指标对于监控主机的可用性非常有用,但是它并不完美,因为我们真正监控的是作业对目标的住区是否成功。然而,知道实例是否已停止响应抓取也是很有用的,这可能表明存在更严重的问题。为此,警报会检测up指标的值是否为0,如果是0则表示抓取失败。

up{job="node"} == 0

我们在severity标签中添加了一个心智critical,并添加了一个模板 注解,以帮助指示哪个实例和作业失败。

在许多情况下,知道单个实例宕机实际上并不是非常重要。相反,我们也可以测试多个失败的实例,例如,失败实例的百分比:

avg(up) by (job) <= 0.50

这个表达式计算出up指标的平均值然后按job聚合,并在该值低于50%时触发。如果作业中50%的实例无法完成抓取,则会触发警报。

另一种方法可能是:

sum by job (up) / count(up) <= 0.8

这里,我们根据job对up指标求和,然后将其除以计数,如果结果大于或等于0.8,或者特定作业中20%的实例未启动,则触发警报。

通过确定目标何时消失,我们可以使up警报稍微健壮一些。例如,如果从服务中删除我们的目标,那么它的指标将不再更新。如果所有目标都从服务发现中消失,则不会记录任何指标,因此up警报不会被触发。 Prometheus有一个功能叫absent,可检测是否存在缺失的指标。

- alert: InstanceGone
  expr: absent(up{job="node"})
  for: 10s
  labels:
    severity: critical
  annotations:
    summary: Host {{ $labels.instance }} is not longer reporting!
    description: "werner Heisenberg says, OMG where are my instances?"

在这里,我们的表达式使用absent函数来检测节点作业中的up指标是否消失,如果消失,那么它将触发警报。

6.alertmanager路由

现在哦我们已经选择了一些具有不同属性的警报,需要将它们路由到不同的目的地。我们之前发现路由是一棵树。顶部的默认路由总会被配置,并匹配任何子路由不匹配的内容。

回到Alertmanager配置,让我们在alertmanager.yml文件中添加一些路由配置。

route:
  group_by: ['instance']
  group_wait: 30s
  
  group_interval: 5m
  repeat_interval: 3h
  receiver: email
  routes:
  - match:
      serverity: critical
    receiver: pager
  - match_re
      serverity: ^(warning|critical)$
    receiver: support_team

receivers:
- name: 'email'
  email_configs:
  - to: 'alerts@example.com'
- name: 'support_team'
  email_configs:
  - to: 'support@example.com'
- name: 'pager'
  email_configs:
  - to: 'alert-pager@example.com'

可以看到,我们为默认路由添加了一些新选项。第一个选项group_by控制的是Alertmanager分组警报的方式。默认情况下,所有警报都组合在一起,但如果我们指定了group_by和任何标签,则Alertmanager将按这些标签对警报进行分组。例如,我们已经指定了instance标签,这意味着来自特定实例的所有警报将被分在一起。如果列出多个标签,则会在每个指定的标签值匹配时对警报进行分组,例如:

route:
  group_by: ['service','cluster']

分组还会更改Alertmanager的行为。如果引发了新警报,那么Alertmanager将等待下一个选项group_wait中指定的时间段,以便在触发警报之前查看是否收到该组中的其它警报。你可以将其视为警报缓冲。在我们的例子中,这个等待时间是30秒。

在发出警报后,如果收到来该分组的下一次评估的新警报,那么Alertmanager将等待group_interval选项中指定的时间段(即5分钟),然后再发送新警报。这可以防止警报分组的警报泛滥。

我们还制定了repeat_interval。这个暂停并不适用于我们的警报组,而是适用于单个警报,并且是等待重新发送相同警报的时间段,我们指定为3个小时。

2.路由表

然后我们列出了分支路由。第一条路由使用了我们定义的新接收器pager,这会将此路由上的警报发送到新的电子邮件地址。它使用match选项查找要发送的特定警报。这里有两种匹配方法:标签匹配和正则表达式匹配。match选项执行简单的标签匹配。

match:
  severity: critical

在这里,我们将所有severity标签与critical值匹配,并将它们发送到pager接收器。

由于路由表都是分支,因此,如果需要我们也可以再次分支路由。例如:

routes:
- match:
  severity: critical
  receiver: pager
  routes:
    - match:
        service: application
      receiver: support_team

可以看到我们的新routes块嵌套在已有的route块中。要触发此路由,我们的警报首先需要severity标签为critical,并且service标签是application。如果这两个条件都匹配,那么我们的警报将被路由到接收器support_team。

我们也可以根据需要尽可能地嵌套路由。默认情况下,与该路由匹配的任何警报都由该路由处理。但是,我们可以使用continue选项来覆盖此行为,该选项控制警报是否先遍历路由,然后再返回以遍历路由树。

routes:
- match:
    severity: critical
  receiver: pager
  continue: true

continue选项默认为false,但如果设置为true,则警报将在此路由中触发(如果匹配),并继续执行下一个相邻路由。有时这对于向两个地方发送警报很有用,但更好的解决方法是在接收器中指定多个端点,例如:

这会添加第二个pagerduty_configs块,该块既可以将警报发送到PagerDuty,也可以发送电子邮件。我们可以指定任何可以用的接收器,例如,可以向Slack等聊天服务发送电子邮件和消息。

[info]你是否习惯查看已解决的警报?这些是警报条件解决后生成的警报。通过在接收器配置中将send_resolved选项设置为true,可以使用Alertmanager发送它们。通常不建议发送这些已解决的警报,因为其可能导致"错误警报"的循环,进而导致警报疲劳,所以在启用之前要仔细考虑。

我们的第二个路由使用match_re选项将正则表达式与标签匹配,正则表达式使用severity标签。

- match_re:
  severity: ^(informational|warning)$

它匹配severity标签中的informational或者warning值。

在重新加载成重新启动Alertmanager以加载新路由后,你可以尝试触发警报并查看路由的情况。

7.接收器和通知模板

现在已经有了一些基本规则,让我们添加一个非电子邮件的接收器[1]。我们将添加Slack接收器,它会将消息发送到Slack实例。让我们在alertmanager.yml配置文件中查看新接收器配置。

首先,我们将为pager接收器添加一个Slack配置。

receivers:
- name: 'pager'
  email_configs:
  - to: 'alert-paper@example.com'
  slack_configs:
  - api_url: https://hooks.slack.com/services/ABC123/EXAMPLE
    channel: # monitoring

现在,任何向pager接收器发送警报的路由都将被发送到Slack的#monitoring频道,并通过电子邮件发送到alert-pager@example.com。

Alertmanager发送给Slack的一般警报消息非常简单。你可以在其源代码中看到Alertmanager使用的默认模板,该模板包含电子邮件和其它接收器的默认值,但是我们可以为许多接收器覆盖这些值。

例如,可以在Slack警报中添加文本行。

slack_configs:
- api_url: https://hooks.slack.com/services/ABC123/EXAMPLE
  channel: # monitoring
  text: '{{ .CommonAnnotations.summary }}'

我们还可以使用Go template函数来引用外部模板,从而避免在配置文件中嵌入较长且复杂的字符串。我们在本章前面引用了模板目录,目录位于/etc/alertmanager/templates/。让我们在这个目录中创建一个模板。

touch /etc/alertmanager/template/slack.tmpl

接下来让我们填充这个文件。

{{ define "slace.example.text" }}{{ .CommonAnnotations.summary }}{{ end }}

这里我们使用define函数定义了一个新模板,以end结尾,并取名为slack.example.text,然后再模板内的text中复制内容。我们现在可以在Alertmanager配置中引用该模板。

添加slack接收器。

slack_configs:
- api_url: https://hooks.slack.com/services/ABC123/EXAMPLE
  channel: # monitoring
  text: '{{ template "slack.example.text" .}}' 

我们使用了template选项来指定模板的名称。现在将使用模板通知来填充text字段,这对于使用上下文装饰通知很有用。

8.silence和维护

通常我们需要让警报系统知道我们已经停止服务以进行维护,并且不希望触发警报。或者,当上游出现问题时,我们需要将下游服务和应用程序"静音"。Prometheus称这种服务警报静音为silence。silence可以设定为特定时期,例如一小时,或者是一个时间窗口(如知道今天午夜)。这是silence的到期时间或到期日期。如果需要,我们也可以提前动手让silence过期(如果我们的维护比计划提前完成)。

可以使用以下两种方法来设置silence:

  • 通过Alertmanager Web控制台
  • 通过amtool命令行工具。

1.通过Alertmanager控制silence

第一种方法是使用web界面,点击New Silence按钮;

silence指定开始时间、结束时间或持续时间。通过使用标签匹配警报来识别要静音的警报,就像警报路由一样。你可以使用直接匹配,例如匹配具有特定值的标签的每个警报,或者可以使用正则表达式匹配。你还需要指定silence的创建者和注释,以说明警报被静音的原因。

我们单机Create按钮以创建新silence,也可以使用Preview Alert来查看是否有警报会被静音。一旦创建完成,我们就可以编辑silence或者使用其过期以消除静音。

通过单击Alertmanager顶部菜单中的Silences菜单项,你就可以在Web界面中看到当前定义的silence列表

创建的警报:

2.通过amtool控制silence

使用amtool设置silence代码案例:

$ amtool --alertmanager.url=http://localhost:9093 silence add alertname=InstancesGone service=applicational

这将在Alertmanager的http://localhost:9093上添加一个新的silence,他将警报与两个标签匹配:自动填充包含警报名称的alertname标签,以及我们设置的service标签。

[info]使用amtool创建的silence被设置为一小时后自动过期,可以使用--expires和--expire-on参数来指定更长的时间窗口

这将返回一个silenceID,以在稍后处理silence示例中ID为:

可以使用query子命令来查询当前silence列表。

$ amtool --alertmanager.url=http://localhost:9093 silence query

这将返回silence列表及其配置,你可以通过ID来使特定的silence过期。

$ amtool --altermanager.url=http://localhost:9093 silence expire a0bacbc8-c7ee-4b94-8b16-b0b8b4d95686

这将使Alertmanager上的silence失效。

你可以为某些选项创建一个YAML配置文件,而不必每次都指定--alertmanager.url参数。amtool查找的默认配置文件路径是$HOME/config/amtool/config.yml或/etc/amtool/conifg.yml。我们来看一个示例文件。

alertmanager.url: "http://localhost:9093"
author: sre@example.com
comment_required: true

可以看到我们已经添加了一个Alertmanager。同时还指定了author属性,这是silence的创建者,它的默认值为本地用于名,你可以使用这样的方式来标记author,或者在命令行上使用-a 或--author参数。comment_required参数控制silence是否需要注释来说明它的作用。

[info]在配置文件中指定所有amtool参数,但有些参数可能没有太大意义。

回到silence的创建,在创建silence时,你还可以使用正则表达式作为标签值。

$ amtool silence add --comment "Appl maintenance" alertname=~'Instance.*' service=application1

这里我们使用=~来表示标签匹配是一个正则表达式,并在所有警报上匹配一个以instance。我们还使用了--comment参数来添加有关警报的信息。
还可以控制silence的更多细节,如下所示:

$ amtool silence add --author "James" --duration "2h" alertname=InstancesGone service=application1

这里,我们用--author参数覆盖了silence的创建者,并将持续时间指定为两个小时,而不是默认的一小时。

[info]还允许我们使用Alertmanager并验证其配置文件,以及其它有用的任务。你可以通过使用--help参数查看命令行参数的完整列表,还可以通过amtool silence --help 获得特定子命令的帮助。此外,你可以使用Github上的说明[1]完成命令行自动补全和帮助页生成。

9.可靠性和扩展性

1.可靠性和容错性

Prometheus解决容错问题的方法考虑了操作和技术的复杂性。在多数情况下,监控服务的容错性可以通过使监控服务高可用来解决,通常的实现方式是构建集群。但是,集群解决方案要相对复杂的网络,并且需要解决集群中节点之间的状态管理问题。

Prometheus专注于实时监控,通常会保留一段时间内的数据,并且我们假设配置是由配置管理工具管理的。从可用性的角度来看,单个Prometheus服务器通常被认为是不可靠的。Prometheus架构认为,实现集群所需的投入以及维护集群节点之间数据一致性的成本要高于数据本身的价值。

但是Prometheus并没有忽视解决容错问题的必要性。实际上,Prometheus推荐的容错解决方案是并行运行两个配置相同的Prometheus服务器,并且这两个服务器同时处于活动状态。该配置生成的重复警报可以交由上游Alertmanager使用其分组(及抑制)功能进行处理。一个推荐的方法是尽可能使上游Alertmanager高度容错,而不是关注Prometheus服务器的容错能力。

这种方法可以通过创建一个Alertmanager集群来实现的。所有Prometheus服务器回向所有的Alertmanager发送警报。如上所述,Alertmanager负责去除重复数据并通过集群共享警报状态。

这种方法有明显的缺点。首先,两个Prometheus服务器都回收集指标,以加倍该集合可能产生的工作负载。然后,可能会有人争辩说:抓取生产的负载较低,这并不是问题。其次,如果某个Prometheus服务器出现故障或中断,那么另一台服务器就会存在数据缺失,在查询该服务器上的数据时会发现这一差距。这也是一个相对较小的问题,因为有另一台服务器可以查询,而且我们一般关注即时数据,而不是使用Prometheus数据进行长期趋势分析。

[info]有多种方法可以在PromQL中对上述问题进行修补。例如,当请求来自两个源的同一指标值时,你可以通过max by 获取两个指标的最大值。或者,当单个工作分片可能存在差距的警报发生时,你可以增加for子句以确保有多个值。

2.可扩展性

Prometheus环境扩展通常有两种形式:功能扩展或水平扩展。

(1)功能扩展

功能扩展使用分批那将监控内容分不到不同的Prometheus服务器上。例如,可以通过地理位置或者逻辑域来拆分服务器。

或者可以通过特定功能,将所有基础设施监控发送到一台服务器,而将所有应用程序监控发送到另一台服务器。

从这里开始,如果需要对某些区域或功能进行整体视图查看,那么你可以使用联合功能(federation,稍后会详细介绍)将时间序列提取到集中的Prometheus服务器。Grafana支持从多个Prometheus服务器提取数据来构建图形,这可能对你很有帮助,它允许在可视化级别联合来自多个服务器的数据,前提是收集的时间序列具有一定的一致性。

(2)水平分片

某些时候,通常是在大规模部署中,垂直分片的容量和复杂性将会出现问题,当单个作业包含数千个实例时尤其如此。这种情况下,你可以考虑另一种方案:水平分片。水平分片使用一系列工作节点(worker),每个节点都抓取一部分目标。然后,我们在工作节点上汇总感兴趣的特定时间序列。

例如,若我们正在监控主机指标,则可能会汇总这些指标的子集。然后,主机点(primary)使用Prometheus federation APU来抓取每个工作节点的聚合指标。

我们的主节点不仅可以提取聚合指标,还可以为grafana等工具暴露指标或者作为可视化的默认数据源。如果需要更深入或进一步扩展,则可以添加分层的主节点和工作节点。一个很好的例子是基于区域的主机点和工作节点,可能是故障域或者像AWS可用区这样的逻辑区域,将基于区域的主节点视为全局的工作节点,然后向全局的主节点进行报告。

[info]如果要查询未聚合的指标,则需要到特定的工作节点获取你感兴趣的目标数据。可以使用worker标签来帮助

需要注意的是,这种扩展方式存在风险和限制,最显而易见的是,你需要从工作节点中抓取一部分指标,而不是大量或正在收集的所有指标。这是一个类似金字塔的层级结构,而不是分布式的层级结构。此外,你还需要考虑主节点对工作节点的抓取请求负载。

接下来,你会在你的环境中创建更复杂的Prometheus服务器层级结构,还需要担心主节点与工作节点之间的连接,而不仅仅是工作节点与目标之间的连接。这可能会降低解决方案的可靠性。

最后,数据的一致性和正确性页可能会降低。工作节点正在根据设定的间隔抓取目标,而你的主节点也要抓取工作节点。这会导致到达主节点的结果出现延迟,并可能导致数据倾斜或警报延迟。

这两个问题的后果是,在主节点上集中警报可能不是一个好主意。相反,应该将警报推送到工作节点上,在那里更有可能识别出问题(例如目标缺失),或者减少识别警报条件和触发警报之间的滞后。

2.1)工作节点的配置

让我们创建一些工作节点,看看他们如何抓住目标子集。我们会创建工作节点并将它们编号为0到2,并假设工作节点执行的主要工作是抓取node_exporter。每个工作节点都需要被唯一识别,我们将使用外部标签来执行此操作,外部标签是在每个时间序列或警报离开Prometheus服务器时添加上的,通过Prometheus.yml文件中external_labels配置块提供。

现在让我们为第一个工作节点worker0创建基本配置。

global:
  external_labels:
    worker: 0
rule_files:
  - "rules/node_rules.yml"
scrape_configs:
  - job_name: 'node'
    file_sd_configs:
      - files:
        - targets: /nodes/*.json
        refresh_interval: 5m
    relabel_configs:
    - source_labels: [__address__]
      modulus: 3
      trget_label: __tmp_hash
      action: hashmod
    - source_labels: [__tmp_hash]
      regex: ^0$
      action: keep
  

我们可以看到external_labels块包含一个标签worker,它的值为0。我们将使用worker:1、worker:2的方式来标识后面的节点,并以此类推。我们定义了一个使用基于文件的服务发现的作业,从targets/nodes目录中加载以*.json结尾的任何文件作为目标列表。我们会使用服务发现工具或配置管理工具将所有节点填充到JSON文件中。

然后,我们使用重新标记来创建source_labels的hash模数。在示例中,我们只是创建连接address标签的hash模数。我们设定模数modulus为3,即抓取指标的工作节点数量。如果添加工作节点,则需要更新该值(使用可自动增加模数的配置管理工具的另一个理由)。使用hashmod[2]操作创建hash,然后将结果存储在名为__tmp_hash的目标标签中。

接下来,我们使用keep动作来匹配任何与模数匹配的目标的时间序列。因此,worker0将从模数为0的目标中获取时间序列,worker1从模数为1的目标中获取等,这使得目标在工作节点之间均匀分布。如果需要扩展到更多目标,则可以添加工作节点并更新hash模数。

可以使用规则来聚合我们想要联合的工作节点的时间序列。假设我们想从Node Exporter中收集内存、CPU和磁盘指标以进行来联合。

groups:
- name: node_rules
  rules:
  - record: instance:node_cpu:avg_rate5m
    expr: 100 - avg(irate(node_cpu_seconds_total{job="node", mode="idle"}[5m]))  by (instance) * 100
  

这将创建一系列新的时间序列,然后上游Prometheus主节点会进行抓取。

2.2)主节点配置

现在配置一个Prometheus主节点来抓取工作节点的时间序列,主节点需要一个或多个作业来抓取工作节点,每个工作节点都是作业的目标。

scrape_ocnfigs:
- job_name: 'node_workers'
  file_sd_configs:
    - files:
      - 'targets/workers/*.json'
      refresh_interval: 5m
   honor_labels: true
   metrics_path: /federate
   params:
     'match[]':
       - '{__name__=~"^instance:.*"}'

在主节点上有一个名为noe_workers的作业,它使用基于文件的服务发现工作节点列表。我们的工作节点配置在 workers/targets/workers.json文件中

[{
    "targets": [
        "worker0:9090",
        "worker1:9090",
        "worker2:9090"
    ]
}]

你会注意到我们已经启用了honor_labels参数,他剋控制Prometheus处理标签间冲突的方式。通过将其设置为true,我们可确保上游主节点不会覆盖下游工作节点的标签。

我们覆盖了标准路径以使用/dederate API。dederate API端点允许我们查询远程Prometheus服务器以查找由匹配参数指定的特定时间序列。

metrics_path: /dederate
params:
  'match[]':
    - '{__name__=~"^instance:.*"}'

我们使用params选项指定match[]参数,它采用一个即时向量选择器,匹配我们想要返回的特定时间序列。在示例中时间序列的名称匹配。

 - '{__name__=~"^instance:.*"}'

[info]可以指定多个match[]参数,Prometheus将返回所有条件的并集。

此匹配时一个正则表达式匹配,它返回以instance:开头的所有时间序列。我们用于聚合Node Exporter指标的所有规则都以instance:为前缀,因此CPU、内存和磁盘的时间序列会被主节点选择并抓取。

我们可以在其中一个工作节点上,通过适当的match[]参数,使用curl或者浏览/dederate路径来查看被选择的内容。

查询返回了以instance:开头的所有时间序列。主节点的node_workers作业将在每次运行时抓取这些指标。你可以使用主节点来查询和绘制工作节点抓取的所有目标的聚合指标。

参考altermanger通过邮件告警:Prometheus 监控报警系统 AlertManager 之邮件告警

posted on 2021-11-25 21:27  jueyuanfengsheng  阅读(877)  评论(0编辑  收藏  举报