image
Фото Ricardo Gomez Angel, Unsplash.com


Используем метрики для отправки уведомлений через Slack


В предыдущей статье мы задеплоили оператор Prometheus с помощью helm-чарта и на примере набора сервисов увидели, как можно собирать метрики через prom-client и экспортеры. Как вы помните, цель observability (наблюдаемости) — узнать статус системы, поэтому нужные люди должны получать уведомления, когда значения метрик выходят за установленные пределы. Для этого надо настроить алерты.


Система алертов в Prometheus состоит из двух частей. В самом Prometheus мы создаем правила алертов, которые определяют условие для срабатывания алертов. Когда алерты срабатывают, Prometheus отправляет их в AlertManager, который может их подавлять, объединять или отправлять на разные платформы.


В этой статье мы создадим несколько правил алертов и отправим уведомления на их основе через Slack. Все ресурсы, которые мы используем в этой статье, можно скачать из репозитория.


Правила алертов


Чтобы создать правило алертов с помощью оператора Prometheus, используем кастомный ресурс PrometheusRule. В PrometheusRule нужно указать следующее:


Groups: коллекция алертов, которые оцениваются последовательно.


Rules: имя, условие срабатывания, период ожидания, метки и аннотации с дополнительной информацией.


Условное выражение алерта основано на выражениях Prometheus. Можно использовать Prometheus expression builder, чтобы проверить условие, прежде чем создавать его. В следующем примере у нас есть группа правил database.rules с одним правилом, которое срабатывает, когда метрика mysql_up отсутствует минимум 1 минуту.


apiVersion: monitoring.coreos.com/v1
kind: PrometheusRule
metadata:
  creationTimestamp: null
  namespace: observability
  labels:
    prometheus: example
    role: alert-rules
    release: prometheus
    app: kube-prometheus-stack
  name: prometheus-example-rules
spec:
  groups:
  - name: database.rules
    rules:
    - alert: "MysqlDown"
      expr: (absent(mysql_up)) == 1
      for: 1m
      labels:
        severity: critical
        namespace: observability
      annotations:
        description: MySQL has been down for more than a minute

Создаем ресурс командой kubectl apply -f alertrules.yml и переходим на страницу Alerts в Prometheus.


image
Алерт Prometheus MysqlDown


Чтобы протестировать это правило, уменьшаем количество реплик деплоя MySQL:


kubectl scale deployment/mysql --replicas=0 -n applications

Где-то через минуту сработает алерт:


image
Сработавший алерт Prometheus MysqlDown


Нам не придется создавать все самим — в helm-чарте Kube Prometheus уже есть много полезных алертов для метрик Kubernetes. На основе этих алертов можно создавать собственные.


image


Что происходит, когда в системе возникает серьезный сбой? Приложения отказывают одно за другим, а команда получает вал уведомлений. Чтобы этого избежать, можно использовать AlertManager, который группирует похожие алерты в одно уведомление.


Посмотрим, как это работает, создав простой алерт, который срабатывает, если у деплоя остается меньше двух реплик контейнера. Но сначала настроим Slack.


apiVersion: monitoring.coreos.com/v1
kind: PrometheusRule
metadata:
  creationTimestamp: null
  namespace: observability
  labels:
    prometheus: example
    role: alert-rules
    release: prometheus
    app: kube-prometheus-stack
  name: prometheus-example-rules-services
spec:
  groups:
  - name: services.rules
    rules:
    - alert: "NotEnoughContainerReplicas"
      expr: count by (namespace,container,job) (up{container=~".*-service"}) < 2
      for: 30s
      labels:
        severity: warning
        namespace: observability
      annotations:
        description: "{{ $labels.job }}: {{ $labels.container }} container doesn't have enough replicas for more than 30 seconds."

Подготовка Slack


Давайте подготовимся к тому, чтобы отправлять все алерты в Slack. Для начала создадим канал Slack.


image
Окно создания канала в Slack


Создаем приложение в рабочем пространстве. Включаем Incoming Webhooks (входящие вебхуки) для приложения и добавляем новый вебхук в рабочее пространство. Скопируем URL вебхука — он понадобится позже.


image
Включение входящих вебхуков в Slack


Настройка AlertManager


Чтобы настроить AlertManager, нужно создать кастомный ресурс с именем AlertmanagerConfig. Для этого мы должны настроить хотя бы один receiver (платформу, которая будет принимать сообщения) и маршрут ко всем receiver-ам.


Для маршрута нужно указать несколько параметров группирования:


  • groupBy содержит метки, которые AlertManager использует для объединения алертов в одно уведомление.
  • groupWait указывает время ожидания до отправки первого уведомления.
  • groupInterval указывает время ожидания до отправки обновленного уведомления.
  • repeatInterval указывает время ожидания до повторной отправки последнего уведомления.

У Receiver-а Slack есть несколько параметров (см. здесь).


  • Для конфигурации Slack нужен URL вебхука в качестве секрета. На этот секрет будет ссылаться параметр apiURL.
  • channel — канал Slack, который будет использоваться для приема уведомлений.
  • SendResolved указывает AlertManager, что нужно отправить уведомление, когда условие алерта уже не выполняется.
  • Title и Text позволяют изменить формат сообщения Slack. Оба параметра могут включать ссылку на существующий шаблон.

В следующем коде все алерты с одинаковым именем, сработавшие за 30 секунд, будут объединены в одно уведомление Slack.


apiVersion: monitoring.coreos.com/v1alpha1
kind: AlertmanagerConfig
metadata:
  name: slack-alertmanager
  labels:
    alertmanagerConfig: slack
  namespace: observability
spec:
  route:
    groupBy: ['alertname']
    groupWait: 30s
    groupInterval: 5m
    repeatInterval: 12h
    receiver: 'slack'
  receivers:
  - name: 'slack'
    slackConfigs:
    - sendResolved: true
      apiURL:
        name: slack-secret
        key: url
      channel: '#alerts'
      title: '{{ template "alert_title" . }}'
      text: '{{ template "alert_description" . }}'

Пример AlertmanagerConfig


Создадим конфигурацию командой kubectl apply -f alertmanagerconfig.yml.


Переходим на страницу статуса AlertManager и видим все настроенные маршруты. Маршрут, который мы настроили, изменился — у него появилось другое имя и параметр match. Параметр match указывает метки, которые нужны алерту, чтобы его можно было отправить в receiver. По умолчанию каждый настроенный маршрут будет изменен — в него будет добавлена метка неймспейса в параметре match, даже если мы включили другие метки.


image


Чтобы проверить, что алерт будет направлен в нужный receiver, используем routing tree editor. Скопируем конфигурацию AlertManager со страницы статуса и протестируем метки алертов.


image
Routing Tree Editor


Шаблоны


Prometheus поддерживает определение шаблонов для уведомлений. С помощью шаблонов мы можем стандартизировать текст уведомлений для всех алертов.


Пример шаблона:


{{ define "__title" }}{{ range .Alerts.Firing }}{{ .Labels.alertname }}{{ end }}{{ range .Alerts.Resolved }}{{ .Labels.alertname }}{{ end }}{{ end }}
{{ define "alert_title" }}[{{ .Status | toUpper }}{{ if eq .Status "firing" }}:{{ .Alerts.Firing | len }}{{ end }}] {{ if or (and (eq (len .Alerts.Firing) 1) (eq (len .Alerts.Resolved) 0)) (and (eq (len .Alerts.Firing) 0) (eq (len .Alerts.Resolved) 1)) }}{{ template "__title" . }}{{ end }}{{ end }}
{{ define "alert_description"}}
{{ if or (and (eq (len .Alerts.Firing) 1) (eq (len .Alerts.Resolved) 0)) (and (eq (len .Alerts.Firing) 0) (eq (len .Alerts.Resolved) 1)) }}
{{ range .Alerts.Firing }}{{ .Annotations.description }} *{{ .Labels.severity | toUpper }}* :chart_with_upwards_trend: *<{{ .GeneratorURL }}|Graph>*{{ end }}
{{ range .Alerts.Resolved }}{{ .Annotations.description }} :chart_with_upwards_trend: *<{{ .GeneratorURL }}|Graph>*{{ end }}
{{ else }}
{{ if gt (len .Alerts.Firing) 0 }}*Alerts Firing:*
{{ range .Alerts.Firing }}- {{ .Annotations.description }} | *{{ .Labels.severity | toUpper }}* :chart_with_upwards_trend: *<{{ .GeneratorURL }}|Graph>*
{{ end }}{{ end }}
{{ if gt (len .Alerts.Resolved) 0 }}
*Resolved:*
{{ range .Alerts.Resolved }}- {{ .Annotations.description }} :chart_with_upwards_trend: *<{{ .GeneratorURL }}|Graph>*
{{ end }}{{ end }}
{{ end }}
{{ end }}

Пример шаблона Prometheus


Ключевое слово define обозначает многоразовый фрагмент кода. В коде три многоразовых фрагмента: __title, alert_title и alert_description.


__title — просматривает сработавшие и разрешенные алерты и выводит имя алерта.


alert_title — выводит статус в верхнем регистре в квадратных скобках, а также число сработавших алертов. Также включает содержимое __title, если сработавший или разрешенный алерт всего один.


alert_description — если алерт всего один, выводит описание и уровень серьезности алерта, а еще ссылку на URL графика в Prometheus. Если алертов несколько, выводит их список.


Чтобы включить файл шаблона в Prometheus с оператором, нужно обновить кастомный ресурс AlertManager. Для этого можно передать кастомные значения в helm-чарт. Раз нам нужно изменить только файлы шаблонов, следующего файла будет достаточно.


alertmanager:
  templateFiles:
    template_1.tmpl: |-
        {{ define "__title" }}{{ range .Alerts.Firing }}{{ .Labels.alertname }}{{ end }}{{ range .Alerts.Resolved }}{{ .Labels.alertname }}{{ end }}{{ end }}
        {{ define "alert_title" }}[{{ .Status | toUpper }}{{ if eq .Status "firing" }}:{{ .Alerts.Firing | len }}{{ end }}] {{ if or (and (eq (len .Alerts.Firing) 1) (eq (len .Alerts.Resolved) 0)) (and (eq (len .Alerts.Firing) 0) (eq (len .Alerts.Resolved) 1)) }}{{ template "__title" . }}{{ end }}{{ end }}
        {{ define "alert_description"}}
        {{ if or (and (eq (len .Alerts.Firing) 1) (eq (len .Alerts.Resolved) 0)) (and (eq (len .Alerts.Firing) 0) (eq (len .Alerts.Resolved) 1)) }}
        {{ range .Alerts.Firing }}{{ .Annotations.description }} *{{ .Labels.severity | toUpper }}* :chart_with_upwards_trend: *<{{ .GeneratorURL }}|Graph>*{{ end }}
        {{ range .Alerts.Resolved }}{{ .Annotations.description }} :chart_with_upwards_trend: *<{{ .GeneratorURL }}|Graph>*{{ end }}
        {{ else }}
        {{ if gt (len .Alerts.Firing) 0 }}*Alerts Firing:*
        {{ range .Alerts.Firing }}- {{ .Annotations.description }} | *{{ .Labels.severity | toUpper }}* :chart_with_upwards_trend: *<{{ .GeneratorURL }}|Graph>*
        {{ end }}{{ end }}
        {{ if gt (len .Alerts.Resolved) 0 }}
        *Resolved:*
        {{ range .Alerts.Resolved }}- {{ .Annotations.description }} :chart_with_upwards_trend: *<{{ .GeneratorURL }}|Graph>*
        {{ end }}{{ end }}
        {{ end }}
        {{ end }}

Можно обновить деплоймент helm следующей командой:


helm upgrade --reuse-values prometheus prometheus-community/kube-prometheus-stack -n observability -f values.yml

Тестирование уведомлений


Чтобы получить уведомление Slack, нам нужен алерт. Давайте уменьшим количество реплик MySQL, чтобы получить уведомление по одному алерту:


Уменьшим количество реплик MySQL: kubectl scale deployment/mysql --replicas=0 -n applications


Через пару минут увеличим: kubectl scale deployment/mysql --replicas=1 -n applications


Переходим в канал Slack, чтобы посмотреть уведомление.


image
Уведомления Prometheus в Slack


Наконец, нужно протестировать объединение алертов в одно уведомление.
Уменьшим количество реплик Node.js:


kubectl scale deployment/format-service-depl --replicas=1 -n applications
kubectl scale deployment/hello-service-depl --replicas=1 -n applications
kubectl scale deployment/people-service-depl --replicas=1 -n applications

Через пару минут увеличим:


kubectl scale deployment/format-service-depl --replicas=2 -n applications
kubectl scale deployment/hello-service-depl --replicas=2 -n applications
kubectl scale deployment/people-service-depl --replicas=2 -n applications 

Возвращаемся в Slack и сравниваем результаты.
image
Объединенные алерты Prometheus в Slack


Как видите, алерты объединены в одно уведомление. В этом случае они входят в одну группу, потому что у них одинаковое имя. Конфигурацию группы можно изменить, добавив дополнительные метки.


image
Алерты с одинаковым именем


Заключение


Уведомления — это удобный способ сообщить команде о том, что происходит в системе. Используйте шаблоны, чтобы повысить точность сообщений — это позволит быстрее решать проблемы. Не забывайте объединять алерты, чтобы не устать от уведомлений.

Комментарии (0)