# 告警的路由与分组

## 告警的标签

prometheus发给alertmanager的每一条告警信息都是带有标签的，而且alertmanager是通过标签来标识每一条告警信息。比如说，下面是alertmanager接收到的一系列告警信息

```
{alertname="NodeCPU",instance="peng01",job="node-exporter",serverity="high",...}  time, annotation
{alertname="NodeMemory",instance="peng01",job="node-exporter",serverity="high",...}  time, annotation
{alertname="NodeCPU",instance="peng02",job="node-exporter",serverity="high",...}  time, annotation 
{alertname="NodeMemory",instance="peng02",job="node-exporter",serverity="high",...}  time, annotation
...
```

## 路由

假设Prometheus同时监控了两个组件：数据库组件与缓存组件；我们希望：数据库组件的告警能够发送给数据库小组的相关人员，缓存组件的告警能发送给缓存小组的相关人员。

基于告警信息的标签与alertmanager的路由机制，我们可以达到上面的效果。

比如，我们定义如下的路由树：

```
route:
  receiver: admin-receiver
  routes:
  - receiver: database-receiver
    match: 
      component: database    
  - receiver: memcache-receiver
    macth: 
      componnet: memcache
```

那么，当alertmanager收到一条告警信息时，首先会发送给admin-receiver；然后根据标签的匹配规则，如果该告警带有标签`component:database`，那么还会发送给database-receiver。

当然，如果数据库的告警还要继续细分，比如mysql的告警还要发送给mysql-receiver，marriadb的告警发送给marriadb-receiver，那么可以路由树如下：

```
route:
  receiver: admin-receiver
  routes:
  - receiver: database-receiver
    match: 
      component: database
    routes:
    - receiver: mysql-receiver
      match:
        type: mysql
    - receiver: marriadb-receiver
      match:
        type: marriadb   
  - receiver: memcache-receiver
    macth: 
      componnet: memcache
```

## 分组

有时候我们会遇到这样的场景：一台主机挂了后，主机上所有的服务都会挂掉，此时alertmanager会连续接收到很多的告警；如果每条告警都发一个邮件出去给接收者，那么短时间内邮件的发送量会很大。

那么，有没有可能把多条告警信息，合并成一个邮件发送出去呢？

答案是可以的，这就是alertmanager的分组功能。假设我们有如下的配置

```
route:
  receiver: admin-receiver
  group_by: ["instance"]
  group_wait: 30s
  group_interval: 5m
  repeat_interval: 4h
```

### Group By

上面我们设置了`group_by ["instance"]`，那么，当AlertManager接收到如下告警时

```
{alertname="NodeCPU",instance="peng01",job="node-exporter",serverity="high",...}  time, annotation
```

便会创建一个Group：`{instance="peng01"}`，然后把该条告警加入到这个Group中。接着，如果再接收到一条告警`{alertname="NodeMemory",instance="peng01",job="node-exporter",serverity="high",...} time, annotation` ，也会加入到这个Group中 。

如果接收到另一个告警

```
{alertname="NodeCPU",instance="peng02",job="node-exporter",serverity="high",...}  time, annotation
```

那么便会创建Group `{instance="peng02"}`，然后把这条告警放在这个Group中。（**注意：只有当一个新的告警到达，且它不属于任何已经存在的分组时，才会创建新的分组**）

而如果接到了一个告警，它没有`instance=xxx`这个Label，那么就会创建一个Group `{}`，把这个告警加入到这个Group中。

> 未设置Group By

上面我们讨论了设置了Group By后，alertmanager如何对告警进行分组。如果我们没有设置Group By，或设置为`group_by: []`，那么alertmanager便不会对告警进行分组（也有可能是对每一个告警中的标签创建一个分组，比如说收到一个告警只有标签`instance="peng01"`，那就创建一个分组`{instance="peng01"}`，收到一个另一个告警有标签`instance="peng01"`和`alertname="NodeCPU"`，就创建一个分组`{instance="peng01",alertname="NodeCPU"}`）。

**需要验证**

### Group Wait

上面我们提到当设置了Group By后，alertManager会对告警进行分组。当一条告警到达时，如果它不属于任何一个已存在的分组，alertManager会创建一个新的分组，然后将该告警加入到这个分组中。此时，alertManager并不会立即把这个告警发给Receiver，而是会等待`group_wait`的时间，如果在这个时间里有属于这个分组的其它告警到达，那么在`group_wait`时间后，alertManager会把这一组告警一次性发给Receiver。

注意：是创建一个分组后，才会等待`group_wait`的时间，等待其他属于这个分组的告警加入。当一个分组里面的告警都已经被“解决”（Resolved）后，这些告警与分组都会删掉，如果再来一个告警，则会重新创建分组。

关于告警如何被“解决”，请阅读后面的《扩展阅读》

### Group Interval

上面提到一个新的Group创建后，要等待`group_wait`的时间才会发通知给Receiver。当发送了通知以后，它会等待`group_interval`的时间，在这段时间内到达该Group的告警，会在`group_interval`后，会作为一个通知发送给Receiver（这个通知中会包含该分组内所有的告警）。

假设`group_interval`为5分钟，某个分组最后一次发送通知的时间为T，当`[T, T+5m]`的时间内该分组没有告警到达，而`T+6m`时该分组内有一个告警到达，那么这个告警会立即被发送给Receiver，而不会等到`T+10m`才发送。然后把`T+6m`作为最后一次发送通知的时间。

### Repeat Interval

当alertManager为某个分组发送了一个通知后，如果该分组里面的告警依然存在（即没有被“解决”）且在`repeat_interval`的时间内没有接收到新的告警，那么在等待`repeat_interval`时间后，alertManager才会为该分组重新发送一个通知。

比如说，`repeat_interval`为4小时，T时刻alertManager为某个分组发送了最后一个通知，这个通知包含了多个告警。在`[T, T+4h]`的时间里，该分组没有接收到新的告警，那么在`T+4h`时alertmanager才会为该分组重新发送一个通知。
