Расскажу про alert систему promethues и оповещение через telegram. На заре прометея с оповещениями было прямо сложно. Нужно было что-то костылить и использовать сторонние инструменты. Единственное, что хорошо работало из коробки это email и slack. Но в большинстве случаев все это не требуется, а хочется просто получать сообщения в telegram. С недавнего времени, прометей умеет наконец, работать с телеграм из коробки. Об этом ниже. Итак, делаем оповещение в телеграм черезе нативный драйвер прометея.
Создаем следующий docker-compose.yml
docker-compose.ymlversion: "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.ymlglobal: 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.rulesgroups: - 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.ymlglobal: 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 дает прекрасную возможность использовать свое 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)
Вывод в телеграм будет выглядеть так:

Как обычно, весь код можно взять в репозиториях:
https://git.yakunin.dev/yakunin/docker
Так же, напоминаю про новый движок и блог по адресу: https://p.yakunin.dev