Promethues оповещение через Telegram.

Расскажу про alert систему promethues и оповещение через telegram. На заре прометея с оповещениями было прямо сложно. Нужно было что-то костылить и использовать сторонние инструменты. Единственное, что хорошо работало из коробки это email и slack. Но в большинстве случаев все это не требуется, а хочется просто получать сообщения в telegram. С недавнего времени, прометей умеет наконец, работать с телеграм из коробки. Об этом ниже. Итак, делаем оповещение в телеграм черезе нативный драйвер прометея.

Создаем следующий docker-compose.yml

version: "3.9"
services:
  prometheus:
    image: prom/prometheus
    container_name: prometheus
    hostname: prometheus
    restart: always
    volumes:
      - ./prometheus.yml:/etc/prometheus/prometheus.yml
      - ./alert.rules:/etc/prometheus/alert.rules
    environment:
      TZ: "Europe/Moscow"
    ports:
      - 9090:9090

  alertmanager:
    image: prom/alertmanager
    container_name: alertmanager
    hostname: alertmanager
    restart: always
    ports:
      - 9093:9093
    volumes:
      - ./alertmanager.yml:/etc/alertmanager/alertmanager.yml
    command:
      - '--config.file=/etc/alertmanager/alertmanager.yml'

Для работы нам нужно так же создать файл конфигурации самого прометея prometheus.yml, правила срабатывания тригеров, ну или сами триггеры если хотите alert.rules, а так же конфигурацию alertmanager-а, alertmanager.yml.

prometheus.yml

global:
  scrape_interval: 5s
  scrape_timeout: 5s
  evaluation_interval: 5s

alerting:
  alertmanagers:
  - scheme: http
    static_configs:
    - targets:
      - "alertmanager:9093"

rule_files:
  - "alert.rules"

scrape_configs:

  - job_name: alert_exporter
    scrape_interval: 5s
    static_configs:
    - targets:
      - "alert_exporter:9000"

alert.rules

groups:

- name: Тестовый счетчик получил значение 0
  rules:
  - alert: Значение тестового счетчика равно 0.
    expr: randome_value == 0
    for: 5s
    labels:
      severity: page


- name: Тестовый счетчик получил значение больше 10
  rules:
  - alert: Значение тестовго счетчика больше либо равно 10.
    expr: randome_value >= 10
    for: 5s
    labels:
      severity: critical
    annotations:
      summary: Значение тестовго счетчика больше либо равно 10.


- name: Тестовый счетчик получил значение больше 5
  rules:
  - alert: Значение тестовго счетчика больше либо равно 5.
    expr: randome_value >= 5
    for: 5s
    labels:
      severity: critical
    annotations:
      summary: Значение тестовго счетчика больше либо равно 5.


- name: Тестовый счетчик получил значение больше 3
  rules:
  - alert: Значение тестовго счетчика больше либо равно 3.
    expr: randome_value >= 10
    for: 5s
    labels:
      severity: critical
    annotations:
      summary: Значение тестовго счетчика больше либо равно 3.

alertmanager.yml

global:
  resolve_timeout: 10s

route:
  group_by: ['alertname']
  group_wait: 3s
  receiver: 'telegram_bot'

receivers:
- name: 'telegram_bot'
  telegram_configs:
  - bot_token: 'YOUR_TOKEN_HERE'
    api_url: 'https://api.telegram.org'
    chat_id: YOUR_CHAT_ID
    parse_mode: 'HTML'

Что бы потестировать оповещения, я написал небольшой экспортер на python, который генерирует рандомное число, по этому числу написаны правила в файле alert.rules, как видите там только числа, но этого достаточно что бы понять как это работает. Возможно, вы так же как и я, захотите потестировать это. По этому я приведу листинг экспортера:

from prometheus_client import start_http_server, Gauge
import random
import time


def get_random_value():
    random_value_list = ['1', '2', '3', '4', '10', '20', '0']
    return random.choice(random_value_list)


prometheus_metric_value = Gauge('randome_value', 'Get randome value from list')


if __name__ == '__main__':
    start_http_server(9000)
    while True:
        prometheus_metric_value.set(get_random_value())
        time.sleep(120)

И на всякий случай, Dockerfile для этого экспортера, плюс блок для docker-compose.

FROM python:3.9-alpine

WORKDIR .
ADD alert_exporter.py .

RUN pip install prometheus-client

CMD ["python", "/alert_exporter.py"]
alert_exporter:
  build: ./src
  container_name: alert_exporter
  hostname: alert_exporter
  restart: always
  ports:
    - 9000:9000

Теперь вы можете запустить весь стек, если необходимо в экспортере можно заменить время ожидания до следующей генерации числа: time.sleep(120), по умолчанию это 2 минуты, время можно уменьшить что бы срабатывания триггеров было быстрее.

prometheus_no_alerts prometheus_alerts

При этом вывод в телеграм канал, выглядит так:

telegram_alerts

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

Вывод при отсутствии триггеров:

>>> curl http://127.0.0.1:9090/api/v1/alerts

{
  "status": "success",
  "data": {
    "alerts": []
  }
}

Вывод при активных триггерах:

>>> curl http://127.0.0.1:9090/api/v1/alerts | jq

{
  "status": "success",
  "data": {
    "alerts": [
      {
        "labels": {
          "alertname": "Значение тестовго счетчика больше либо равно 5.",
          "instance": "alert_exporter:9000",
          "job": "alert_exporter",
          "severity": "critical"
        },
        "annotations": {
          "summary": "Значение тестовго счетчика больше либо равно 5."
        },
        "state": "firing",
        "activeAt": "2023-02-21T09:28:04.540123176Z",
        "value": "2e+01"
      },
      {
        "labels": {
          "alertname": "Значение тестовго счетчика больше либо равно 3.",
          "instance": "alert_exporter:9000",
          "job": "alert_exporter",
          "severity": "critical"
        },
        "annotations": {
          "summary": "Значение тестовго счетчика больше либо равно 3."
        },
        "state": "firing",
        "activeAt": "2023-02-21T09:28:06.596726863Z",
        "value": "2e+01"
      },
      {
        "labels": {
          "alertname": "Значение тестовго счетчика больше либо равно 10.",
          "instance": "alert_exporter:9000",
          "job": "alert_exporter",
          "severity": "critical"
        },
        "annotations": {
          "summary": "Значение тестовго счетчика больше либо равно 10."
        },
        "state": "firing",
        "activeAt": "2023-02-21T09:28:02.404178541Z",
        "value": "2e+01"
      }
    ]
  }
}

Почему я решил изобретать велосипед? Хотя бы просто потому что я могу сам формировать вывод сообщений в телеграм, шаблонизировать вывод, возможность интеграции с другими каналами связи которые ещё не интегрировали в prometheus. Мой небольшой скрипт для отправки оповещений в телеграм:

import urllib.request
import json
import time
import requests

prometheus_url = 'http://127.0.0.1:9090'
bot_token = "YOUR_TOKEN"
bot_chatID = "YOUR_CHAT_ID"


def tg_bot(bot_message):
    send_text = "https://api.telegram.org/bot" + bot_token + "/sendMessage?chat_id=" + bot_chatID + "&parse_mode=HTML&text=" + bot_message
    response = requests.get(send_text)
    return response.json()


def get_data(api):
    with urllib.request.urlopen(prometheus_url + '/api/v1/' + api) as url:
        data = json.load(url)
    return data


def get_alert_status():
    data = get_data('rules')
    alert_state = 0
    for item in data['data']['groups']:
        for state in item['rules']:
            if state['state'] == 'firing':
                alert_state += 1
    return alert_state


def get_alerts_message():
    data = get_data('alerts')
    msg = ''
    if data['data']['alerts']:
        for items in data['data']['alerts']:
            msg += '\nMessage: ' + items['labels']['alertname'] + '\n\n<i>' + \
                'Host: ' + items['labels']['instance'] + '\n' + \
                'Job: ' + items['labels']['job'] + '\n' + \
                'Status: ' + items['labels']['severity'] + '\n</i>'
    return msg


switch_state = None

while True:
    try:
        if get_alert_status() >= 1 and switch_state != 1:
            tg_bot('<b>Alerts!</b>\n' + get_alerts_message())
            switch_state = 1
        if get_alert_status() == 0 and switch_state != 0:
            tg_bot('<b>No alerts now!</b>')
            switch_state = 0
        time.sleep(5)

    except:
        print('Can`t connect to prometheus... Next try attempt for 30s')
        time.sleep(30)

Вывод в телеграм будет выглядеть так:

telegram_alerts

Как обычно, весь код можно взять в репозиториях:



Home »