Alertmanager 简介
之前我们学习 Prometheus 的时候就了解到 Prometheus 包含一个报警模块,就是我们的 AlertManager,Alertmanager 主要用于接收 Prometheus 发送的告警信息,它支持丰富的告警通知渠道,而且很容易做到告警信息进行去重,降噪,分组等,是一款前卫的告警通知系统。
接下来我们就来学习下 AlertManager 的具体使用方法。更多关于企业级监控平台系列的学习文章,请参阅:构建企业级监控平台,本系列持续更新中。
通过在 Prometheus 中定义告警规则,Prometheus 会周期性的对告警规则进行计算,如果满足告警触发条件就会向 Alertmanager 发送告警信息。
在 Prometheus 中一条告警规则主要由以下几部分组成:
- 告警名称:用户需要为告警规则命名,当然对于命名而言,需要能够直接表达出该告警的主要内容
- 告警规则:告警规则实际上主要由
PromQL
进行定义,其实际意义是当表达式(PromQL)查询结果持续多长时间(During)后触发告警
在 Prometheus 中,还可以通过 Group(告警组)对一组相关的告警进行统一定义。Alertmanager 作为一个独立的组件,负责接收并处理来自 Prometheus Server 的告警信息。Alertmanager 可以对这些告警信息进行进一步的处理,比如当接收到大量重复告警时能够消除重复的告警信息,同时对告警信息进行分组并且路由到正确的通知方,Prometheus 内置了对邮件、Slack 多种通知方式的支持,同时还支持与 Webhook 的集成,以支持更多定制化的场景。例如,目前 Alertmanager 还不支持钉钉,用户完全可以通过 Webhook 与钉钉机器人进行集成,从而通过钉钉接收告警信息。同时 AlertManager 还提供了静默和告警抑制机制来对告警通知行为进行优化。
AlertManager 安装
从官方文档中我们可以看到下载AlertManager
二进制文件后,可以通过下面的命令运行:
$ ./alertmanager --config.file=simple.yml
- name: alertmanager
image: prom/alertmanager:v0.14.0
imagePullPolicy: IfNotPresent
args:
- "--config.file=/etc/alertmanager/alertmanager.yml"
其中-config.file
参数是用来指定对应的配置文件的,由于我们这里同样要运行到 Kubernetes 集群中来,所以我们使用docker
镜像的方式来安装,使用的镜像是:prom/alertmanager:v0.15.3
。
首先,指定配置文件,同样的,我们这里使用一个 ConfigMap 资源对象:(alertmanager-conf.yaml。
代码语言:javascript复制apiVersion: v1
kind: ConfigMap
metadata:
name: alert-config
namespace: kube-ops
data:
config.yml: |-
global:
# 在没有报警的情况下声明为已解决的时间
resolve_timeout: 5m
# 配置邮件发送信息
smtp_smarthost: 'smtp.163.com:25'
smtp_from: 'ych_1024@163.com'
smtp_auth_username: 'ych_1024@163.com'
smtp_auth_password: '<邮箱密码>'
smtp_hello: '163.com'
smtp_require_tls: false
# 所有报警信息进入后的根路由,用来设置报警的分发策略
route:
# 这里的标签列表是接收到报警信息后的重新分组标签,例如,接收到的报警信息里面有许多具有 cluster=A 和 alertname=LatncyHigh 这样的标签的报警信息将会批量被聚合到一个分组里面
group_by: ['alertname', 'cluster']
# 当一个新的报警分组被创建后,需要等待至少group_wait时间来初始化通知,这种方式可以确保您能有足够的时间为同一分组来获取多个警报,然后一起触发这个报警信息。
group_wait: 30s
# 当第一个报警发送后,等待'group_interval'时间来发送新的一组报警信息。
group_interval: 5m
# 如果一个报警信息已经发送成功了,等待'repeat_interval'时间来重新发送他们
repeat_interval: 5m
# 默认的receiver:如果一个报警没有被一个route匹配,则发送给默认的接收器
receiver: default
# 上面所有的属性都由所有子路由继承,并且可以在每个子路由上进行覆盖。
routes:
- receiver: email
group_wait: 10s
match:
team: node
receivers:
- name: 'default'
email_configs:
- to: '517554016@qq.com'
send_resolved: true
- name: 'email'
email_configs:
- to: '517554016@qq.com'
send_resolved: true
分组
分组机制可以将详细的告警信息合并成一个通知,在某些情况下,比如由于系统宕机导致大量的告警被同时触发,在这种情况下分组机制可以将这些被触发的告警合并为一个告警通知,避免一次性接受大量的告警通知,而无法对问题进行快速定位。
这是 AlertManager 的配置文件,我们先直接创建这个 ConfigMap 资源对象:
代码语言:javascript复制$ kubectl create -f alertmanager-conf.yamlconfigmap "alert-config" created
然后配置 AlertManager 的容器,我们可以直接在之前的 Prometheus 的 Pod 中添加这个容器,对应的 YAML 资源声明如下:
代码语言:javascript复制- name: alertmanager
image: prom/alertmanager:v0.15.3
imagePullPolicy: IfNotPresent
args:
- "--config.file=/etc/alertmanager/config.yml"
ports:
- containerPort: 9093
name: http
volumeMounts:
- mountPath: "/etc/alertmanager"
name: alertcfg
resources:
requests:
cpu: 100m
memory: 256Mi
limits:
cpu: 100m
memory: 256Mi
volumes:
- name: alertcfg
configMap:
name: alert-config
这里我们将上面创建的 alert-config 这个 ConfigMap 资源对象以 Volume 的形式挂载到 /etc/alertmanager
目录下去,然后在启动参数中指定了配置文件--config.file=/etc/alertmanager/config.yml
,然后我们可以来更新这个 Prometheus 的 Pod:
$ kubectl apply -f prome-deploy.yamldeployment.extensions "prometheus" configured
当然我们也可以将 AlertManager 的配置文件内容直接放入到之前的 Prometheus 的 ConfigMap 的资源对象中,也可以用一个单独的 Pod 来运行 AlertManager 这个容器,完整的资源清单文件可以参考这里:https://github.com/cnych/kubeapp/tree/master/prometheus。更多关于企业级监控平台系列的学习文章,请参阅:构建企业级监控平台,本系列持续更新中。
AlertManager 的容器启动起来后,我们还需要在 Prometheus 中配置下 AlertManager 的地址,让 Prometheus 能够访问到 AlertManager,在 Prometheus 的 ConfigMap 资源清单中添加如下配置:
代码语言:javascript复制alerting:
alertmanagers:
- static_configs:
- targets: ["localhost:9093"]
更新这个资源对象后,稍等一小会儿,执行 reload 操作:
代码语言:javascript复制$ kubectl delete -f prome-cm.yaml
configmap "prometheus-config" deleted
$ kubectl create -f prome-cm.yaml
configmap "prometheus-config" created
# 隔一会儿后
$ kubectl get svc -n kube-ops
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
prometheus NodePort 10.102.74.90 <none> 9090:30358/TCP 3d
$ curl -X POST "http://10.102.74.90:9090/-/reload"
更新完成后,我们查看 Pod 发现有错误,查看下 alertmanager 容器的日志,发现有如下错误信息:
代码语言:javascript复制$ kubectl get pods -n kube-ops
NAME READY STATUS RESTARTS AGE
prometheus-56d64bf6f7-rpz9j 1/2 CrashLoopBackOff 491 1d
$ kubectl logs -f prometheus-56d64bf6f7-rpz9j alertmanager -n kube-ops
level=info ts=2018-11-28T10:33:51.830071513Z caller=main.go:174 msg="Starting Alertmanager" version="(version=0.15.3, branch=HEAD, revision=d4a7697cc90f8bce62efe7c44b63b542578ec0a1)"
level=info ts=2018-11-28T10:33:51.830362309Z caller=main.go:175 build_context="(go=go1.11.2, user=root@4ecc17c53d26, date=20181109-15:40:48)"
level=error ts=2018-11-28T10:33:51.830464639Z caller=main.go:179 msg="Unable to create data directory" err="mkdir data/: read-only file system"
这个是因为新版本dockerfile
中的默认WORKDIR
发生了变化,变成了/etc/alertmanager
目录,默认情况下存储路径--storage.path
是相对目录data/
,因此,alertmanager 会在我们上面挂载的 ConfigMap 中去创建这个目录,所以会报错,我们可以通过覆盖--storage.path
参数来解决这个问题,在容器启动参数中添加该参数:
- name: alertmanager
image: prom/alertmanager:v0.15.3
imagePullPolicy: IfNotPresent
args:
- "--config.file=/etc/alertmanager/config.yml"
- "--storage.path=/alertmanager/data"
重新更新 Pod,可以发现 Prometheus 已经是 Running 状态了:
代码语言:javascript复制$ kubectl apply -f prome-deploy.yaml
deployment.extensions "prometheus" configured
$ kubectl get pods -n kube-ops
NAME READY STATUS RESTARTS AGE
prometheus-646f457455-gr8x5 2/2 Running 0 3m
$ kubectl logs -f prometheus-646f457455-gr8x5 alertmanager -n kube-ops
level=info ts=2018-11-28T11:03:16.054633463Z caller=main.go:174 msg="Starting Alertmanager" version="(version=0.15.3, branch=HEAD, revision=d4a7697cc90f8bce62efe7c44b63b542578ec0a1)"
level=info ts=2018-11-28T11:03:16.054931931Z caller=main.go:175 build_context="(go=go1.11.2, user=root@4ecc17c53d26, date=20181109-15:40:48)"
level=info ts=2018-11-28T11:03:16.351058702Z caller=cluster.go:155 component=cluster msg="setting advertise address explicitly" addr=10.244.2.217 port=9094
level=info ts=2018-11-28T11:03:16.456683857Z caller=main.go:322 msg="Loading configuration file" file=/etc/alertmanager/config.yml
level=info ts=2018-11-28T11:03:16.548558156Z caller=cluster.go:570 component=cluster msg="Waiting for gossip to settle..." interval=2s
level=info ts=2018-11-28T11:03:16.556768564Z caller=main.go:398 msg=Listening address=:9093
level=info ts=2018-11-28T11:03:18.549158865Z caller=cluster.go:595 component=cluster msg="gossip not settled" polls=0 before=0 now=1 elapsed=2.000272112s
level=info ts=2018-11-28T11:03:26.558221484Z caller=cluster.go:587 component=cluster msg="gossip settled; proceeding" elapsed=10.009335611s
AlertManager 报警规则
现在我们只是把 AlertManager 容器运行起来了,也和 Prometheus 进行了关联,但是现在我们并不知道要做什么报警,因为没有任何地方告诉我们要报警,所以我们还需要配置一些报警规则来告诉我们对哪些数据进行报警。
警报规则允许你基于 Prometheus 表达式语言的表达式来定义报警报条件,并在触发警报时发送通知给外部的接收者。
同样在 Prometheus 的配置文件中添加如下报警规则配置:
代码语言:javascript复制prometheus.yml: |
rule_files:
- /etc/prometheus/rules.yml
alerting:
alertmanagers:
- static_configs:
- targets: ["localhost:9093"]
代码语言:javascript复制[root@master prometheus]# cat prometheus-deploy.yaml
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: prometheus-server
namespace: monitor
labels:
app: prometheus
spec:
replicas: 1
selector:
matchLabels:
app: prometheus
component: server
#matchExpressions:
#- {key: app, operator: In, values: [prometheus]}
#- {key: component, operator: In, values: [server]}
template:
metadata:
labels:
app: prometheus
component: server
annotations:
prometheus.io/scrape: 'false'
spec:
serviceAccountName: monitor
containers:
- name: prometheus
image: prom/prometheus:v2.2.1
imagePullPolicy: IfNotPresent
command:
- prometheus
- --config.file=/etc/prometheus/prometheus.yml
- --storage.tsdb.path=/prometheus
- --storage.tsdb.retention=720h
- --web.enable-lifecycle
ports:
- containerPort: 9090
protocol: TCP
volumeMounts:
- mountPath: /etc/prometheus
name: prometheus-config
- name: localtime
mountPath: /etc/localtime
volumes:
- name: prometheus-config
configMap:
name: prometheus-config
- name: localtime
hostPath:
path: /usr/share/zoneinfo/Asia/Shanghai
其中rule_files
就是用来指定报警规则的,这里我们同样将rules.yml
文件用 ConfigMap 的形式挂载到/etc/prometheus
目录下面即可:
apiVersion: v1
kind: ConfigMap
metadata:
name: prometheus-config
namespace: kube-ops
data:
prometheus.yml: |
...
...
...
...
rules.yml: |
groups:
- name: test-rule
rules:
- alert: NodeMemoryUsage
expr: (node_memory_MemTotal_bytes - (node_memory_MemFree_bytes node_memory_Buffers_bytes node_memory_Cached_bytes)) / node_memory_MemTotal_bytes * 100 > 20
for: 2m
labels:
team: node
annotations:
summary: "{{$labels.instance}}: High Memory usage detected"
description: "{{$labels.instance}}: Memory usage is above 20% (current value is: {{ $value }}"
代码语言:javascript复制[root@master prometheus]# kubectl exec -it prometheus-server-5775f99578-vngfh -n monitor -c prometheus sh
kubectl exec [POD] [COMMAND] is DEPRECATED and will be removed in a future version. Use kubectl exec [POD] -- [COMMAND] instead.
/prometheus $ ls /etc/prometheus/
prometheus.yml rules.yml
上面我们定义了一个名为NodeMemoryUsage
的报警规则,其中:
for
语句会使 Prometheus 服务等待指定的时间, 然后执行查询表达式。labels
语句允许指定额外的标签列表,把它们附加在告警上。annotations
语句指定了另一组标签,它们不被当做告警实例的身份标识,它们经常用于存储一些额外的信息,用于报警信息的展示之类的。- 更多关于企业级监控平台系列的学习文章,请参阅:构建企业级监控平台,本系列持续更新中。
为了方便演示,我们将的表达式判断报警临界值设置为20,重新更新 ConfigMap 资源对象,由于我们在 Prometheus 的 Pod 中已经通过 Volume 的形式将 prometheus-config 这个一个 ConfigMap 对象挂载到了/etc/prometheus
目录下面,所以更新后,该目录下面也会出现rules.yml
文件,所以前面配置的rule_files
路径也是正常的,更新完成后,重新执行reload
操作,这个时候我们去 Prometheus 的 Dashboard 中切换到alerts
路径下面就可以看到有报警配置规则的数据了:
我们可以看到页面中出现了我们刚刚定义的报警规则信息,而且报警信息中还有状态显示。一个报警信息在生命周期内有下面3种状态:
- inactive: 表示当前报警信息既不是firing状态也不是pending状态
- pending: 表示在设置的阈值时间范围内被激活了
- firing: 表示超过设置的阈值时间被激活了
我们这里的状态现在是firing
就表示这个报警已经被激活了,我们这里的报警信息有一个team=node
这样的标签,而最上面我们配置 alertmanager 的时候就有如下的路由配置信息了:
routes:
- receiver: email
group_wait: 10s
match:
team: node
所以我们这里的报警信息会被email
这个接收器来进行报警,我们上面配置的是邮箱,所以正常来说这个时候我们会收到一封如下的报警邮件:
Alertmanager 何时报警
在 Prometheus 与 Alertmanager 中有很多地方都涉及到报警时机的配置,那么我的报警规则具体会在什么时候报警呢?
首先在 Prometheus 的全局配置中有一个 evaluation_interval 属性,该属性表示的是间隔多长时间对报警规则进行评估,比如我们这里配置的 30s,那么每 30s 会去验证下我们的报警规则是否达到了阈值。
代码语言:javascript复制global:
scrape_interval: 15s
scrape_timeout: 10s
evaluation_interval: 1m
此时我们的报警状态处于 inactive,如果评估后达到了阈值,则会变成 pending 状态,这个时候报警还没有发送给 Alertmanager,什么时候触发需要依赖报警规则中的 for 属性配置了,比如我们配置成 1m,那么也就是在后续 1 分钟内的评估如果都达到了阈值,那么就会变成 pending 状态,并将报警发送给 Alertmanager,后续的操作就是 Alertmanager 来处理了。
所以有的场景下我们的监控图表上面已经有部分指标达到了告警的阈值了,但是并不一定会触发告警规则,比如我们上面的规则中,设置的是 1 分钟的 Pending Duration,对于下图这种情况就不会触发告警,因为持续时间太短,没有达到一分钟:
报警具体什么时候发送就要看我们的报警路由规则如何配置的了,核心是下面几个属性:
代码语言:javascript复制group_by: [instance] # 报警分组
group_wait: 30s # 在组内等待所配置的时间,如果同组内,30秒内出现相同报警,在一个组内出现。
group_interval: 5m # 每个分组中最多每5分钟发送一条警报
repeat_interval: 1h # 发送报警间隔,如果指定时间内没有修复,则重新发送报警。
Alertmanager 会将收到的报警规则按照 group_by 进行分组。当一个报警触发之后,如果之前没有分组,那么就会创建一个分组,当创建完之后会等待 group_wait 这么长的时间才会发送,这里不会马上发送就是需要积攒一定数量的报警,防止报警数量过多,形成报警风暴。
当一个报警触发之后,如果这个报警属于某个 group,但是这个 group 因为距离第一个 alert 已经足够 group_wait 时间了,发送过一次了,那么就会等待 group_interval 时间之后再次发送新的报警 group 了。
所以,如果报警已经从 Prometheus 送到 Alertmanager 了,那么最多可能等待 max(group_wait,group_interval)的时间。更多关于企业级监控平台系列的学习文章,请参阅:构建企业级监控平台,本系列持续更新中。
假如一个相同的警报一直 FIRING,Alertmanager 并不会一直发送警报,而会等待一段时间,这个等待时间就是 repeat_interval,显然,不同类型警报的发送频率也是不一样的。
比如现在我们添加两条如下所示的报警规则:
代码语言:javascript复制groups:
- name: test-node-mem
rules: # 具体的报警规则
- alert: NodeMemoryUsage # 报警规则的名称
expr: (node_memory_MemTotal_bytes - (node_memory_MemFree_bytes node_memory_Buffers_bytes node_memory_Cached_bytes)) / node_memory_MemTotal_bytes * 100 > 30
for: 1m
labels:
team: node
annotations:
summary: "{{$labels.instance}}: High Memory usage detected"
description: "{{$labels.instance}}: Memory usage is above 30% (current value is: {{ $value }})"
- name: test-node-cpu
rules:
- alert: NodeCpuUsage
expr: ((1 - sum(increase(node_cpu_seconds_total{mode="idle"}[1m])) by (instance) / sum(increase(node_cpu_seconds_total[1m])) by (instance) ) * 100) > 5
for: 2m
labels:
team: node
annotations:
summary: "{{$labels.instance}}: High CPU usage detected"
description: "{{$labels.instance}}: CPU usage is above 5% (current value is: {{ $value }})"
并在 Alertmanager 中配置路由的时候设置根据 instance 进行分组:
代码语言:javascript复制routes:
- receiver: email
group_wait: 10s
group_by: ["instance"]
match:
team: node
同一个分组的报警会发送一条,如果组内有新的报警信息出现,则会等待 group_interval 时间再次发送,如果没有新的变化,并且报警信息还是 FIRING 状态,则需要等待 repeat_interval 时间后再次发送。
更多关于企业级监控平台系列的学习文章,请参阅:构建企业级监控平台,本系列持续更新中。
参考文章:https://blog.csdn.net/qq_34556414/ article/details/121777449 https://blog.csdn.net/qq_34556414/ article/details/124465241