Alertmanager是用来处理客户端(例如:promethues)发送过来的报警,负责对这些报警进行去重,分组和路由,以及发送给receiver(例如邮件),它还负责告警的静音和抑制

概念

Grouping 分组

分组将类似性质的警报归类为单个通知.这在大型中断期间尤其有用,当许多系统一旦失败,可能同时发出数百到数千个警报

举个例子:当发生网络分区时,集群中很多服务如果都依赖数据库的话,这时每个服务都会触发一个无法连接数据库的报警,结果就是成百上千个报警发送到了Alertmanager.

作为用户来说,只想要看到受到影响的服务实例的单个页面,因此可以将Alertmanager配置为按照集群以及他们的alertname进行分组来发送单个的通知.

通过配置文件中的路由树来配置告警的分组,时机,以及告警通知的receiver.

Inhibition 抑制

抑制的概念是说,当某些报警已经触发,就抑制掉一些其他的报警

举个例子:当触发一个无法访问集群的报警,Alertmanager可以通过配置,在触发这个报警时,忽略其他与该集群相关的报警,这可以防止发送数百或数千个与实际问题无关的警报,也更容易定位root case

Silences 静音

静音指的是在一段时间内不发送某个规则的告警,一个静音的配置是基于匹配器的,就像路由树一样.接收到的报警都会检查是否匹配了静音配置,如果匹配了,那么就不会发送通知

Client behavior

Alertmanager对于客户端有特殊要求,是对除了promethues以外的

High Availability 高可用

Alertmanager支持通过配置创建高可用的集群,可以使用--cluster- *标志进行配置

Alertmanager配置文件相关概念

接下来是alertmanager的配置文件,先解释一下相关概念

route

就是用来设置报警的分发策略,它是一个树状结构,按照深度优先从左向右的顺序进行匹配。 路由块定义了路由树及其子节点。如果没有设置的话,子节点的可选配置参数从其父节点继承。

每个警报都会在配置的顶级路由中进入路由树,该路由树必须匹配所有警报(即没有任何配置的receiver)。然后遍历子节点。如果continue的值设置为false,它在第一个匹配的子节点之后就停止;如果continue的值为true,警报将继续进行后续子节点的匹配。如果警报不匹配任何节点的任何子节点(没有匹配的子节点,或不存在),该警报基于当前节点的配置处理。

下面是相关源码

// A Route is a node that contains definitions of how to handle alerts.
type Route struct {
	Receiver string            `yaml:"receiver,omitempty" json:"receiver,omitempty"`
	GroupBy  []model.LabelName `yaml:"group_by,omitempty" json:"group_by,omitempty"`

	Match    map[string]string `yaml:"match,omitempty" json:"match,omitempty"`
	MatchRE  map[string]Regexp `yaml:"match_re,omitempty" json:"match_re,omitempty"`
	Continue bool              `yaml:"continue,omitempty" json:"continue,omitempty"`
	Routes   []*Route          `yaml:"routes,omitempty" json:"routes,omitempty"`

	GroupWait      *model.Duration `yaml:"group_wait,omitempty" json:"group_wait,omitempty"`
	GroupInterval  *model.Duration `yaml:"group_interval,omitempty" json:"group_interval,omitempty"`
	RepeatInterval *model.Duration `yaml:"repeat_interval,omitempty" json:"repeat_interval,omitempty"`
}


// Match does a depth-first left-to-right search through the route tree
// and returns the matching routing nodes.
func (r *Route) Match(lset model.LabelSet) []*Route {
	if !r.Matchers.Match(lset) {
		return nil
	}
	var all []*Route
	for _, cr := range r.Routes {
		matches := cr.Match(lset)
		all = append(all, matches...)
		if matches != nil && !cr.Continue {
			break
		}
	}

	// If no child nodes were matches, the current node itself is a match.
	if len(all) == 0 {
		all = append(all, r)
	}
	return all
}

alert

alert是收到promethues发送的报警信息,具有相同Lables的Alert(key和value都相同)才会被认为是同一种。在prometheus rules文件配置的一条规则可能会产生多种报警

相关源码

// Alert is a generic representation of an alert in the Prometheus eco-system.
type Alert struct {
	// Label value pairs for purpose of aggregation, matching, and disposition
	// dispatching. This must minimally include an "alertname" label.
	Labels LabelSet `json:"labels"`

	// Extra key/value information which does not define alert identity.
	Annotations LabelSet `json:"annotations"`

	// The known time range for this alert. Both ends are optional.
	StartsAt     time.Time `json:"startsAt,omitempty"`
	EndsAt       time.Time `json:"endsAt,omitempty"`
	GeneratorURL string    `json:"generatorURL"`
}

receivers

就是警报接收的配置,例如邮件,webhook等 下面是相关源码

每一个发送通知都会经历GossipSettleStage,InhibitStage,SilenceStage这几个阶段,分别用来匹配

  • GossipSettleStage 这个与集群部署相关
  • InhibitStage 抑制规则匹配
  • SilenceStage 静音规则匹配
// BuildPipeline builds a map of receivers to Stages.
func BuildPipeline(
	confs []*config.Receiver,
	tmpl *template.Template,
	wait func() time.Duration,
	muter types.Muter,
	silences *silence.Silences,
	notificationLog NotificationLog,
	marker types.Marker,
	peer *cluster.Peer,
	logger log.Logger,
) RoutingStage {
	rs := RoutingStage{}

	ms := NewGossipSettleStage(peer)
	is := NewInhibitStage(muter)
	ss := NewSilenceStage(silences, marker)

	for _, rc := range confs {
		rs[rc.Name] = MultiStage{ms, is, ss, createStage(rc, tmpl, wait, notificationLog, logger)}
	}
	return rs
}

// createStage creates a pipeline of stages for a receiver.
func createStage(rc *config.Receiver, tmpl *template.Template, wait func() time.Duration, notificationLog NotificationLog, logger log.Logger) Stage {
	var fs FanoutStage
	for _, i := range BuildReceiverIntegrations(rc, tmpl, logger) {
		recv := &nflogpb.Receiver{
			GroupName:   rc.Name,
			Integration: i.name,
			Idx:         uint32(i.idx),
		}
		var s MultiStage
		s = append(s, NewWaitStage(wait))
		s = append(s, NewDedupStage(i, notificationLog, recv))
		s = append(s, NewRetryStage(i, rc.Name))
		s = append(s, NewSetNotifiesStage(notificationLog, recv))

		fs = append(fs, s)
	}
	return fs
}

配置

Prometheus

首先,需要在promethues的配置文件prometheus.yml中,添加Alertmanager的地址端口,以及rule_files

global:
  scrape_interval:     5s
  evaluation_interval: 5s # 匹配报警的时间 默认是1m
  external_labels:
      monitor: 'prometheus-grafana-exporter'

# Alertmanager configuration
alerting:
  alertmanagers:
  - static_configs:
    - targets:
      - alertmanager:9093

rule_files:
  - "prometheus.rule.yml" # 可以使用通配符 *rule.yml

当prometheus配置了alertmanager,那么promethues就会根据一定的条件发送给alertmanager报警信息,然后alertmanager来决定如何处理这些警报,这些条件是在prometheus.rules.yml文件中

groups:
- name: httpd
  rules:
  - alert: httpd_down # alertname 不需要唯一
    expr: probe_success{instance="http://httpd:80",job="httpd"} == 0 # PromQL表达式
    for: 1s # 持续多久 
    labels:
      severity: critical # 标签
    annotations:
      summary: "httpd is down" # 注释

Alertmanager

# global的配置可以作为默认配置
global:
  # 邮件基本配置
  smtp_smarthost: 'localhost:25'
  smtp_from: 'alertmanager@example.org'

# root route配置
route:
  # 根路由不能有任何的match(匹配器),而且需要一个receiver,保证所有的alert都有receiver
  receiver: 'team-X-mails'

  # 这个用来将这些key对应value相同的alert合并成一组
  group_by: ['alertname']

  # 当一个alert创建了一个新的group的时候,会等待这个时间,将相同的group的合并
  group_wait: 30s

  # 已有的组发送之后,下一次发送的间隔
  group_interval: 5m

  # 当上次发送间隔大于这个的时候会再次发送
  repeat_interval: 3h

  # 子路由树,会继承根路由的所有配置
  routes:
    # 正则匹配器
  - match_re:
      service: ^(foo1|foo2|baz)$
    receiver: team-X-mails

    # 又一个子路由
    routes:
    # 匹配label
    - match:
        severity: critical
      receiver: team-X-pager

  - match:
      service: files
    receiver: team-Y-mails

    routes:
    - match:
        severity: critical
      receiver: team-Y-pager

  # 这个匹配所有的数据库相关
  - match:
      service: database

    receiver: team-DB-pager
    group_by: [alertname, cluster, database]

    routes:
    - match:
        owner: team-X
      receiver: team-X-pager

    - match:
        owner: team-Y
      receiver: team-Y-pager


# 抑制配置,允许当某个alert是firing状态时,不发送其他的alert
inhibit_rules:
- source_match: # 当报警级别是 critical时
    severity: 'critical'
  target_match: # 不发怂级别是 warning的
    severity: 'warning'
  # 必须是alertname相同的情况下
  equal: ['alertname']


receivers:
- name: 'team-X-mails'
  email_configs:
  - to: 'team-X+alerts@example.org, team-Y+alerts@example.org'

- name: 'team-X-pager'
  email_configs:
  - to: 'team-X+alerts-critical@example.org'
  pagerduty_configs:
  - routing_key: <team-X-key>

- name: 'team-Y-mails'
  email_configs:
  - to: 'team-Y+alerts@example.org'

- name: 'team-Y-pager'
  pagerduty_configs:
  - routing_key: <team-Y-key>

- name: 'team-DB-pager'
  pagerduty_configs:
  - routing_key: <team-DB-key>

消息格式

[
 {
  "labels": {
     "alertname": "low_connected_users",
     "severity": "warning"
   },
   "annotations": {
      "description": "Instance play-app:9000 under lower load",
      "summary": "play-app:9000 of job playframework-app is under lower load"
    }
 }
]

工作流程

整体流程

architecture

alertmanager内部流程

arch

alert的状态

alert-stage

  • Inactive: 这个状态是什么都没有

  • Pending: promethues发送了一个alert过来,然而,这个alert需要匹配分组,抑制或者静音,一旦所有的验证都通过了,就转到Firing.

  • Firing: 警报发送到Notification Pipeline,它将联系警报的所有接收者.然后客户端告诉我们警报解除,所以转换到状Inactive状态。